diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 9436a62..0000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,43 +0,0 @@ -# Contributing - -## Reporting issues - -We would love to help if you are having a problem. Feel free to [open an -issue](https://github.com/jish/pre-commit/issues). We ask that you please -provide as much detail as possible. - -## Adding extra checks - -A set of `pre-commit` checks is included in this repo. If you find a bug or -wish to create an enhancement to one of these checks, please open an issue or a -pull request. - -You can also create your own `pre-commit-plugin` in a separate repo. See the -[pre-commit-plugins organization](https://github.com/pre-commit-plugins) on -GitHub for examples. - -## Adding extra configuration sources - -Currently `pre-commit` supports reading configuration from `git config`, your -shell environment (through `ENV`), or a YAML file. See -`lib/plugins/pre_commit/configuration/providers/` for more information and -details on how to create a new configuration provider if necessary. - -## Development - -Bootstrap a development environment: - - $ ./script/bootstrap - -Run tests: - - $ bundle exec rake test - ---- - -### Closing old issues - -Issues that require user feedback will be marked with the `need info` -label. If there is no feedback in two months we will close the issue. If an -issue is closed in this way, the requester or the core team will reopen the -issue when more information is provided. diff --git a/.gitignore b/.gitignore deleted file mode 100644 index bc0cffd..0000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.bundle -bin/rake -coverage/ -/rdoc/html -Gemfile.lock -*.gem diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0790e8b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -sudo: false -addons: - apt: - packages: - - php5 -bundler_args: "" -before_install: - - npm install -g coffeelint - - rvm default do gem install execjs - - git config --global user.name "Pre Commit" - - git config --global user.email "pre-commit+travis@example.com" - - gem update bundler -cache: - - bundler - - npm -language: ruby -rvm: - - jruby - - 2.1.10 - - 2.2.10 - - 2.3.7 - - 2.4.4 - - 2.5.1 -matrix: - fast_finish: true diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 3776799..0000000 --- a/Gemfile +++ /dev/null @@ -1,14 +0,0 @@ -source "https://rubygems.org" - -gemspec - -group :development do - # checks that do not have to be enabled - gem "execjs" - gem "scss-lint" - - # statistics only on MRI 2.0 - avoid problems on older rubies - gem "redcarpet", :platforms => [:mri_20] - gem "simplecov", :platforms => [:mri_20] - gem "coveralls", :platforms => [:mri_20] -end diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d556033..0000000 --- a/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2010-2016 Shajith Chacko, Josh Lubaway - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 3233ce8..0000000 --- a/Rakefile +++ /dev/null @@ -1,22 +0,0 @@ -require 'rake/testtask' -require 'rdoc/task' - -Rake::TestTask.new do |test| - test.libs << "test" << "lib" - test.pattern = "test/**/*_test.rb" - test.verbose = true -end - -task :ci => [:test] -task :default => [:test] - -namespace :pre_commit do - desc "run the tests" - task :ci => [:test] -end - -RDoc::Task.new do |task| - task.main = 'Readme.md' - task.rdoc_files.include('Readme.md', 'lib/**/*.rb') - task.rdoc_dir = 'rdoc/html' -end diff --git a/Readme.md b/Readme.md deleted file mode 100644 index 59ab3ad..0000000 --- a/Readme.md +++ /dev/null @@ -1,148 +0,0 @@ -A better pre-commit hook for git. - -[![Current version](https://badge.fury.io/rb/pre-commit.svg)](https://rubygems.org/gems/pre-commit) -[![Code Climate](https://img.shields.io/codeclimate/github/jish/pre-commit.svg)](https://codeclimate.com/github/jish/pre-commit) -[![Coverage Status](https://img.shields.io/coveralls/jish/pre-commit/master.svg)](https://coveralls.io/r/jish/pre-commit?branch=master) -[![Build status](https://travis-ci.org/jish/pre-commit.svg?branch=master)](https://travis-ci.org/jish/pre-commit) -[![Dependency Status](https://gemnasium.com/jish/pre-commit.png)](https://gemnasium.com/jish/pre-commit) -[![Documentation](https://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/pre-commit/frames) - -## Installation - -Install the gem - - $ gem install pre-commit - -Use the pre-commit command to generate a stub pre-commit hook - - # In your git repo - $ pre-commit install - -This creates a .git/hooks/pre-commit script which will check your git config and run checks that are enabled. - -### Bundler - -If you want to use Bundler to specify a version of RuboCop, add the following to `Gemfile`: - -```ruby -group :development do - gem "pre-commit", require: false - gem "rubocop", require: false -end -``` - -And run the following to run `pre-commit` via Bundler: - -``` -$ git config pre-commit.ruby "bundle exec ruby" -``` - -### RVM - -If you are using rvm you need to install pre-commit into the ```default``` gemset, because it does not use the ```current``` environment - - $ rvm default do gem install pre-commit - -Alternatively you can configure pre-commit to use the ```current``` rvm gemset - - $ git config pre-commit.ruby "rvm `rvm current` do ruby" # OR: - $ git config pre-commit.ruby `rvm wrapper current show ruby` # available in RVM 1.26.12 - -## Available checks - -These are the available checks: - -* white_space -* console_log -* debugger -* pry -* tabs -* jshint -* js_lint -* php (Runs php -l on all staged files) -* rspec_focus (Will check if you are about to check in a :focus in a spec file) -* ruby_symbol_hashrockets (1.9 syntax. BAD :foo => "bar". GOOD foo: "bar") -* local (executes `config/pre-commit.rb` with list of changed files) -* merge_conflict (Will check if you are about to check in a merge conflict) -* migrations (Will make sure you check in the proper files after creating a Rails migration) -* ci (Will run the `pre_commit:ci` rake task and pass or fail accordingly) -* rubocop (Check ruby code style using the rubocop gem. Rubocop must be installed) -* before_all (Check your RSpec tests for the use of `before(:all)`) -* coffeelint (Check your coffeescript files using the [coffeelint gem.](https://github.com/clutchski/coffeelint)) -* gobuild (Runs go build and fails if can't compile) -* gofmt (Runs go fmt on go source files and fail if formatting is incorrect) -* scss_lint (Check your SCSS files using the [scss-lint gem](https://github.com/brigade/scss-lint)) -* yaml (Check that your YAML is parsable) -* json (Checks if JSON is parsable) - -## Default checks - -Use `pre-commit list` to see the list of default and enabled checks and warnings. - -## Enabling / Disabling Checks / Warnings - -### Git configuration - - git config pre-commit.checks "[whitespace, jshint, debugger]" - -To disable, simply leave one off the list - - git config pre-commit.checks "[whitespace, jshint]" - -### CLI configuration - -```ssh -pre-commit check1 [check2...] -``` - -The `git` provider can be used for local machine configuration, the `yaml` can be used for shared -project configuration. - -Example move `jshint` from `checks` to `warnings` in `yaml` provider and save configuration to git: -```bash -pre-commit disable yaml checks jshint -pre-commit enable yaml warnings jshint -git add config/pre_commit.yml -git commit -m "pre-commit: move jshint from checks to warnings" -``` - -Example `config/pre_commit.yml`: -```yaml ---- -:warnings_remove: [] -:warnings_add: -- :jshint -- :tabs -``` - -## Running test manually - -This functionality was added in version `0.17.0` - -```bash -pre-commit run # run on the files added to index not yet commited -pre-commit run all # run on all files in current directory -pre-commit run git # run on all git-tracked files, respect gitignore (added in 0.19.0) -pre-commit run # run on the list of files, patterns not supported -``` - -## Configuration providers - -`pre-commit` comes with 4 configuration providers: - -- `default` - basic settings, read only -- `git` - reads configuration from `git config pre-commit.*`, allow local update -- `yaml` - reads configuration from `/etc/pre_commit.yml`, `$HOME/.pre_commit.yml` and `config/pre_commit.yml`, allows `config/pre_commit.yml` updates -- `env` - reads configuration from environment variables - -## Excluding files from checks - -`pre-commit` uses `git` to get list of files to check, you can ignore -the list of git files to check with: - -1. `.gitignore` - git supported file shared beteen all checkouts -2. `.git/info/exclude` - git supported file only for this checkout -3. `.pre_commit.ignore` - `pre-commit` specific list can be shared, - [Allowed filters](http://ruby-doc.org/core-2.1.3/File.html#method-c-fnmatch) - -## [Contributing](.github/CONTRIBUTING.md) diff --git a/bin/pre-commit b/bin/pre-commit deleted file mode 100755 index 3888407..0000000 --- a/bin/pre-commit +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env ruby - -require 'pre-commit/cli' - -# Change directory to the root of the git repository. -repo_root = `git rev-parse --show-toplevel`.strip -abort "No .git directory found." unless File.directory?(repo_root) -Dir.chdir repo_root - -PreCommit::Cli.new(*ARGV).execute or exit 1 diff --git a/changelog.md b/changelog.md deleted file mode 100644 index bce5706..0000000 --- a/changelog.md +++ /dev/null @@ -1,299 +0,0 @@ -## 0.40.0 - -* Add Ruby 3.2 support -- deprecate `File.exists?` -* Missing migrations check only lists versions missing from schema - -## 0.39.0 - -* Handle new Rails migration format - -## 0.38.1 - -* Fix bug when there is no standard output when detecting the grep version - -## 0.38.0 - -* Ignore standard error when checking grep version -* Security: Depend on RuboCop ~> 0.49 - -## 0.37.0 - -* Skip running binary test on known binary files and known source files. - This improves performance by not opening and reading each file before it is - committed. - -## 0.36.0 - -* Add more RSpec focus syntaxes to the RSpec focus check - -## 0.35.0 - -* Fix a bug in the `go_fmt` plugin - -## 0.34.0 - -* Update to latest pluginator version to fix bug with bundler 1.15 - -## 0.33.0 - -* Remove post-install message, it wasn't functioning properly anyway :( -* Upgrade JSHint to 2.9.4 -* Fix Ruby warnings - -## 0.32.0 - -* Go back to original binary file check. It was creating false-positives. - -## 0.31.0 - -* Handle empty file sizes on Windows -* Add a SourceTree pre-commit hook template `pre-commit install --sourcetree` - -## 0.30.0 - -* Fixed a bug where the default pre-commit hook could not be installed on some -systems. - -## 0.29.0 - -* Fixed a bug where version 0.27.0 and 0.28.0 could not be installed on Windows. - -## 0.28.0 - -* The binary check handles unicode source files. - -## 0.27.0 - -* Removed the closure check -* Separated gofmt and gobuild checks -* Run tests against more (newer) versions of Ruby -- 2.2 & 2.3 -* config.ru is an allowed file for the rubocop check -* Added a `pre-commit new` command to generate a new pre-commit plugin -* The pry check now cheks for `binding.remote_pry` and `binding.remote_pry_em` - -## 0.26.0 - -* Add byebug support to the debugger check - -## 0.25.0 - -* Rubocop check was not filtering file extensions properly. It was ignoring the dot `.` (e.g. `.foo`). -* Users should now be able to commit from subdirectories (e.g. `$ cd subdir`, `$ git commit`). - -## 0.24.0 - -* Whitespace check will not run if there are no staged files. -* The `pre-commit` script now runs properly in GitHub for Mac. - -## 0.23.0 - -* RuboCop checks more than just .rb files (`.gemspec`, `Rakefile`, etc.) - -## 0.22.1 - -* `pre-commit run` used to always exit with the same status code. Now it exits with 0 on success, 1 on error - -## 0.22.0 - -* Read all versions from schema files in the migration check. - -## 0.21.0 - -* Ignore `no rvm in path` errors if rvm is not present. - -## 0.20.0 - -* Add the ability to pass command line flags to Rubocop. `rubocop.flags` -* Upgrade JSHint to 2.5.4 -* The `local` check will look for and prefer `config/pre_commit.rb` in addition to `config/pre-commit.rb` -* Allow committing a large number of files (> 7,000) - -## 0.19.0 - -* Add a `pre-commit run git` command to run `pre-commit` checks on all files tracked by git. -* Filenames containing spaces no longer break the Grep check - -## 0.18.0 - -* Improve version number discovery in schema and migration files so that a blank `schema.rb` (with version `0`) does not fail the migration check -* Fix `pre-commit run `. - -## 0.17.0 - -* Fix a bug where `pre-commit list` would bomb when you had older versions of `pre-commit` installed -* Smarter debugger check allows you to leave `debugger` in comments, quotes, or other intended uses -* Add a `pre-commit run` CLI command -* Fix intermittent install failures #154 - -## 0.16.3 - -* Account for the `RuboCop` top level namespace case sensitivity change. - -## 0.16.2 - -* Fix an error that occurred when adding a git submodule - -## 0.16.1 - -* Adds --force-exclusion option to rubocop CLI - -## 0.16.0 - -* Add a JSON check -- checks if JSON is parsable. -* Add a YAML check -- checks if YAML is parsable. -* Add an SCSS-Lint check -- scss-lint is a tool to help keep your SCSS files clean and readable. -* The `console.log` check will run on coffe script files as well. - -## 0.15.0 - -* Add a standard way for checks to find a config file. `check_name.config` - -## 0.14.1 - -* use `get` to read rubocop.config, fix #124 - -## 0.14.0 - -* Add a `Go` check -* Fix for bug: "Could not find template default" - -## 0.13.0 - -* Hashrockets check only runs on Ruby files -* Fix "uninitialized constant" bug when running Rubocop -* Add [CSSLint](http://csslint.net/) support -* New configuration strategies -- backwards compatible with old configuration - -## 0.12.0 - -* Add a `before_all` check for RSpec `before(:all)` blocks. -* Add a `coffeelint` check. -* Do not load `execjs` unless JavaScript checks are enabled -* Load checks with [pluginator](https://github.com/rvm/pluginator) -* Allow configuration of warnings (log to `stderr`, but do not abort the commit) in `git config pre-commit.warnings` -* Use the Apache 2.0 liscense -* The `nb_space` check reads files in utf-8 - -## 0.11.0 - -* Converted the hook template to shell (instead of ruby) keep an eye out for problems, and file an issue if anything comes up https://github.com/jish/pre-commit/issues -* Added a `pre-commit.ruby` git config option. If this option is set, the hook will use that ruby. `git config pre-commit.ruby "ruby"` -* Drop `Ruby 1.8.7` support - -## 0.10.0 - -* Enhancement: Migration check will ensure the proper versions are in the schema file - -## 0.9.2 - -* Does not run the debugger check on `Gemfile`, `Gemfile.lock` - -## 0.9.0 - -* adding spec directory to checked dirs of pry and debugger - -## 0.8.1 - -* Better system ruby suppot on Mac OS. - -## 0.8.0 - -* Added a check for `binding.pry` -* Allowing `mount Application::API => '/api'` syntax in the hashrocket check -* Added a check for `:focus` in rspec tests. - -## 0.7.0 - -* Added a `local` check. Will run `config/pre-commit.rb` and pass or fail accordingly. - -## 0.6.1 - -* Properly require `ruby_symbol_hashrockets`. - -## 0.6.0 - -* Adding a Ruby hashrocket syntax check. If you're into that kind of thing. - -## 0.5.0 - -* Checking for `rbenv` on boot as well as `rvm` - -## 0.4.0 - -* Detecting if the pre-commit gem is no longer installed. This is usually due to a Ruby version upgrade. -* Only running the ConsoleLog check on javascript files. - -## 0.3.1 - -* Fix for Mountain Lion's grep. - -## 0.3.0 - -* Adding the merge conflict check to the list of default checks - -## 0.2.0 - -* Fixing a segmentation fault that was occurring when some people did not have the proper ruby setup in their environment -* Adding the option to overwrite existing pre-commit hooks during installation - -## 0.1.19 - -* Removing arguments from the shebang line as these are not interpreted the same way on all operating systems - -## 0.1.18 - -* Upgrading JSHint -* Playing nicely with execjs - -## 0.1.17 - -* Fixing typos -* Adding a php check - -## 0.1.16 - -* Detecting leading whitespace before leading tabs in the tabs check. - -## 0.1.15 - -* The previous release handled some error reporting when using therubyracer vm. This release fixes errors if you're using ExecJS and *do not* have therubyracer installed. - -## 0.1.14 - -* Better error reporting when JSHint stops scanning a file for errors half way through - -## 0.1.13 - -* Adding a JSHint config file. You can put your options in a .jshintrc file -* Adding a ci check. You can run a quick test suite each time you commit. - -## 0.1.10 - -* Adding a migration sanity check - -## 0.1.9 - -* Allowing commented out console.log to pass (only single line comment support for now =/) - -## 0.1.7 - -* Adding JSHint support -* Making JSHint a default check - -## 0.1.6 - -### Bugs -* Pre commit would fail -- silently :( -- when adding new .js files due to an error in the jslint check. - -## 0.1.3 - -### Bugs -* On the debugger check, only checking the lines that the committer has added. (thanks to staugaard for pointing this out) - -### Enhancements -* Adding a reminder that the pre-commit check can be bypassed using `git commit -n` - -## 0.1.2 - -### Bugs -* The tabs check was detecting leading tabs in binary files. The tabs check no longer checks binary files. (thanks to morten for pointing this out) diff --git a/checks/ci/index.html b/checks/ci/index.html new file mode 100644 index 0000000..ed91699 --- /dev/null +++ b/checks/ci/index.html @@ -0,0 +1,49 @@ + + + + CI Check + + + + + + +

pre-commit

+ +
+

CI Check

+ +

If you turn on the ci check, it will look for and run a rake task called pre_commit:ci before each commit.

+ +

Example output:

+ +
+

$ git commit

+

pre-commit: your test suite has failed

+

for the full output run `pre_commit:ci`

+

 

+

You can bypass this check using `git commit -n`

+
+ +

Troubleshooting

+ +
+

$ git commit

+

pre-commit: skipping ci check. The `pre_commit:ci` task is not defined.

+
+ +

If you see a message like this, you need to define a rake task called pre_commit:ci.

+ +

If you're using test/unit you can point the pre_commit:ci task to run your test suite like this:

+ +
+        namespace :pre_commit do
+          task :ci => [:test]
+        end
+      
+ +

Or you can define your own custom pre_commit:ci task. This could, for example, run a small test suite of core tests. This small test suite should run very quickly. Remember, you're going to be running this test before every commit.

+
+ + + diff --git a/css/master.css b/css/master.css new file mode 100644 index 0000000..1c6d16a --- /dev/null +++ b/css/master.css @@ -0,0 +1,52 @@ + +body { + font-family: Arial; +} + +#container { + border: 1px solid #ddd; + margin: 1em auto 1em; + padding: 0 15px; + width: 500px; + + border-radius: 10px; +} + +h1 { + margin: 20px auto 10px; + width: 500px; +} + +h2 { + color: #d22; +} + +code { + font-family: monaco, monospace; + font-size: 9pt; +} + +.terminal { + background: #222; + color: #ddd; + font-family: monaco, monospace; + font-size: 9pt; + padding: 11px 12px; + + border-radius: 10px; +} + +.terminal p { + margin: 0; +} + +#footer { + font-size: .85em; + margin: 10px auto 20px; + text-align: right; + width: 500px; +} + +#footer p { + margin: 0; +} diff --git a/docs/choose_your_own_template.md b/docs/choose_your_own_template.md deleted file mode 100644 index 6d0f568..0000000 --- a/docs/choose_your_own_template.md +++ /dev/null @@ -1,50 +0,0 @@ - -## Choose your own template - -The most common criticism of this project is the `pre-commit` hook template we use. Everyone has their own environment, and everyone has their own opinions. - -Right now the template's goal is portability. We want the hook to work on as many machines, and in as many environments as possible -- that's why it looks so strange. - ---- - -Everyone does not share this opinion. Some people value speed, others their Ruby version switcher of choice. This document is a proposal to allow users to choose the template that is appropriate for their repository. - -## Options - -### Portable template - -The goal of this template will be to work in as many environments as possible. - - $ pre-commit install --portable - $ pre-commit install --simple - $ pre-commit install --automatic - $ pre-commit install # maybe the default option? - -### Fast (Manual? Advanced?) template - -This template will assume your environment is set up properly, and will value simplicity and speed - -```ruby -require 'pre-commit' - -PreCommit.run -``` - -Installation options: - - $ pre-commit install --advanced - $ pre-commit install --manual - $ pre-commit install --fast - $ pre-commit install # maybe the default option? - -### RVM template - -This template will assume you are using RVM as a Ruby version switcher, and will attempt to setup the RVM environment for you. - - $ pre-commit install --rvm - -### rbenv template - -This template will assume you are using rbenv as a Ruby version switcher, and will attempt to setup the rbenv environment for you. - - $ pre-commit install --rbenv diff --git a/index.html b/index.html new file mode 100644 index 0000000..9b57c52 --- /dev/null +++ b/index.html @@ -0,0 +1,67 @@ + + + + pre-commit + + + + + + +

pre-commit

+ +
+ +

pre-commit checks your code for errors before you commit it.

+ +

pre-commit is configurable.

+ +

Check for merge conflicts

+

Make sure you don't commit a merge conflict

+
+

$ git commit

+

pre-commit: detected a merge conflict

+

lib/foo.rb:13 <<<<<<< HEAD

+

 

+

You can bypass this check using `git commit -n`

+
+ +

Check for console.log

+

Everyone adds console.log statements to help debug javascript. Make sure you don't accidentally commit one.

+
+

$ git commit

+

pre-commit: console.log found

+

public/javascripts/application.js:64 console.log('checking something');

+

 

+

You can bypass this check using `git commit -n`

+
+ +

More checks

+

pre-commit comes with a variety of checks that you can turn on or off.

+
    +
  • debugger: make sure you don't commit a debugger statement
  • +
  • tabs: make sure your code uses leading spaces instead of tabs
  • +
  • whitespace: make sure you don't commit trailing whitespace
  • +
  • jslint: syntax check your javascript before you commit it
  • +
  • ci: run a quick test suite before you commit more…
  • +
+ +

Install

+
+

$ gem install pre-commit

+

$ pre-commit install

+
+ +

Bugs

+

Please report bugs to github's issue tracker.

+ +
+ + + + + diff --git a/lib/plugins/pluginator/extensions/find_check.rb b/lib/plugins/pluginator/extensions/find_check.rb deleted file mode 100644 index 113f953..0000000 --- a/lib/plugins/pluginator/extensions/find_check.rb +++ /dev/null @@ -1,26 +0,0 @@ -require "plugins/pluginator/extensions/conversions" - -module Pluginator::Extensions - # Extension to find class or first plugin that answers the question with true or to print warning - module FindCheck - include Conversions - - def find_check(name) - klass = string2class(name) - @plugins["checks"].detect do |plugin| - class2name(plugin) == klass || - plugin.respond_to?(:aliases) && plugin.public_send(:aliases).include?(name.to_sym) - end || - begin - $stderr.puts "Could not find plugin supporting #{name} / #{klass}, -available plugins: #{available_plugins}" - nil - end - end - - def available_plugins - @plugins["checks"].map{|plugin| class2name(plugin) }.join(", ") - end - - end -end diff --git a/lib/plugins/pre_commit/checks/before_all.rb b/lib/plugins/pre_commit/checks/before_all.rb deleted file mode 100644 index e8f83af..0000000 --- a/lib/plugins/pre_commit/checks/before_all.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class BeforeAll < Grep - - def files_filter(staged_files) - staged_files.grep(/\.rb$/) - end - - def extra_grep - %w{-v //} - end - - def message - "before(:all) found:" - end - - def pattern - "before.*:all" - end - - def self.description - "Find ruby files with 'before :all' pattern" - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/ci.rb b/lib/plugins/pre_commit/checks/ci.rb deleted file mode 100644 index 14c2894..0000000 --- a/lib/plugins/pre_commit/checks/ci.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - ## - # The CI check will run `rake pre_commmit:ci` before the commit and check - # its exit code. If the task runs successfully, the commit will proceed. - # If it fails, the commit will be aborted. - # - class Ci < Plugin - CI_TASK_NAME = 'pre_commit:ci' - - def self.description - "Runs 'rake #{CI_TASK_NAME} --silent'" - end - - def call(_) - return if system("rake", CI_TASK_NAME, "--silent") - - PreCommit::ErrorList.new( - "your test suite has failed, for the full output run `#{CI_TASK_NAME}`" - ) - end - end - end -end diff --git a/lib/plugins/pre_commit/checks/coffeelint.rb b/lib/plugins/pre_commit/checks/coffeelint.rb deleted file mode 100644 index 5948a85..0000000 --- a/lib/plugins/pre_commit/checks/coffeelint.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'pre-commit/checks/shell' - -module PreCommit - module Checks - class Coffeelint < Shell - - def call(staged_files) - staged_files = staged_files.grep(/\.coffee$/) - return if staged_files.empty? - - result = - in_groups(staged_files).map do |files| - args = %w{coffeelint} + config_file_flag + files - execute(args) - end.compact - - result.empty? ? nil : result.join("\n") - end - - def config_file_flag - config_file ? ['-f', config_file] : [] - end - - def alternate_config_file - 'coffeelint.json' - end - - def self.description - "Runs coffeelint to detect errors" - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/common.rb b/lib/plugins/pre_commit/checks/common.rb deleted file mode 100644 index 47cfde2..0000000 --- a/lib/plugins/pre_commit/checks/common.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Common < Plugin - - def self.includes - [:tabs, :nb_space, :whitespace, :merge_conflict, :debugger] - end - - def self.description - "Plugins common for all languages." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/console_log.rb b/lib/plugins/pre_commit/checks/console_log.rb deleted file mode 100644 index eca5937..0000000 --- a/lib/plugins/pre_commit/checks/console_log.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class ConsoleLog < Grep - - def files_filter(staged_files) - staged_files.grep(/\.(js|coffee)$/) - end - - def extra_grep - %w{-v //} - end - - def message - "console.log found:" - end - - def pattern - "console\\.log" - end - - def self.description - "Finds javascript files with 'console.log'." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/csslint.rb b/lib/plugins/pre_commit/checks/csslint.rb deleted file mode 100644 index fbb9f24..0000000 --- a/lib/plugins/pre_commit/checks/csslint.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'pre-commit/checks/js' - -module PreCommit - module Checks - class Csslint < Js - - SOURCE = "https://github.com/stubbornella/csslint/blob/v0.10.0/release/csslint.js" - - def run_check(file) - context = ExecJS.compile(File.read(linter_src)) - context.call("CSSLint.verify", File.read(file))["messages"] - end - - def linter_src - File.expand_path("../../../../pre-commit/support/csslint/csslint.js", __FILE__) - end - - def error_selector - 'message' - end - - def files_filter(staged_files) - staged_files.grep(/\.css$/) - end - - def self.description - "Checks CSS files with CSSLint." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/debugger.rb b/lib/plugins/pre_commit/checks/debugger.rb deleted file mode 100644 index f4048bb..0000000 --- a/lib/plugins/pre_commit/checks/debugger.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class Debugger < Grep - - def files_filter(staged_files) - staged_files.reject { |file| File.basename(file) =~ /^Gemfile/ } - end - - def message - "debugger statement(s) found:" - end - - def pattern - "^[ ]*(debugger|byebug)" - end - - def self.description - "Finds files with 'debugger'." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/gemfile_path.rb b/lib/plugins/pre_commit/checks/gemfile_path.rb deleted file mode 100644 index a866f0b..0000000 --- a/lib/plugins/pre_commit/checks/gemfile_path.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class GemfilePath < Grep - - def files_filter(staged_files) - staged_files.grep(/^Gemfile$/) - end - - def message - "local path found in Gemfile:" - end - - def pattern - "path:|:path\\s*=>" - end - - def extra_grep - %w{-v #} - end - - def self.description - "Checks 'Gemfile' for local paths." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/go.rb b/lib/plugins/pre_commit/checks/go.rb deleted file mode 100644 index 0f710ac..0000000 --- a/lib/plugins/pre_commit/checks/go.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Go < Plugin - - def self.includes - [:gobuild, :gofmt] - end - - - def self.description - "Plugins for Go code" - end - end - end -end diff --git a/lib/plugins/pre_commit/checks/go_build.rb b/lib/plugins/pre_commit/checks/go_build.rb deleted file mode 100644 index 3209118..0000000 --- a/lib/plugins/pre_commit/checks/go_build.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class GoBuild < Plugin - - def call(staged_files) - staged_files = staged_files.grep(/\.go$/) - return if staged_files.empty? - - errors = staged_files.map { |file| run_check(file) }.compact - return if errors.empty? - - errors.join("\n") - end - - def run_check(file) - cmd = "go build -o /dev/null #{file} 2>&1" - %x[ #{cmd} ] - end - - def self.description - "Detects Go compiler errors" - end - end - end -end diff --git a/lib/plugins/pre_commit/checks/go_fmt.rb b/lib/plugins/pre_commit/checks/go_fmt.rb deleted file mode 100644 index 93f78f0..0000000 --- a/lib/plugins/pre_commit/checks/go_fmt.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class GoFmt < Plugin - - def call(staged_files) - staged_files = staged_files.grep(/\.go$/) - return if staged_files.empty? - - errors = staged_files.map { |file| run_check(file) }.compact - return if errors.empty? - - errors.join("\n") - end - - def run_check(file) - cmd = "gofmt -l #{file} 2>&1" - %x[ #{cmd} ] - end - - def self.description - "Detects bad Go formatting" - end - end - end -end diff --git a/lib/plugins/pre_commit/checks/jshint.rb b/lib/plugins/pre_commit/checks/jshint.rb deleted file mode 100644 index e76547d..0000000 --- a/lib/plugins/pre_commit/checks/jshint.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'pre-commit/checks/js' - -module PreCommit - module Checks - class Jshint < Js - - def run_check(file) - context.call("JSHINT._getErrors", File.read(file), js_config, js_config["globals"]) - end - - def linter_src - File.expand_path("../../../../pre-commit/support/jshint/jshint.js", __FILE__) - end - - def alternate_config_file - ".jshintrc" - end - - def self.description - "Checks javascript files with JSHint." - end - - private - - def context - @context ||= ExecJS.compile("global = this;" << File.read(linter_src) << <<-JAVASCRIPT) - ;JSHINT._getErrors = function(source, options, globals) { - JSHINT(source, options, globals); - return JSHINT.errors; - } - JAVASCRIPT - end - - def js_config - @js_config ||= if config_file - ExecJS.exec("return (#{File.read(config_file)});") - else - {} - end - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/jslint.rb b/lib/plugins/pre_commit/checks/jslint.rb deleted file mode 100644 index 3a4b68c..0000000 --- a/lib/plugins/pre_commit/checks/jslint.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'pre-commit/checks/js' - -module PreCommit - module Checks - class Jslint < Js - - def self.aliases - [ :js_lint, :js_lint_all, :js_lint_new ] - end - - def run_check(file) - context = ExecJS.compile(File.read(linter_src)) - if !(context.call('JSLINT', File.read(file))) - context.exec('return JSLINT.errors;') - else - [] - end - end - - def linter_src - File.expand_path("../../../../pre-commit/support/jslint/lint.js", __FILE__) - end - - def self.description - "Checks javascript files with JSLint." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/json.rb b/lib/plugins/pre_commit/checks/json.rb deleted file mode 100644 index cd5c608..0000000 --- a/lib/plugins/pre_commit/checks/json.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'json' -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Json < Plugin - def call(staged_files) - staged_files = staged_files.grep(/\.json$/) - return if staged_files.empty? - - errors = staged_files.map {|file| load_file(file)}.compact - - errors.join("\n") + "\n" unless errors.empty? - end - - def load_file(file) - File.open(file) {|io| JSON.load(io)} - nil - rescue JSON::ParserError => e - "#{e.message} parsing #{file}" - end - - def self.description - 'Runs json to detect errors.' - end - end - end -end diff --git a/lib/plugins/pre_commit/checks/local.rb b/lib/plugins/pre_commit/checks/local.rb deleted file mode 100644 index 6de8316..0000000 --- a/lib/plugins/pre_commit/checks/local.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Local < Plugin - - attr_writer :script - - def call(staged_files) - return unless script - output = `ruby #{script} #{staged_files.join(" ")} 2>&1` - "#{script} failed:\n#{output}" unless $?.success? - end - - def self.description - "Executes a custom script located at config/pre_commit.rb" - end - - def script - @script ||= ["config/pre_commit.rb", "config/pre-commit.rb"].detect do |file| - File.exist?(file) - end - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/merge_conflict.rb b/lib/plugins/pre_commit/checks/merge_conflict.rb deleted file mode 100644 index 652d375..0000000 --- a/lib/plugins/pre_commit/checks/merge_conflict.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class MergeConflict < Grep - - def message - "detected a merge conflict" - end - - def pattern - "<<<<<<<" - end - - def self.description - "Finds files with unresolved merge conflicts." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/migration.rb b/lib/plugins/pre_commit/checks/migration.rb deleted file mode 100644 index 937f7a2..0000000 --- a/lib/plugins/pre_commit/checks/migration.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Migration < Plugin - VERSION_PATTERN = /(\d{4}_?\d{2}_?\d{2}_?\d{6})/ - - VersionedFile = Struct.new(:file, :version) - - def self.aliases - [:migrations] - end - - def call(staged_files) - migration_files = versioned_migration_files(staged_files) - schema_files = versioned_schema_files(staged_files) - - if migration_files.any? && schema_files.none? - "It looks like you're adding a migration, but did not update the schema file" - elsif migration_files.none? && schema_files.any? - "You're trying to change the schema without adding a migration file" - elsif migration_files.any? && schema_files.any? - migration_versions = migration_files.map(&:version) - schema_versions = schema_files.map(&:version) - missing_versions = migration_versions - schema_versions - - if missing_versions.any? - "You did not add the schema versions for "\ - "#{missing_versions.join(', ')} to #{schema_files.map(&:file).join(' or ')}" - end - end - end - - private - - def versioned_migration_files(staged_files) - files = staged_files.grep(/db\/migrate\/.*\.rb/) - - files.each_with_object([]) do |f, result| - if f =~ VERSION_PATTERN - result << VersionedFile.new(f, $1) - end - end - end - - def versioned_schema_files(staged_files) - files = staged_files.select do |f| - File.basename(f) =~ /schema\.rb|structure.*\.sql/ - end - - files.each_with_object([]) do |f, result| - File.read(f).scan(VERSION_PATTERN) do |i| - version = i.first.gsub(/_/, "") - result << VersionedFile.new(f, version) - end - end - end - - def self.description - "Detects rails database migrations and schema incompatibilities." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/nb_space.rb b/lib/plugins/pre_commit/checks/nb_space.rb deleted file mode 100644 index 35ef4e7..0000000 --- a/lib/plugins/pre_commit/checks/nb_space.rb +++ /dev/null @@ -1,31 +0,0 @@ -# encoding: utf-8 -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class NbSpace < Plugin - - def call(staged_files) - nb_space = " " - raise "you messed that up" unless nb_space.bytes.to_a == [194, 160] - - staged_files.reject! { |f| f =~ /^vendor\// || !File.read(f, encoding: 'utf-8').include?(nb_space) } - - bad = staged_files.map do |file| - content = File.read(file).lines.to_a - line_no = content.index { |l| l.include?(nb_space) } - char_no = content[line_no].index(nb_space) - [file, line_no, char_no] - end - - return if bad.empty? - "Detected non-breaking space in #{bad.map { |f,l,c| "#{f}:#{l+1} character:#{c+1}" }.join(" and")}, remove it!" - end - - def self.description - "Detected non-breaking spaces 194, 160." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/pry.rb b/lib/plugins/pre_commit/checks/pry.rb deleted file mode 100644 index 08f4bd3..0000000 --- a/lib/plugins/pre_commit/checks/pry.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class Pry < Grep - - def message - "binding.pry found:" - end - - def pattern - "binding\.(remote_)?pry" - end - - def self.description - "Finds files with 'binding.pry'." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/rails.rb b/lib/plugins/pre_commit/checks/rails.rb deleted file mode 100644 index 89da57c..0000000 --- a/lib/plugins/pre_commit/checks/rails.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Rails < Plugin - - def self.includes - [:ruby, :jshint, :console_log, :migration] - end - - def self.description - "Plugins common for ruby on rails." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/rspec_focus.rb b/lib/plugins/pre_commit/checks/rspec_focus.rb deleted file mode 100644 index 1117fea..0000000 --- a/lib/plugins/pre_commit/checks/rspec_focus.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class RspecFocus < Grep - - def files_filter(staged_files) - staged_files.grep(/_spec\.rb$/) - end - - def message - "focus found in specs:" - end - - def pattern - "(describe|context|it).*(:focus|focus:).*do" - end - - def extra_pattern - "(fdescribe|fcontext|fit).*(\"|').*(\"|').*do" - end - - def self.description - "Finds ruby specs that are focused." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/rubocop.rb b/lib/plugins/pre_commit/checks/rubocop.rb deleted file mode 100644 index 0d93caa..0000000 --- a/lib/plugins/pre_commit/checks/rubocop.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'stringio' -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Rubocop < Plugin - - WHITELIST = %w[ - \.gemspec \.jbuilder \.opal \.podspec \.rake \.rb config\.ru - Berksfile Capfile Cheffile Gemfile Guardfile Podfile - Rakefile Thorfile Vagabondfile Vagrantfile - ] - - def self.aliases - [ :rubocop_all, :rubocop_new ] - end - - def self.excludes - [ :ruby_symbol_hashrocket ] - end - - def call(staged_files) - require 'rubocop' - rescue LoadError => e - $stderr.puts "Could not find rubocop: #{e}" - else - staged_files = filter_staged_files(staged_files) - return if staged_files.empty? - - args = config_file_flag + user_supplied_flags + ["--force-exclusion"] + staged_files - - success, captured = capture { ::RuboCop::CLI.new.run(args) == 0 } - captured unless success - end - - def filter_staged_files(staged_files) - expression = Regexp.new(WHITELIST.map { |i| i + "\\Z" }.join("|")) - staged_files.grep(expression) - end - - def capture - $stdout, stdout = StringIO.new, $stdout - $stderr, stderr = StringIO.new, $stderr - result = yield - [result, $stdout.string + $stderr.string] - ensure - $stdout = stdout - $stderr = stderr - end - - def config_file_flag - config_file ? ['-c', config_file] : [] - end - - def user_supplied_flags - Array(config.get('rubocop.flags')).reject(&:empty?) - end - - def alternate_config_file - '.rubocop.yml' - end - - def self.description - "Runs rubocop to detect errors." - end - end - end -end diff --git a/lib/plugins/pre_commit/checks/ruby.rb b/lib/plugins/pre_commit/checks/ruby.rb deleted file mode 100644 index d3c824c..0000000 --- a/lib/plugins/pre_commit/checks/ruby.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Ruby < Plugin - - def self.includes - [:pry, :local] - end - - def self.description - "Plugins common for ruby." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/ruby_symbol_hashrockets.rb b/lib/plugins/pre_commit/checks/ruby_symbol_hashrockets.rb deleted file mode 100644 index 574e6f6..0000000 --- a/lib/plugins/pre_commit/checks/ruby_symbol_hashrockets.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class RubySymbolHashrockets < Grep - - def files_filter(staged_files) - staged_files.grep(/\.rb$/) - end - - def message - "detected :symbol => value hashrocket:" - end - - def pattern - '[^:](:{1}(?:\$|@|@@|[_A-Za-z])?\w*[=!?]?\s*=>\s*)' - end - - def self.description - "Finds ruby 1.8 '=>' hash definitions." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/scss_lint.rb b/lib/plugins/pre_commit/checks/scss_lint.rb deleted file mode 100644 index 8558145..0000000 --- a/lib/plugins/pre_commit/checks/scss_lint.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'pre-commit/checks/shell' - -module PreCommit - module Checks - class ScssLint < Shell - - def call(staged_files) - staged_files = staged_files.grep(/\.scss$/) - return if staged_files.empty? - - result = - in_groups(staged_files).map do |files| - args = %w{scss-lint} + config_file_flag + files - execute(args) - end.compact - - result.empty? ? nil : result.join("\n") - end - - def config_file_flag - config_file ? ['-c', config_file] : [] - end - - def alternate_config_file - '.scss-lint.yml' - end - - def self.description - "Runs scss lint to detect errors" - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/tabs.rb b/lib/plugins/pre_commit/checks/tabs.rb deleted file mode 100644 index 801e5f3..0000000 --- a/lib/plugins/pre_commit/checks/tabs.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'pre-commit/checks/grep' - -module PreCommit - module Checks - class Tabs < Grep - - def message - "detected tab before initial space:" - end - - def pattern - "^ *\t" - end - - def self.description - "Finds ruby files with tabulation character before text in line." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/whitespace.rb b/lib/plugins/pre_commit/checks/whitespace.rb deleted file mode 100644 index c599ef4..0000000 --- a/lib/plugins/pre_commit/checks/whitespace.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Whitespace < Plugin - - def self.aliases - [:white_space] - end - - def files_filter(staged_files) - if - @list.map(&:name).include?("PreCommit::Checks::Rubocop") - then - staged_files.reject{|name| name =~ /\.rb$/ } - else - staged_files - end - end - - def files_string(staged_files) - files_filter(staged_files).map{|file| "'#{file}'" }.join(" ") - end - - def call(staged_files) - return if staged_files.empty? - - errors = `git diff-index --check --cached HEAD -- #{files_string(staged_files)} 2>&1` - return if $?.success? - - # Initial commit: diff against the empty tree object - if errors =~ /fatal: bad revision 'HEAD'/ - errors = `git diff-index --check --cached 4b825dc642cb6eb9a060e54bf8d69288fbee4904 -- 2>&1` - return if $?.success? - end - - errors - end - - def self.description - "Finds white space." - end - - end - end -end diff --git a/lib/plugins/pre_commit/checks/yaml.rb b/lib/plugins/pre_commit/checks/yaml.rb deleted file mode 100644 index 0dc62b4..0000000 --- a/lib/plugins/pre_commit/checks/yaml.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen-string-literal: true - -require 'yaml' -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - ## - # The Yaml check will read and parse YAML files to ensure they have valid - # syntax. - # - class Yaml < Plugin - def self.description - 'Runs yaml to detect errors.' - end - - def call(staged_files) - staged_files = staged_files.grep(/\.(yml|yaml)$/) - return if staged_files.empty? - - errors = staged_files.map {|file| load_file(file)}.compact - - errors.join("\n") + "\n" unless errors.empty? - end - - private - - def load_file(file) - if YAML.respond_to?(:safe_load) - safe_load_file(file) - else - normal_load_file(file) - end - - rescue Psych::SyntaxError => e - e.message - end - - def safe_load_file(file) - YAML.safe_load(File.read(file), [::Symbol], [], true, file) - - nil - rescue Psych::DisallowedClass - $stdout.puts "Warning: Skipping '#{file}' because it contains serialized ruby objects." - end - - def normal_load_file(file) - YAML.load_file(file) - - nil - rescue ArgumentError - $stdout.puts "Warning: Skipping '#{file}' because it contains serialized ruby objects." - end - end - end -end diff --git a/lib/plugins/pre_commit/configuration/providers/README.md b/lib/plugins/pre_commit/configuration/providers/README.md deleted file mode 100644 index 712d8dc..0000000 --- a/lib/plugins/pre_commit/configuration/providers/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# PreCommit::Configurations::Providers - -## Ordering - -Providers are ordered based on `priority`. - -## Adding new one - -You can add extra providers by creating gem with a new provider in -`lib/plugins/pre_commit/configuration/providers`. diff --git a/lib/plugins/pre_commit/configuration/providers/default.rb b/lib/plugins/pre_commit/configuration/providers/default.rb deleted file mode 100644 index d826740..0000000 --- a/lib/plugins/pre_commit/configuration/providers/default.rb +++ /dev/null @@ -1,34 +0,0 @@ -module PreCommit - class CanNotUpdateDefauls < StandardError - end - - class Configuration - class Providers - class Default - - DEFAULTS = - { - :warnings => [], - :checks => [:common, :rails] - } - - def self.priority - 0 - end - - def initialize(defaults = nil) - @defaults = defaults || DEFAULTS - end - - def [](name) - @defaults[name] - end - - def update(name, value) - raise PreCommit::CanNotUpdateDefauls.new("Can not update default settings") - end - - end - end - end -end diff --git a/lib/plugins/pre_commit/configuration/providers/env.rb b/lib/plugins/pre_commit/configuration/providers/env.rb deleted file mode 100644 index c34391f..0000000 --- a/lib/plugins/pre_commit/configuration/providers/env.rb +++ /dev/null @@ -1,26 +0,0 @@ -module PreCommit - class Configuration - class Providers - - class Env - def self.priority - 30 - end - - def [](name) - ENV[key(name)] - end - - def update(name, value) - ENV[key(name)] = value - end - - private - - def key(name) - name.to_s.upcase.split('.').join('_') - end - end - end - end -end diff --git a/lib/plugins/pre_commit/configuration/providers/git.rb b/lib/plugins/pre_commit/configuration/providers/git.rb deleted file mode 100644 index b2f392c..0000000 --- a/lib/plugins/pre_commit/configuration/providers/git.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'pre-commit/utils/git_conversions' - -module PreCommit - class Configuration - class Providers - - class Git - include PreCommit::Utils::GitConversions - - def self.priority - 10 - end - - def [](name) - git_to_ruby(`git config pre-commit.#{name.to_s.gsub(/_/,".")} 2>/dev/null`) - end - - def update(name, value) - `git config pre-commit.#{name.to_s.gsub(/_/,".")} "#{ruby_to_git(value)}" 2>/dev/null` - end - - end - - end - end -end diff --git a/lib/plugins/pre_commit/configuration/providers/git_old.rb b/lib/plugins/pre_commit/configuration/providers/git_old.rb deleted file mode 100644 index 8396ee4..0000000 --- a/lib/plugins/pre_commit/configuration/providers/git_old.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'plugins/pre_commit/configuration/providers/git' - -module PreCommit - class Configuration - class Providers - - class GitOld < Git - - def self.priority - 11 - end - - def [](name) - value = super(name) - if - name == :checks && value && ! value.kind_of?(Array) - then - value = value.chomp.split(/,\s*/).map(&:to_sym) || [] - update(name, value) unless value.empty? - end - value - end - - end - - end - end -end diff --git a/lib/plugins/pre_commit/configuration/providers/yaml.rb b/lib/plugins/pre_commit/configuration/providers/yaml.rb deleted file mode 100644 index 188e226..0000000 --- a/lib/plugins/pre_commit/configuration/providers/yaml.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'yaml' -require "pre-commit/configuration/top_level" - -module PreCommit - class Configuration - class Providers - - class Yaml - include PreCommit::Configuration::TopLevel - - def self.priority - 20 - end - - def [](name) - config[name] - end - - def update(name, value) - content = read_config(local_file) - content[name] = value - save_config(local_file, content) - end - - private - - def config - @config ||= begin - config = {} - config.merge!(read_config(system_file)) - config.merge!(read_config(global_file)) - config.merge!(read_config(local_file)) - config - end - end - - def read_config(path) - content = YAML.load_file(path) if File.exist?(path) - content || {} - end - - def save_config(path, content) - parent = File.expand_path('..', path) - Dir.mkdir(parent) unless Dir.exist?(parent) - File.open(path, "w") do |file| - file.write(YAML.dump(content)) - end - end - - def system_file - @system_file ||= '/etc/pre_commit.yml' - end - - def global_file - @global_file ||= File.join(ENV['HOME'], '.pre_commit.yml') - end - - def local_file - File.join(top_level, 'config', 'pre_commit.yml') - end - - end - - end - end -end diff --git a/lib/pre-commit.rb b/lib/pre-commit.rb deleted file mode 100755 index 1676675..0000000 --- a/lib/pre-commit.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'pre-commit/runner' - -## -# The pre-commit gem. -# -module PreCommit - - # Can not delete this method with out a deprecation strategy. - # It is refered to in the generated pre-commit hook in versions 0.0-0.1.1 - # - # NOTE: The deprecation strategy *may* be just delete it since, we're still - # pre 1.0. - # - # Actually, on the deprecation note. This method isn't really the problem. - # The problem is the default generated pre-commit hook. It shouldn't have - # logic in it. The we have freedom to change the gem implementation however - # we want, and nobody is forced to update their pre-commit binary. - def self.checks_to_run - warn "WARNING: You are using old hook version, you can update it with: pre-commit install" - runner.list_to_run(:checks) - end - - def self.run - runner.run or exit 1 - end - - def self.runner - @runner ||= PreCommit::Runner.new - end -end diff --git a/lib/pre-commit/checks.rb b/lib/pre-commit/checks.rb deleted file mode 100644 index 6fe0a65..0000000 --- a/lib/pre-commit/checks.rb +++ /dev/null @@ -1 +0,0 @@ -require 'pre-commit' diff --git a/lib/pre-commit/checks/grep.rb b/lib/pre-commit/checks/grep.rb deleted file mode 100644 index 4f08e03..0000000 --- a/lib/pre-commit/checks/grep.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'open3' - -require 'pre-commit/checks/shell' -require 'pre-commit/error_list' -require 'pre-commit/line' - -module PreCommit - module Checks - class Grep < Shell - def initialize(*) - super - - @extra_grep = nil - @message = nil - @pattern = nil - end - - class PaternNotSet < StandardError - def message - "Please define 'pattern' method." - end - end - - # overwrite those: - - def files_filter(staged_files) - staged_files - end - - def extra_grep - @extra_grep or [] - end - - def message - @message or "" - end - - def pattern - @pattern or raise PaternNotSet.new - end - - def extra_pattern - @extra_pattern - end - - # general code: - - def call(staged_files) - staged_files = files_filter(staged_files) - return if staged_files.empty? - - result = - in_groups(staged_files).map do |files| - args = grep + [pattern] + files - args += ["|", "grep"] + extra_grep if !extra_grep.nil? and !extra_grep.empty? - - results = [ - execute(args, success_status: false), - extra_execute(files) - ].compact - - results.empty? ? nil : results.join('') - end.compact - - result.empty? ? nil : parse_errors(message, result) - end - - private - - def parse_errors(message, list) - result = PreCommit::ErrorList.new(message) - result.errors += - list.map do |group| - group.split(/\n/) - end.flatten.compact.map do |line| - PreCommit::Line.new(nil, *parse_error(line)) - end - result - end - - def parse_error(line) - matches = /^([^:]+):([[:digit:]]+):(.*)$/.match(line) - matches and matches.captures - end - - def grep(grep_version = nil) - grep_version ||= detect_grep_version - if grep_version =~ /FreeBSD/ - %w{grep -EnIH} - else - %w{grep -PnIH} - end - end - - def detect_grep_version - Open3.popen3('grep', '--version') do |_, stdout, _| - return '' if stdout.eof? - - first_line = stdout.readline - return first_line.sub(/^[^0-9.]*\([0-9.]*\)$/, '\1') - end - end - - def extra_execute(files) - return nil if extra_pattern.nil? or extra_pattern.empty? - args = grep + [extra_pattern] + files - - execute(args, success_status: false) - end - - end - end -end diff --git a/lib/pre-commit/checks/js.rb b/lib/pre-commit/checks/js.rb deleted file mode 100644 index bb4ba07..0000000 --- a/lib/pre-commit/checks/js.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'pre-commit/checks/plugin' - -module PreCommit - module Checks - class Js < Plugin - def call(staged_files) - require 'execjs' - rescue RuntimeError, LoadError => e - $stderr.puts "Could not load execjs: #{e}. You need to manually install execjs to use JavaScript plugins for security reasons." - else - staged_files = files_filter(staged_files) - return if staged_files.empty? - - errors = [] - staged_files.each do |file| - error_list = Array(run_check(file)) - error_list.each { |error_object| errors << display_error(error_object, file) } - end - - return if errors.empty? - errors.join("\n") - end - - def linter_src - raise "Must be defined by subclass" - end - - def files_filter(staged_files) - staged_files.grep(/\.js$/) - end - - def error_selector - 'reason' - end - - def display_error(error_object, file) - return "" unless error_object - - line = error_object['line'].to_i + 1 - "#{error_object[error_selector]}\n#{file}:#{line} #{error_object['evidence']}" - end - end - end -end diff --git a/lib/pre-commit/checks/plugin.rb b/lib/pre-commit/checks/plugin.rb deleted file mode 100644 index ae9b81a..0000000 --- a/lib/pre-commit/checks/plugin.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'plugins/pluginator/extensions/conversions' -require 'pre-commit/checks/plugin/config_file' -require 'pre-commit/line' - -module PreCommit - module Checks - class Plugin - include Pluginator::Extensions::Conversions - - attr_accessor :pluginator, :config - - def initialize(pluginator, config, list) - @pluginator = pluginator - @config = config - @list = list - end - - def name - class2string(class2name(self.class)) - end - - private - - def config_file - @config_file ||= ConfigFile.new(name, config, alternate_config_file) - @config_file.location - end - - def alternate_config_file - '' - end - - # group files in packs smaller then 127kB (1000 files) - # 127k based on http://www.in-ulm.de/~mascheck/various/argmax/ - # and 262144 limit on OSX - my env size /2 to be safe - # assuming mean file length shorter then 127 chars splitting to - # groups of 1000 files, each_slice for simplicity, doing real - # check could be to time consuming - def in_groups(files, group_size = 1000) - files.each_slice(group_size) - end - - end - end -end diff --git a/lib/pre-commit/checks/plugin/config_file.rb b/lib/pre-commit/checks/plugin/config_file.rb deleted file mode 100644 index f6b9dca..0000000 --- a/lib/pre-commit/checks/plugin/config_file.rb +++ /dev/null @@ -1,68 +0,0 @@ -module PreCommit - module Checks - class Plugin - class ConfigFile - def initialize(name, config, alternate_location) - @name = name - @config = config - @alternate_location = alternate_location - end - - def location - return @location if defined?(@location) - - @location = location_from_config || location_from_alternate - end - - private - attr_reader :name, :config, :alternate_location - - def location_from_config - location_from(config_location, true) - end - - def location_from_alternate - location_from(alternate_location) - end - - def location_from(location, show_usage = false) - if location && !location.empty? - if File.exist?(location) - location - else - usage if show_usage - nil - end - end - end - - def config_location - @config_location ||= config.get(property_name) - end - - def property_name - "#{name}.config" - end - - def environment_variable_name - "#{name.upcase}_CONFIG" - end - - def yaml_property_name - property_name.gsub(/_/, '.') - end - - def usage - $stderr.puts "Warning: #{name} config file '#{config_location}' does not exist" - $stderr.puts "Set the path to the config file using:" - $stderr.puts "\tgit config pre-commit.#{property_name} 'path/relative/to/git/dir/#{name}.config'" - $stderr.puts "Or in 'config/pre_commit.yml':" - $stderr.puts "\t#{yaml_property_name}: path/relative/to/git/dir/#{name}.config" - $stderr.puts "Or set the environment variable:" - $stderr.puts "\texport #{environment_variable_name}='path/relative/to/git/dir/#{name}.config'" - $stderr.puts "#{name} will look for a configuration file in the project root or use its default behavior.\n\n" - end - end - end - end -end diff --git a/lib/pre-commit/checks/shell.rb b/lib/pre-commit/checks/shell.rb deleted file mode 100644 index 27d16c3..0000000 --- a/lib/pre-commit/checks/shell.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'pre-commit/checks/plugin' -require 'shellwords' - -module PreCommit - module Checks - class Shell < Plugin - - private - - def execute(*args) - options = args.last.is_a?(::Hash) ? args.pop : {} - args = build_command(*args) - execute_raw(args, options) - end - - def build_command(*args) - args.flatten.map do |arg| - arg = arg.shellescape if arg != '|' && arg != '&&' && arg != '||' - arg - end.join(" ") - end - - def execute_raw(command, options = {}) - result = `#{command} 2>&1` - $?.success? == (options.fetch(:success_status, true)) ? nil : result - end - end - end -end diff --git a/lib/pre-commit/cli.rb b/lib/pre-commit/cli.rb deleted file mode 100644 index 843286b..0000000 --- a/lib/pre-commit/cli.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'fileutils' -require 'pre-commit/configuration' -require 'pre-commit/installer' -require 'pre-commit/list_evaluator' -require 'pre-commit/template' - -module PreCommit - - TemplateNotFound = Class.new(StandardError) - - class Cli - - def initialize(*args) - @args = args - end - - def execute() - action_name = @args.shift or 'help' - action = :"execute_#{action_name}" - if respond_to?(action) - then send(action, *@args) - else execute_help(action_name, *@args) - end - end - - def execute_help(*args) - warn "Unknown parameters: #{args.map(&:inspect) * " "}" unless args.empty? - warn "Usage: pre-commit install" - warn "Usage: pre-commit list" - warn "Usage: pre-commit plugins" - warn "Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin'" - warn "Usage: pre-commit check1 [check2...]" - args.empty? # return status, it's ok if user requested help - end - - def execute_run(*args) - require 'pre-commit/runner' - PreCommit::Runner.new.run(*args).tap { |ok| puts "No failed checks." if ok } - end - - def execute_install(key = nil, *args) - PreCommit::Installer.new(key).install - end - - def execute_list(*args) - puts list_evaluator.list - true - end - - def execute_plugins(*args) - puts list_evaluator.plugins - true - end - - def execute_new(*args) - PreCommit::Template.new(*args).save - rescue ArgumentError => e - warn e - warn "Usage: pre-commit new plugin-name 'Author Name' author@email 'description of the plugin'" - end - - def execute_enable(*args) - config.enable(*args) - rescue ArgumentError - execute_help('enable', *args) - end - - def execute_disable(*args) - config.disable(*args) - rescue ArgumentError - execute_help('disable', *args) - end - - def config - @config ||= PreCommit::Configuration.new(PreCommit.pluginator) - end - - def list_evaluator - @list_evaluator ||= PreCommit::ListEvaluator.new(config) - end - - end -end diff --git a/lib/pre-commit/configuration.rb b/lib/pre-commit/configuration.rb deleted file mode 100644 index 6c05d1a..0000000 --- a/lib/pre-commit/configuration.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'pluginator' -require 'pre-commit/configuration/providers' -require 'pre-commit/plugins_list' - -module PreCommit - class Configuration - attr_reader :pluginator, :providers - - def initialize(pluginator, providers = nil) - @pluginator = (pluginator or PreCommit.pluginator) - @providers = (providers or Providers.new(@pluginator)) - end - - def get(name) - @providers[name.to_sym] - end - - def get_arr(name) - value = get(name) - case value - when nil then [] - when Array then value - else raise PreCommit::NotAnArray.new - end - end - - def get_combined(name) - get_arr(name) + get_arr("#{name}_add") - end - - def enable(plugin_name, type, check1, *checks) - checks.unshift(check1) # check1 is ArgumentError triger - checks.map!(&:to_sym) - @providers.update( plugin_name, "#{type}_remove", :-, checks ) - @providers.update( plugin_name, "#{type}_add", :+, (checks or []) - (@providers.default(type) or []) ) - true - rescue PreCommit::PluginNotFound => e - $stderr.puts e - false - end - - def disable(plugin_name, type, check1, *checks) - checks.unshift(check1) # check1 is ArgumentError triger - checks.map!(&:to_sym) - @providers.update( plugin_name, "#{type}_add", :-, checks ) - @providers.update( plugin_name, "#{type}_remove", :+, checks ) - true - rescue PreCommit::PluginNotFound => e - warn e.message - false - end - - end -end diff --git a/lib/pre-commit/configuration/providers.rb b/lib/pre-commit/configuration/providers.rb deleted file mode 100644 index cc62d36..0000000 --- a/lib/pre-commit/configuration/providers.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'plugins/pluginator/extensions/conversions' - -module PreCommit - class NotAnArray < StandardError - end - - class PluginNotFound < StandardError - end - - class Configuration - class Providers - include Pluginator::Extensions::Conversions - - def initialize(pluginator, plugins = nil) - @pluginator = pluginator - @plugins = plugins - end - - def [](name) - plugins.map{|plugin| plugin[name] }.compact.last - end - - def default(name) - plugins[0][name] - end - - def update(plugin_name, name, operation, list) - plugin = find_update_plugin(plugin_name) - name = name.to_sym - value = plugin[name] || [] - raise PreCommit::NotAnArray.new unless Array === value - value = value.send(operation, list) - plugin.update(name, value) - end - - def list - plugins.map{|plugin| "#{class2string(class2name(plugin.class))}(#{plugin.class.priority})" } - end - - private - def plugins - @plugins ||= @pluginator['configuration/providers'].sort_by(&:priority).map(&:new) - end - - def find_update_plugin(plugin_name) - plugins.detect{|plugin| class2string(class2name(plugin.class)) == plugin_name.to_s} || raise(PluginNotFound.new("Plugin not found for #{plugin_name}.")) - end - - end - end -end diff --git a/lib/pre-commit/configuration/top_level.rb b/lib/pre-commit/configuration/top_level.rb deleted file mode 100644 index ae629ad..0000000 --- a/lib/pre-commit/configuration/top_level.rb +++ /dev/null @@ -1,11 +0,0 @@ -module PreCommit - class Configuration - module TopLevel - def top_level - top_level = `git rev-parse --show-toplevel`.chomp.strip - raise "no git repo!" if top_level == "" - top_level - end - end - end -end diff --git a/lib/pre-commit/error_list.rb b/lib/pre-commit/error_list.rb deleted file mode 100644 index b37ae16..0000000 --- a/lib/pre-commit/error_list.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'pre-commit/line' - -module PreCommit - class ErrorList < Struct.new :errors - - def initialize(errors = []) - case errors - when "",nil then errors = [] - when String then errors = [PreCommit::Line.new(errors)] - end - super errors - end - - def to_a - errors.map(&:to_s) - end - - def to_s - to_a.join("\n") - end - - end -end diff --git a/lib/pre-commit/installer.rb b/lib/pre-commit/installer.rb deleted file mode 100644 index d79990c..0000000 --- a/lib/pre-commit/installer.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'fileutils' -require 'pre-commit/configuration' - -module PreCommit - class Installer - - TARGET_GIT_PATH = '.git' - TARGET_HOOKS_PATH = 'hooks/pre-commit' - TEMPLATE_DIR = File.expand_path("../../../templates/hooks/", __FILE__) - - attr_reader :key - - def initialize(key = nil) - @key = key || "automatic" - end - - def hook - templates[key.sub(/^--/, "")] - end - - def target - target_git_path = - if File.directory?(TARGET_GIT_PATH) - then TARGET_GIT_PATH - else File.readlines('.git').first.match(/gitdir: (.*)$/)[1] - end - File.join(target_git_path, TARGET_HOOKS_PATH) - end - - def install - if - hook - then - FileUtils.mkdir_p(File.dirname(target)) - FileUtils.cp(hook, target) - FileUtils.chmod(0755, target) - puts "Installed #{hook} to #{target}" - true - else - warn "Could not find template #{key}" - false - end - end - - def templates - @templates ||= begin - pattern = File.join(TEMPLATE_DIR, "*") - - Dir.glob(pattern).inject({}) do |hash, file| - key = file.match(/\/([^\/]+?)$/)[1] - hash[key] = file - hash - end - end - end - - end -end diff --git a/lib/pre-commit/line.rb b/lib/pre-commit/line.rb deleted file mode 100644 index ca9754f..0000000 --- a/lib/pre-commit/line.rb +++ /dev/null @@ -1,22 +0,0 @@ -module PreCommit - class Line < Struct.new :message, :file, :line, :code - - def to_s - result = message.to_s - unless empty? file - result = "#{result}#{"\n" unless empty?(result)}#{file}" - result = "#{result}:#{line}" unless empty? line - result = "#{result}:#{code}" unless empty? code - end - result - end - - protected - - def empty?(string) - string == nil || string == "" - end - - end -end - diff --git a/lib/pre-commit/list_evaluator.rb b/lib/pre-commit/list_evaluator.rb deleted file mode 100644 index 7386fff..0000000 --- a/lib/pre-commit/list_evaluator.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'pluginator' -require 'pre-commit/configuration' -require 'plugins/pluginator/extensions/conversions' -require 'pre-commit/plugins_list' - -module PreCommit - class ListEvaluator - include Pluginator::Extensions::Conversions - - attr_reader :config - - def initialize(config) - @config = config - end - - def list - <<-DATA -Available providers: #{config.providers.list.join(" ")} -Available checks : #{plugin_names.join(" ")} -Default checks : #{config.get_arr(:checks).join(" ")} -Enabled checks : #{checks_config.join(" ")} -Evaluated checks : #{checks_evaluated.join(" ")} -Default warnings : #{config.get_arr(:warnings).join(" ")} -Enabled warnings : #{warnings_config.join(" ")} -Evaluated warnings : #{warnings_evaluated.join(" ")} -DATA - end - - def plugins - list = config.pluginator['checks'].map{|plugin| [class2string(class2name(plugin)), plugin] }.sort - separator = list.map{|name, plugin| name.length }.max - list.map{ |name, plugin| format_plugin(name, separator, plugin) }.flatten - end - - def checks_config - get_combined_checks - get_arr_checks_remove - end - - def checks_evaluated(type = :evaluated_names) - PreCommit::PluginsList.new(get_combined_checks, get_arr_checks_remove) do |name| - config.pluginator.find_check(name) - end.send(type) - end - - def warnings_config - get_combined_warnings - get_arr_warnings_remove - end - - def warnings_evaluated(type = :evaluated_names) - PreCommit::PluginsList.new(get_combined_warnings, get_arr_warnings_remove) do |name| - config.pluginator.find_check(name) - end.send(type) - end - - private - - def plugin_names - config.pluginator['checks'].map{|plugin| class2string(class2name(plugin)) }.sort - end - - def format_plugin(name, separator, plugin) - line = [sprintf("%#{separator}s : %s", name, plugin.description)] - line << sprintf("%#{separator}s - includes: %s", "", plugin.includes.join(" ")) if plugin.respond_to?(:includes) - line << sprintf("%#{separator}s - excludes: %s", "", plugin.excludes.join(" ")) if plugin.respond_to?(:excludes) - line - end - - def get_combined_checks - @get_combined_checks ||= config.get_combined(:checks) - end - - def get_arr_checks_remove - @get_arr_checks_remove ||= config.get_arr("checks_remove") - end - - def get_combined_warnings - @get_combined_warnings ||= config.get_combined(:warnings) - end - - def get_arr_warnings_remove - @get_arr_warnings_remove ||= config.get_arr("warnings_remove") - end - - end -end diff --git a/lib/pre-commit/message.rb b/lib/pre-commit/message.rb deleted file mode 100644 index 5382a71..0000000 --- a/lib/pre-commit/message.rb +++ /dev/null @@ -1,17 +0,0 @@ -module PreCommit - class Message < Struct.new :message, :lines - - def to_s - result = message - result = "#{message}\n#{lines.map(&:to_s).join("\n")}" unless lines.nil? - result - end - - protected - - def empty?(string) - string == nil || string == "" - end - - end -end diff --git a/lib/pre-commit/plugins_list.rb b/lib/pre-commit/plugins_list.rb deleted file mode 100644 index f179cb7..0000000 --- a/lib/pre-commit/plugins_list.rb +++ /dev/null @@ -1,87 +0,0 @@ -module PreCommit - - def self.pluginator - Pluginator.find('pre_commit', :extends => [:find_check]) - end - - class PluginsList - attr_reader :configured_names, :configured_remove - - def initialize(configured_names, configured_remove, &block) - @configured_names = configured_names - @configured_remove = configured_remove - @class_finder = block - end - - def evaluated_names - evaluated_names_(evaluated_names_pairs).flatten.compact - end - - def list_to_run - list_to_run_(evaluated_names_pairs).flatten.compact - end - - private - - def evaluated_names_(list) - list.map{|name, klass, includes| [name] + evaluated_names_(includes||[]) } - end - - def list_to_run_(list) - list.map{|name, klass, includes| [klass] + list_to_run_(includes||[]) } - end - - def evaluated_names_pairs - list = find_classes_and_includes(configured_names) - excludes = excludes(list).flatten.compact - list = filter_excludes(list, excludes) - list = filter_excludes(list, configured_remove_aliases) - list = filter_callable(list) - list - end - - def find_classes_and_includes(list) - find_classes(list).map{ |name, klass| class_and_includes(name, klass) } - end - - def find_class(name) - @class_finder.call(name) - end - - def find_classes(list) - list.map{|name| [name, find_class(name)] }.reject{|name, klass| klass.nil? } - end - - def class_and_includes(name, klass) - [ name, klass, klass.respond_to?(:includes) ? find_classes_and_includes(klass.includes) : [] ] - end - - def excludes(list) - list.map{|name, klass, includes| class_excludes(klass) + excludes(includes) } - end - - def class_excludes(klass) - klass.respond_to?(:excludes) ? klass.excludes : [] - end - - def filter_excludes(list, excludes) - list.map{|name, klass, includes| excludes.include?(name) ? nil : [name, klass, filter_excludes(includes, excludes)] }.compact - end - - def filter_callable(list) - list.map{|name, klass, includes| - (klass.instance_methods.include?(:call) ? [name, klass] : [nil, nil]) << filter_callable(includes) - }.compact - end - - def configured_remove_aliases - @configured_remove_aliases ||= configured_remove.map{|remove| - list = [remove] - klass = find_class(remove) - list += klass.aliases if klass.respond_to?(:aliases) - list - }.flatten.compact - end - - end -end diff --git a/lib/pre-commit/runner.rb b/lib/pre-commit/runner.rb deleted file mode 100644 index aa665dd..0000000 --- a/lib/pre-commit/runner.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'pluginator' -require 'benchmark' - -require 'pre-commit/utils/staged_files' -require 'pre-commit/configuration' -require 'pre-commit/list_evaluator' -require 'pre-commit/error_list' - -module PreCommit - class Runner - include PreCommit::Utils::StagedFiles - - attr_reader :pluginator, :config, :debug - - def initialize(stderr = nil, staged_files = nil, config = nil, pluginator = nil) - @stderr = (stderr or $stderr) - @pluginator = (pluginator or PreCommit.pluginator) - @config = (config or PreCommit::Configuration.new(@pluginator)) - @staged_files = staged_files - @debug = ENV["PRE_COMMIT_DEBUG"] - end - - def run(*args) - set_staged_files(*args) - run_single(:warnings) - run_single(:checks ) or return false - true - end - - def run_single(name) - show_output(name, execute(list_to_run(name))) - end - - def show_output(name, list) - if list.any? - @stderr.puts send(name, list) - return false - end - true - end - - def execute(list) - list.map do |cmd| - result = nil - - seconds = Benchmark.realtime do - result = cmd.new(pluginator, config, list).call(staged_files.dup) - end - - puts "#{cmd} #{seconds*1000}ms" if debug - - result - end.compact - end - - def list_to_run(name) - list_evaluator.send(:"#{name}_evaluated", :list_to_run) - end - - def list_evaluator - @list_evaluator ||= PreCommit::ListEvaluator.new(config) - end - - def warnings(list) - <<-WARNINGS -pre-commit: Some warnings were raised. These will not stop commit: -#{errors_to_string(list)} -WARNINGS - end - - def checks(list) - <<-ERRORS -pre-commit: Stopping commit because of errors. -#{errors_to_string(list)} -pre-commit: You can bypass this check using `git commit -n` - -ERRORS - end - - def errors_to_string(list) - list.map(&:to_s).join("\n") - end - - end -end diff --git a/lib/pre-commit/support/csslint/csslint.js b/lib/pre-commit/support/csslint/csslint.js deleted file mode 100644 index d86142d..0000000 --- a/lib/pre-commit/support/csslint/csslint.js +++ /dev/null @@ -1,9259 +0,0 @@ -/*! -CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ -/* Build: v0.10.0 15-August-2013 01:07:22 */ -var exports = exports || {}; -var CSSLint = (function(){ -/*! -Parser-Lib -Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ -var parserlib = {}; -(function(){ - - -/** - * A generic base to inherit from for any object - * that needs event handling. - * @class EventTarget - * @constructor - */ -function EventTarget(){ - - /** - * The array of listeners for various events. - * @type Object - * @property _listeners - * @private - */ - this._listeners = {}; -} - -EventTarget.prototype = { - - //restore constructor - constructor: EventTarget, - - /** - * Adds a listener for a given event type. - * @param {String} type The type of event to add a listener for. - * @param {Function} listener The function to call when the event occurs. - * @return {void} - * @method addListener - */ - addListener: function(type, listener){ - if (!this._listeners[type]){ - this._listeners[type] = []; - } - - this._listeners[type].push(listener); - }, - - /** - * Fires an event based on the passed-in object. - * @param {Object|String} event An object with at least a 'type' attribute - * or a string indicating the event name. - * @return {void} - * @method fire - */ - fire: function(event){ - if (typeof event == "string"){ - event = { type: event }; - } - if (typeof event.target != "undefined"){ - event.target = this; - } - - if (typeof event.type == "undefined"){ - throw new Error("Event object missing 'type' property."); - } - - if (this._listeners[event.type]){ - - //create a copy of the array and use that so listeners can't chane - var listeners = this._listeners[event.type].concat(); - for (var i=0, len=listeners.length; i < len; i++){ - listeners[i].call(this, event); - } - } - }, - - /** - * Removes a listener for a given event type. - * @param {String} type The type of event to remove a listener from. - * @param {Function} listener The function to remove from the event. - * @return {void} - * @method removeListener - */ - removeListener: function(type, listener){ - if (this._listeners[type]){ - var listeners = this._listeners[type]; - for (var i=0, len=listeners.length; i < len; i++){ - if (listeners[i] === listener){ - listeners.splice(i, 1); - break; - } - } - - - } - } -}; -/** - * Convenient way to read through strings. - * @namespace parserlib.util - * @class StringReader - * @constructor - * @param {String} text The text to read. - */ -function StringReader(text){ - - /** - * The input text with line endings normalized. - * @property _input - * @type String - * @private - */ - this._input = text.replace(/\n\r?/g, "\n"); - - - /** - * The row for the character to be read next. - * @property _line - * @type int - * @private - */ - this._line = 1; - - - /** - * The column for the character to be read next. - * @property _col - * @type int - * @private - */ - this._col = 1; - - /** - * The index of the character in the input to be read next. - * @property _cursor - * @type int - * @private - */ - this._cursor = 0; -} - -StringReader.prototype = { - - //restore constructor - constructor: StringReader, - - //------------------------------------------------------------------------- - // Position info - //------------------------------------------------------------------------- - - /** - * Returns the column of the character to be read next. - * @return {int} The column of the character to be read next. - * @method getCol - */ - getCol: function(){ - return this._col; - }, - - /** - * Returns the row of the character to be read next. - * @return {int} The row of the character to be read next. - * @method getLine - */ - getLine: function(){ - return this._line ; - }, - - /** - * Determines if you're at the end of the input. - * @return {Boolean} True if there's no more input, false otherwise. - * @method eof - */ - eof: function(){ - return (this._cursor == this._input.length); - }, - - //------------------------------------------------------------------------- - // Basic reading - //------------------------------------------------------------------------- - - /** - * Reads the next character without advancing the cursor. - * @param {int} count How many characters to look ahead (default is 1). - * @return {String} The next character or null if there is no next character. - * @method peek - */ - peek: function(count){ - var c = null; - count = (typeof count == "undefined" ? 1 : count); - - //if we're not at the end of the input... - if (this._cursor < this._input.length){ - - //get character and increment cursor and column - c = this._input.charAt(this._cursor + count - 1); - } - - return c; - }, - - /** - * Reads the next character from the input and adjusts the row and column - * accordingly. - * @return {String} The next character or null if there is no next character. - * @method read - */ - read: function(){ - var c = null; - - //if we're not at the end of the input... - if (this._cursor < this._input.length){ - - //if the last character was a newline, increment row count - //and reset column count - if (this._input.charAt(this._cursor) == "\n"){ - this._line++; - this._col=1; - } else { - this._col++; - } - - //get character and increment cursor and column - c = this._input.charAt(this._cursor++); - } - - return c; - }, - - //------------------------------------------------------------------------- - // Misc - //------------------------------------------------------------------------- - - /** - * Saves the current location so it can be returned to later. - * @method mark - * @return {void} - */ - mark: function(){ - this._bookmark = { - cursor: this._cursor, - line: this._line, - col: this._col - }; - }, - - reset: function(){ - if (this._bookmark){ - this._cursor = this._bookmark.cursor; - this._line = this._bookmark.line; - this._col = this._bookmark.col; - delete this._bookmark; - } - }, - - //------------------------------------------------------------------------- - // Advanced reading - //------------------------------------------------------------------------- - - /** - * Reads up to and including the given string. Throws an error if that - * string is not found. - * @param {String} pattern The string to read. - * @return {String} The string when it is found. - * @throws Error when the string pattern is not found. - * @method readTo - */ - readTo: function(pattern){ - - var buffer = "", - c; - - /* - * First, buffer must be the same length as the pattern. - * Then, buffer must end with the pattern or else reach the - * end of the input. - */ - while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length){ - c = this.read(); - if (c){ - buffer += c; - } else { - throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + "."); - } - } - - return buffer; - - }, - - /** - * Reads characters while each character causes the given - * filter function to return true. The function is passed - * in each character and either returns true to continue - * reading or false to stop. - * @param {Function} filter The function to read on each character. - * @return {String} The string made up of all characters that passed the - * filter check. - * @method readWhile - */ - readWhile: function(filter){ - - var buffer = "", - c = this.read(); - - while(c !== null && filter(c)){ - buffer += c; - c = this.read(); - } - - return buffer; - - }, - - /** - * Reads characters that match either text or a regular expression and - * returns those characters. If a match is found, the row and column - * are adjusted; if no match is found, the reader's state is unchanged. - * reading or false to stop. - * @param {String|RegExp} matchter If a string, then the literal string - * value is searched for. If a regular expression, then any string - * matching the pattern is search for. - * @return {String} The string made up of all characters that matched or - * null if there was no match. - * @method readMatch - */ - readMatch: function(matcher){ - - var source = this._input.substring(this._cursor), - value = null; - - //if it's a string, just do a straight match - if (typeof matcher == "string"){ - if (source.indexOf(matcher) === 0){ - value = this.readCount(matcher.length); - } - } else if (matcher instanceof RegExp){ - if (matcher.test(source)){ - value = this.readCount(RegExp.lastMatch.length); - } - } - - return value; - }, - - - /** - * Reads a given number of characters. If the end of the input is reached, - * it reads only the remaining characters and does not throw an error. - * @param {int} count The number of characters to read. - * @return {String} The string made up the read characters. - * @method readCount - */ - readCount: function(count){ - var buffer = ""; - - while(count--){ - buffer += this.read(); - } - - return buffer; - } - -}; -/** - * Type to use when a syntax error occurs. - * @class SyntaxError - * @namespace parserlib.util - * @constructor - * @param {String} message The error message. - * @param {int} line The line at which the error occurred. - * @param {int} col The column at which the error occurred. - */ -function SyntaxError(message, line, col){ - - /** - * The column at which the error occurred. - * @type int - * @property col - */ - this.col = col; - - /** - * The line at which the error occurred. - * @type int - * @property line - */ - this.line = line; - - /** - * The text representation of the unit. - * @type String - * @property text - */ - this.message = message; - -} - -//inherit from Error -SyntaxError.prototype = new Error(); -/** - * Base type to represent a single syntactic unit. - * @class SyntaxUnit - * @namespace parserlib.util - * @constructor - * @param {String} text The text of the unit. - * @param {int} line The line of text on which the unit resides. - * @param {int} col The column of text on which the unit resides. - */ -function SyntaxUnit(text, line, col, type){ - - - /** - * The column of text on which the unit resides. - * @type int - * @property col - */ - this.col = col; - - /** - * The line of text on which the unit resides. - * @type int - * @property line - */ - this.line = line; - - /** - * The text representation of the unit. - * @type String - * @property text - */ - this.text = text; - - /** - * The type of syntax unit. - * @type int - * @property type - */ - this.type = type; -} - -/** - * Create a new syntax unit based solely on the given token. - * Convenience method for creating a new syntax unit when - * it represents a single token instead of multiple. - * @param {Object} token The token object to represent. - * @return {parserlib.util.SyntaxUnit} The object representing the token. - * @static - * @method fromToken - */ -SyntaxUnit.fromToken = function(token){ - return new SyntaxUnit(token.value, token.startLine, token.startCol); -}; - -SyntaxUnit.prototype = { - - //restore constructor - constructor: SyntaxUnit, - - /** - * Returns the text representation of the unit. - * @return {String} The text representation of the unit. - * @method valueOf - */ - valueOf: function(){ - return this.toString(); - }, - - /** - * Returns the text representation of the unit. - * @return {String} The text representation of the unit. - * @method toString - */ - toString: function(){ - return this.text; - } - -}; -/*global StringReader, SyntaxError*/ - -/** - * Generic TokenStream providing base functionality. - * @class TokenStreamBase - * @namespace parserlib.util - * @constructor - * @param {String|StringReader} input The text to tokenize or a reader from - * which to read the input. - */ -function TokenStreamBase(input, tokenData){ - - /** - * The string reader for easy access to the text. - * @type StringReader - * @property _reader - * @private - */ - this._reader = input ? new StringReader(input.toString()) : null; - - /** - * Token object for the last consumed token. - * @type Token - * @property _token - * @private - */ - this._token = null; - - /** - * The array of token information. - * @type Array - * @property _tokenData - * @private - */ - this._tokenData = tokenData; - - /** - * Lookahead token buffer. - * @type Array - * @property _lt - * @private - */ - this._lt = []; - - /** - * Lookahead token buffer index. - * @type int - * @property _ltIndex - * @private - */ - this._ltIndex = 0; - - this._ltIndexCache = []; -} - -/** - * Accepts an array of token information and outputs - * an array of token data containing key-value mappings - * and matching functions that the TokenStream needs. - * @param {Array} tokens An array of token descriptors. - * @return {Array} An array of processed token data. - * @method createTokenData - * @static - */ -TokenStreamBase.createTokenData = function(tokens){ - - var nameMap = [], - typeMap = {}, - tokenData = tokens.concat([]), - i = 0, - len = tokenData.length+1; - - tokenData.UNKNOWN = -1; - tokenData.unshift({name:"EOF"}); - - for (; i < len; i++){ - nameMap.push(tokenData[i].name); - tokenData[tokenData[i].name] = i; - if (tokenData[i].text){ - typeMap[tokenData[i].text] = i; - } - } - - tokenData.name = function(tt){ - return nameMap[tt]; - }; - - tokenData.type = function(c){ - return typeMap[c]; - }; - - return tokenData; -}; - -TokenStreamBase.prototype = { - - //restore constructor - constructor: TokenStreamBase, - - //------------------------------------------------------------------------- - // Matching methods - //------------------------------------------------------------------------- - - /** - * Determines if the next token matches the given token type. - * If so, that token is consumed; if not, the token is placed - * back onto the token stream. You can pass in any number of - * token types and this will return true if any of the token - * types is found. - * @param {int|int[]} tokenTypes Either a single token type or an array of - * token types that the next token might be. If an array is passed, - * it's assumed that the token can be any of these. - * @param {variant} channel (Optional) The channel to read from. If not - * provided, reads from the default (unnamed) channel. - * @return {Boolean} True if the token type matches, false if not. - * @method match - */ - match: function(tokenTypes, channel){ - - //always convert to an array, makes things easier - if (!(tokenTypes instanceof Array)){ - tokenTypes = [tokenTypes]; - } - - var tt = this.get(channel), - i = 0, - len = tokenTypes.length; - - while(i < len){ - if (tt == tokenTypes[i++]){ - return true; - } - } - - //no match found, put the token back - this.unget(); - return false; - }, - - /** - * Determines if the next token matches the given token type. - * If so, that token is consumed; if not, an error is thrown. - * @param {int|int[]} tokenTypes Either a single token type or an array of - * token types that the next token should be. If an array is passed, - * it's assumed that the token must be one of these. - * @param {variant} channel (Optional) The channel to read from. If not - * provided, reads from the default (unnamed) channel. - * @return {void} - * @method mustMatch - */ - mustMatch: function(tokenTypes, channel){ - - var token; - - //always convert to an array, makes things easier - if (!(tokenTypes instanceof Array)){ - tokenTypes = [tokenTypes]; - } - - if (!this.match.apply(this, arguments)){ - token = this.LT(1); - throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name + - " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol); - } - }, - - //------------------------------------------------------------------------- - // Consuming methods - //------------------------------------------------------------------------- - - /** - * Keeps reading from the token stream until either one of the specified - * token types is found or until the end of the input is reached. - * @param {int|int[]} tokenTypes Either a single token type or an array of - * token types that the next token should be. If an array is passed, - * it's assumed that the token must be one of these. - * @param {variant} channel (Optional) The channel to read from. If not - * provided, reads from the default (unnamed) channel. - * @return {void} - * @method advance - */ - advance: function(tokenTypes, channel){ - - while(this.LA(0) !== 0 && !this.match(tokenTypes, channel)){ - this.get(); - } - - return this.LA(0); - }, - - /** - * Consumes the next token from the token stream. - * @return {int} The token type of the token that was just consumed. - * @method get - */ - get: function(channel){ - - var tokenInfo = this._tokenData, - reader = this._reader, - value, - i =0, - len = tokenInfo.length, - found = false, - token, - info; - - //check the lookahead buffer first - if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length){ - - i++; - this._token = this._lt[this._ltIndex++]; - info = tokenInfo[this._token.type]; - - //obey channels logic - while((info.channel !== undefined && channel !== info.channel) && - this._ltIndex < this._lt.length){ - this._token = this._lt[this._ltIndex++]; - info = tokenInfo[this._token.type]; - i++; - } - - //here be dragons - if ((info.channel === undefined || channel === info.channel) && - this._ltIndex <= this._lt.length){ - this._ltIndexCache.push(i); - return this._token.type; - } - } - - //call token retriever method - token = this._getToken(); - - //if it should be hidden, don't save a token - if (token.type > -1 && !tokenInfo[token.type].hide){ - - //apply token channel - token.channel = tokenInfo[token.type].channel; - - //save for later - this._token = token; - this._lt.push(token); - - //save space that will be moved (must be done before array is truncated) - this._ltIndexCache.push(this._lt.length - this._ltIndex + i); - - //keep the buffer under 5 items - if (this._lt.length > 5){ - this._lt.shift(); - } - - //also keep the shift buffer under 5 items - if (this._ltIndexCache.length > 5){ - this._ltIndexCache.shift(); - } - - //update lookahead index - this._ltIndex = this._lt.length; - } - - /* - * Skip to the next token if: - * 1. The token type is marked as hidden. - * 2. The token type has a channel specified and it isn't the current channel. - */ - info = tokenInfo[token.type]; - if (info && - (info.hide || - (info.channel !== undefined && channel !== info.channel))){ - return this.get(channel); - } else { - //return just the type - return token.type; - } - }, - - /** - * Looks ahead a certain number of tokens and returns the token type at - * that position. This will throw an error if you lookahead past the - * end of input, past the size of the lookahead buffer, or back past - * the first token in the lookahead buffer. - * @param {int} The index of the token type to retrieve. 0 for the - * current token, 1 for the next, -1 for the previous, etc. - * @return {int} The token type of the token in the given position. - * @method LA - */ - LA: function(index){ - var total = index, - tt; - if (index > 0){ - //TODO: Store 5 somewhere - if (index > 5){ - throw new Error("Too much lookahead."); - } - - //get all those tokens - while(total){ - tt = this.get(); - total--; - } - - //unget all those tokens - while(total < index){ - this.unget(); - total++; - } - } else if (index < 0){ - - if(this._lt[this._ltIndex+index]){ - tt = this._lt[this._ltIndex+index].type; - } else { - throw new Error("Too much lookbehind."); - } - - } else { - tt = this._token.type; - } - - return tt; - - }, - - /** - * Looks ahead a certain number of tokens and returns the token at - * that position. This will throw an error if you lookahead past the - * end of input, past the size of the lookahead buffer, or back past - * the first token in the lookahead buffer. - * @param {int} The index of the token type to retrieve. 0 for the - * current token, 1 for the next, -1 for the previous, etc. - * @return {Object} The token of the token in the given position. - * @method LA - */ - LT: function(index){ - - //lookahead first to prime the token buffer - this.LA(index); - - //now find the token, subtract one because _ltIndex is already at the next index - return this._lt[this._ltIndex+index-1]; - }, - - /** - * Returns the token type for the next token in the stream without - * consuming it. - * @return {int} The token type of the next token in the stream. - * @method peek - */ - peek: function(){ - return this.LA(1); - }, - - /** - * Returns the actual token object for the last consumed token. - * @return {Token} The token object for the last consumed token. - * @method token - */ - token: function(){ - return this._token; - }, - - /** - * Returns the name of the token for the given token type. - * @param {int} tokenType The type of token to get the name of. - * @return {String} The name of the token or "UNKNOWN_TOKEN" for any - * invalid token type. - * @method tokenName - */ - tokenName: function(tokenType){ - if (tokenType < 0 || tokenType > this._tokenData.length){ - return "UNKNOWN_TOKEN"; - } else { - return this._tokenData[tokenType].name; - } - }, - - /** - * Returns the token type value for the given token name. - * @param {String} tokenName The name of the token whose value should be returned. - * @return {int} The token type value for the given token name or -1 - * for an unknown token. - * @method tokenName - */ - tokenType: function(tokenName){ - return this._tokenData[tokenName] || -1; - }, - - /** - * Returns the last consumed token to the token stream. - * @method unget - */ - unget: function(){ - //if (this._ltIndex > -1){ - if (this._ltIndexCache.length){ - this._ltIndex -= this._ltIndexCache.pop();//--; - this._token = this._lt[this._ltIndex - 1]; - } else { - throw new Error("Too much lookahead."); - } - } - -}; - - - - -parserlib.util = { -StringReader: StringReader, -SyntaxError : SyntaxError, -SyntaxUnit : SyntaxUnit, -EventTarget : EventTarget, -TokenStreamBase : TokenStreamBase -}; -})(); - - -/* -Parser-Lib -Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ -(function(){ -var EventTarget = parserlib.util.EventTarget, -TokenStreamBase = parserlib.util.TokenStreamBase, -StringReader = parserlib.util.StringReader, -SyntaxError = parserlib.util.SyntaxError, -SyntaxUnit = parserlib.util.SyntaxUnit; - - -var Colors = { - aliceblue :"#f0f8ff", - antiquewhite :"#faebd7", - aqua :"#00ffff", - aquamarine :"#7fffd4", - azure :"#f0ffff", - beige :"#f5f5dc", - bisque :"#ffe4c4", - black :"#000000", - blanchedalmond :"#ffebcd", - blue :"#0000ff", - blueviolet :"#8a2be2", - brown :"#a52a2a", - burlywood :"#deb887", - cadetblue :"#5f9ea0", - chartreuse :"#7fff00", - chocolate :"#d2691e", - coral :"#ff7f50", - cornflowerblue :"#6495ed", - cornsilk :"#fff8dc", - crimson :"#dc143c", - cyan :"#00ffff", - darkblue :"#00008b", - darkcyan :"#008b8b", - darkgoldenrod :"#b8860b", - darkgray :"#a9a9a9", - darkgreen :"#006400", - darkkhaki :"#bdb76b", - darkmagenta :"#8b008b", - darkolivegreen :"#556b2f", - darkorange :"#ff8c00", - darkorchid :"#9932cc", - darkred :"#8b0000", - darksalmon :"#e9967a", - darkseagreen :"#8fbc8f", - darkslateblue :"#483d8b", - darkslategray :"#2f4f4f", - darkturquoise :"#00ced1", - darkviolet :"#9400d3", - deeppink :"#ff1493", - deepskyblue :"#00bfff", - dimgray :"#696969", - dodgerblue :"#1e90ff", - firebrick :"#b22222", - floralwhite :"#fffaf0", - forestgreen :"#228b22", - fuchsia :"#ff00ff", - gainsboro :"#dcdcdc", - ghostwhite :"#f8f8ff", - gold :"#ffd700", - goldenrod :"#daa520", - gray :"#808080", - green :"#008000", - greenyellow :"#adff2f", - honeydew :"#f0fff0", - hotpink :"#ff69b4", - indianred :"#cd5c5c", - indigo :"#4b0082", - ivory :"#fffff0", - khaki :"#f0e68c", - lavender :"#e6e6fa", - lavenderblush :"#fff0f5", - lawngreen :"#7cfc00", - lemonchiffon :"#fffacd", - lightblue :"#add8e6", - lightcoral :"#f08080", - lightcyan :"#e0ffff", - lightgoldenrodyellow :"#fafad2", - lightgray :"#d3d3d3", - lightgreen :"#90ee90", - lightpink :"#ffb6c1", - lightsalmon :"#ffa07a", - lightseagreen :"#20b2aa", - lightskyblue :"#87cefa", - lightslategray :"#778899", - lightsteelblue :"#b0c4de", - lightyellow :"#ffffe0", - lime :"#00ff00", - limegreen :"#32cd32", - linen :"#faf0e6", - magenta :"#ff00ff", - maroon :"#800000", - mediumaquamarine:"#66cdaa", - mediumblue :"#0000cd", - mediumorchid :"#ba55d3", - mediumpurple :"#9370d8", - mediumseagreen :"#3cb371", - mediumslateblue :"#7b68ee", - mediumspringgreen :"#00fa9a", - mediumturquoise :"#48d1cc", - mediumvioletred :"#c71585", - midnightblue :"#191970", - mintcream :"#f5fffa", - mistyrose :"#ffe4e1", - moccasin :"#ffe4b5", - navajowhite :"#ffdead", - navy :"#000080", - oldlace :"#fdf5e6", - olive :"#808000", - olivedrab :"#6b8e23", - orange :"#ffa500", - orangered :"#ff4500", - orchid :"#da70d6", - palegoldenrod :"#eee8aa", - palegreen :"#98fb98", - paleturquoise :"#afeeee", - palevioletred :"#d87093", - papayawhip :"#ffefd5", - peachpuff :"#ffdab9", - peru :"#cd853f", - pink :"#ffc0cb", - plum :"#dda0dd", - powderblue :"#b0e0e6", - purple :"#800080", - red :"#ff0000", - rosybrown :"#bc8f8f", - royalblue :"#4169e1", - saddlebrown :"#8b4513", - salmon :"#fa8072", - sandybrown :"#f4a460", - seagreen :"#2e8b57", - seashell :"#fff5ee", - sienna :"#a0522d", - silver :"#c0c0c0", - skyblue :"#87ceeb", - slateblue :"#6a5acd", - slategray :"#708090", - snow :"#fffafa", - springgreen :"#00ff7f", - steelblue :"#4682b4", - tan :"#d2b48c", - teal :"#008080", - thistle :"#d8bfd8", - tomato :"#ff6347", - turquoise :"#40e0d0", - violet :"#ee82ee", - wheat :"#f5deb3", - white :"#ffffff", - whitesmoke :"#f5f5f5", - yellow :"#ffff00", - yellowgreen :"#9acd32", - //CSS2 system colors http://www.w3.org/TR/css3-color/#css2-system - activeBorder :"Active window border.", - activecaption :"Active window caption.", - appworkspace :"Background color of multiple document interface.", - background :"Desktop background.", - buttonface :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.", - buttonhighlight :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.", - buttonshadow :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.", - buttontext :"Text on push buttons.", - captiontext :"Text in caption, size box, and scrollbar arrow box.", - graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.", - highlight :"Item(s) selected in a control.", - highlighttext :"Text of item(s) selected in a control.", - inactiveborder :"Inactive window border.", - inactivecaption :"Inactive window caption.", - inactivecaptiontext :"Color of text in an inactive caption.", - infobackground :"Background color for tooltip controls.", - infotext :"Text color for tooltip controls.", - menu :"Menu background.", - menutext :"Text in menus.", - scrollbar :"Scroll bar gray area.", - threeddarkshadow :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", - threedface :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", - threedhighlight :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", - threedlightshadow :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", - threedshadow :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", - window :"Window background.", - windowframe :"Window frame.", - windowtext :"Text in windows." -}; -/*global SyntaxUnit, Parser*/ -/** - * Represents a selector combinator (whitespace, +, >). - * @namespace parserlib.css - * @class Combinator - * @extends parserlib.util.SyntaxUnit - * @constructor - * @param {String} text The text representation of the unit. - * @param {int} line The line of text on which the unit resides. - * @param {int} col The column of text on which the unit resides. - */ -function Combinator(text, line, col){ - - SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE); - - /** - * The type of modifier. - * @type String - * @property type - */ - this.type = "unknown"; - - //pretty simple - if (/^\s+$/.test(text)){ - this.type = "descendant"; - } else if (text == ">"){ - this.type = "child"; - } else if (text == "+"){ - this.type = "adjacent-sibling"; - } else if (text == "~"){ - this.type = "sibling"; - } - -} - -Combinator.prototype = new SyntaxUnit(); -Combinator.prototype.constructor = Combinator; - - -/*global SyntaxUnit, Parser*/ -/** - * Represents a media feature, such as max-width:500. - * @namespace parserlib.css - * @class MediaFeature - * @extends parserlib.util.SyntaxUnit - * @constructor - * @param {SyntaxUnit} name The name of the feature. - * @param {SyntaxUnit} value The value of the feature or null if none. - */ -function MediaFeature(name, value){ - - SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE); - - /** - * The name of the media feature - * @type String - * @property name - */ - this.name = name; - - /** - * The value for the feature or null if there is none. - * @type SyntaxUnit - * @property value - */ - this.value = value; -} - -MediaFeature.prototype = new SyntaxUnit(); -MediaFeature.prototype.constructor = MediaFeature; - - -/*global SyntaxUnit, Parser*/ -/** - * Represents an individual media query. - * @namespace parserlib.css - * @class MediaQuery - * @extends parserlib.util.SyntaxUnit - * @constructor - * @param {String} modifier The modifier "not" or "only" (or null). - * @param {String} mediaType The type of media (i.e., "print"). - * @param {Array} parts Array of selectors parts making up this selector. - * @param {int} line The line of text on which the unit resides. - * @param {int} col The column of text on which the unit resides. - */ -function MediaQuery(modifier, mediaType, features, line, col){ - - SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE); - - /** - * The media modifier ("not" or "only") - * @type String - * @property modifier - */ - this.modifier = modifier; - - /** - * The mediaType (i.e., "print") - * @type String - * @property mediaType - */ - this.mediaType = mediaType; - - /** - * The parts that make up the selector. - * @type Array - * @property features - */ - this.features = features; - -} - -MediaQuery.prototype = new SyntaxUnit(); -MediaQuery.prototype.constructor = MediaQuery; - - -/*global Tokens, TokenStream, SyntaxError, Properties, Validation, ValidationError, SyntaxUnit, - PropertyValue, PropertyValuePart, SelectorPart, SelectorSubPart, Selector, - PropertyName, Combinator, MediaFeature, MediaQuery, EventTarget */ - -/** - * A CSS3 parser. - * @namespace parserlib.css - * @class Parser - * @constructor - * @param {Object} options (Optional) Various options for the parser: - * starHack (true|false) to allow IE6 star hack as valid, - * underscoreHack (true|false) to interpret leading underscores - * as IE6-7 targeting for known properties, ieFilters (true|false) - * to indicate that IE < 8 filters should be accepted and not throw - * syntax errors. - */ -function Parser(options){ - - //inherit event functionality - EventTarget.call(this); - - - this.options = options || {}; - - this._tokenStream = null; -} - -//Static constants -Parser.DEFAULT_TYPE = 0; -Parser.COMBINATOR_TYPE = 1; -Parser.MEDIA_FEATURE_TYPE = 2; -Parser.MEDIA_QUERY_TYPE = 3; -Parser.PROPERTY_NAME_TYPE = 4; -Parser.PROPERTY_VALUE_TYPE = 5; -Parser.PROPERTY_VALUE_PART_TYPE = 6; -Parser.SELECTOR_TYPE = 7; -Parser.SELECTOR_PART_TYPE = 8; -Parser.SELECTOR_SUB_PART_TYPE = 9; - -Parser.prototype = function(){ - - var proto = new EventTarget(), //new prototype - prop, - additions = { - - //restore constructor - constructor: Parser, - - //instance constants - yuck - DEFAULT_TYPE : 0, - COMBINATOR_TYPE : 1, - MEDIA_FEATURE_TYPE : 2, - MEDIA_QUERY_TYPE : 3, - PROPERTY_NAME_TYPE : 4, - PROPERTY_VALUE_TYPE : 5, - PROPERTY_VALUE_PART_TYPE : 6, - SELECTOR_TYPE : 7, - SELECTOR_PART_TYPE : 8, - SELECTOR_SUB_PART_TYPE : 9, - - //----------------------------------------------------------------- - // Grammar - //----------------------------------------------------------------- - - _stylesheet: function(){ - - /* - * stylesheet - * : [ CHARSET_SYM S* STRING S* ';' ]? - * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]* - * [ namespace [S|CDO|CDC]* ]* - * [ [ ruleset | media | page | font_face | keyframes ] [S|CDO|CDC]* ]* - * ; - */ - - var tokenStream = this._tokenStream, - charset = null, - count, - token, - tt; - - this.fire("startstylesheet"); - - //try to read character set - this._charset(); - - this._skipCruft(); - - //try to read imports - may be more than one - while (tokenStream.peek() == Tokens.IMPORT_SYM){ - this._import(); - this._skipCruft(); - } - - //try to read namespaces - may be more than one - while (tokenStream.peek() == Tokens.NAMESPACE_SYM){ - this._namespace(); - this._skipCruft(); - } - - //get the next token - tt = tokenStream.peek(); - - //try to read the rest - while(tt > Tokens.EOF){ - - try { - - switch(tt){ - case Tokens.MEDIA_SYM: - this._media(); - this._skipCruft(); - break; - case Tokens.PAGE_SYM: - this._page(); - this._skipCruft(); - break; - case Tokens.FONT_FACE_SYM: - this._font_face(); - this._skipCruft(); - break; - case Tokens.KEYFRAMES_SYM: - this._keyframes(); - this._skipCruft(); - break; - case Tokens.VIEWPORT_SYM: - this._viewport(); - this._skipCruft(); - break; - case Tokens.UNKNOWN_SYM: //unknown @ rule - tokenStream.get(); - if (!this.options.strict){ - - //fire error event - this.fire({ - type: "error", - error: null, - message: "Unknown @ rule: " + tokenStream.LT(0).value + ".", - line: tokenStream.LT(0).startLine, - col: tokenStream.LT(0).startCol - }); - - //skip braces - count=0; - while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) == Tokens.LBRACE){ - count++; //keep track of nesting depth - } - - while(count){ - tokenStream.advance([Tokens.RBRACE]); - count--; - } - - } else { - //not a syntax error, rethrow it - throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol); - } - break; - case Tokens.S: - this._readWhitespace(); - break; - default: - if(!this._ruleset()){ - - //error handling for known issues - switch(tt){ - case Tokens.CHARSET_SYM: - token = tokenStream.LT(1); - this._charset(false); - throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol); - case Tokens.IMPORT_SYM: - token = tokenStream.LT(1); - this._import(false); - throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol); - case Tokens.NAMESPACE_SYM: - token = tokenStream.LT(1); - this._namespace(false); - throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol); - default: - tokenStream.get(); //get the last token - this._unexpectedToken(tokenStream.token()); - } - - } - } - } catch(ex) { - if (ex instanceof SyntaxError && !this.options.strict){ - this.fire({ - type: "error", - error: ex, - message: ex.message, - line: ex.line, - col: ex.col - }); - } else { - throw ex; - } - } - - tt = tokenStream.peek(); - } - - if (tt != Tokens.EOF){ - this._unexpectedToken(tokenStream.token()); - } - - this.fire("endstylesheet"); - }, - - _charset: function(emit){ - var tokenStream = this._tokenStream, - charset, - token, - line, - col; - - if (tokenStream.match(Tokens.CHARSET_SYM)){ - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - - this._readWhitespace(); - tokenStream.mustMatch(Tokens.STRING); - - token = tokenStream.token(); - charset = token.value; - - this._readWhitespace(); - tokenStream.mustMatch(Tokens.SEMICOLON); - - if (emit !== false){ - this.fire({ - type: "charset", - charset:charset, - line: line, - col: col - }); - } - } - }, - - _import: function(emit){ - /* - * import - * : IMPORT_SYM S* - * [STRING|URI] S* media_query_list? ';' S* - */ - - var tokenStream = this._tokenStream, - tt, - uri, - importToken, - mediaList = []; - - //read import symbol - tokenStream.mustMatch(Tokens.IMPORT_SYM); - importToken = tokenStream.token(); - this._readWhitespace(); - - tokenStream.mustMatch([Tokens.STRING, Tokens.URI]); - - //grab the URI value - uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1"); - - this._readWhitespace(); - - mediaList = this._media_query_list(); - - //must end with a semicolon - tokenStream.mustMatch(Tokens.SEMICOLON); - this._readWhitespace(); - - if (emit !== false){ - this.fire({ - type: "import", - uri: uri, - media: mediaList, - line: importToken.startLine, - col: importToken.startCol - }); - } - - }, - - _namespace: function(emit){ - /* - * namespace - * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S* - */ - - var tokenStream = this._tokenStream, - line, - col, - prefix, - uri; - - //read import symbol - tokenStream.mustMatch(Tokens.NAMESPACE_SYM); - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - this._readWhitespace(); - - //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT - if (tokenStream.match(Tokens.IDENT)){ - prefix = tokenStream.token().value; - this._readWhitespace(); - } - - tokenStream.mustMatch([Tokens.STRING, Tokens.URI]); - /*if (!tokenStream.match(Tokens.STRING)){ - tokenStream.mustMatch(Tokens.URI); - }*/ - - //grab the URI value - uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1"); - - this._readWhitespace(); - - //must end with a semicolon - tokenStream.mustMatch(Tokens.SEMICOLON); - this._readWhitespace(); - - if (emit !== false){ - this.fire({ - type: "namespace", - prefix: prefix, - uri: uri, - line: line, - col: col - }); - } - - }, - - _media: function(){ - /* - * media - * : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S* - * ; - */ - var tokenStream = this._tokenStream, - line, - col, - mediaList;// = []; - - //look for @media - tokenStream.mustMatch(Tokens.MEDIA_SYM); - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - - this._readWhitespace(); - - mediaList = this._media_query_list(); - - tokenStream.mustMatch(Tokens.LBRACE); - this._readWhitespace(); - - this.fire({ - type: "startmedia", - media: mediaList, - line: line, - col: col - }); - - while(true) { - if (tokenStream.peek() == Tokens.PAGE_SYM){ - this._page(); - } else if (tokenStream.peek() == Tokens.FONT_FACE_SYM){ - this._font_face(); - } else if (!this._ruleset()){ - break; - } - } - - tokenStream.mustMatch(Tokens.RBRACE); - this._readWhitespace(); - - this.fire({ - type: "endmedia", - media: mediaList, - line: line, - col: col - }); - }, - - - //CSS3 Media Queries - _media_query_list: function(){ - /* - * media_query_list - * : S* [media_query [ ',' S* media_query ]* ]? - * ; - */ - var tokenStream = this._tokenStream, - mediaList = []; - - - this._readWhitespace(); - - if (tokenStream.peek() == Tokens.IDENT || tokenStream.peek() == Tokens.LPAREN){ - mediaList.push(this._media_query()); - } - - while(tokenStream.match(Tokens.COMMA)){ - this._readWhitespace(); - mediaList.push(this._media_query()); - } - - return mediaList; - }, - - /* - * Note: "expression" in the grammar maps to the _media_expression - * method. - - */ - _media_query: function(){ - /* - * media_query - * : [ONLY | NOT]? S* media_type S* [ AND S* expression ]* - * | expression [ AND S* expression ]* - * ; - */ - var tokenStream = this._tokenStream, - type = null, - ident = null, - token = null, - expressions = []; - - if (tokenStream.match(Tokens.IDENT)){ - ident = tokenStream.token().value.toLowerCase(); - - //since there's no custom tokens for these, need to manually check - if (ident != "only" && ident != "not"){ - tokenStream.unget(); - ident = null; - } else { - token = tokenStream.token(); - } - } - - this._readWhitespace(); - - if (tokenStream.peek() == Tokens.IDENT){ - type = this._media_type(); - if (token === null){ - token = tokenStream.token(); - } - } else if (tokenStream.peek() == Tokens.LPAREN){ - if (token === null){ - token = tokenStream.LT(1); - } - expressions.push(this._media_expression()); - } - - if (type === null && expressions.length === 0){ - return null; - } else { - this._readWhitespace(); - while (tokenStream.match(Tokens.IDENT)){ - if (tokenStream.token().value.toLowerCase() != "and"){ - this._unexpectedToken(tokenStream.token()); - } - - this._readWhitespace(); - expressions.push(this._media_expression()); - } - } - - return new MediaQuery(ident, type, expressions, token.startLine, token.startCol); - }, - - //CSS3 Media Queries - _media_type: function(){ - /* - * media_type - * : IDENT - * ; - */ - return this._media_feature(); - }, - - /** - * Note: in CSS3 Media Queries, this is called "expression". - * Renamed here to avoid conflict with CSS3 Selectors - * definition of "expression". Also note that "expr" in the - * grammar now maps to "expression" from CSS3 selectors. - * @method _media_expression - * @private - */ - _media_expression: function(){ - /* - * expression - * : '(' S* media_feature S* [ ':' S* expr ]? ')' S* - * ; - */ - var tokenStream = this._tokenStream, - feature = null, - token, - expression = null; - - tokenStream.mustMatch(Tokens.LPAREN); - - feature = this._media_feature(); - this._readWhitespace(); - - if (tokenStream.match(Tokens.COLON)){ - this._readWhitespace(); - token = tokenStream.LT(1); - expression = this._expression(); - } - - tokenStream.mustMatch(Tokens.RPAREN); - this._readWhitespace(); - - return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null)); - }, - - //CSS3 Media Queries - _media_feature: function(){ - /* - * media_feature - * : IDENT - * ; - */ - var tokenStream = this._tokenStream; - - tokenStream.mustMatch(Tokens.IDENT); - - return SyntaxUnit.fromToken(tokenStream.token()); - }, - - //CSS3 Paged Media - _page: function(){ - /* - * page: - * PAGE_SYM S* IDENT? pseudo_page? S* - * '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S* - * ; - */ - var tokenStream = this._tokenStream, - line, - col, - identifier = null, - pseudoPage = null; - - //look for @page - tokenStream.mustMatch(Tokens.PAGE_SYM); - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - - this._readWhitespace(); - - if (tokenStream.match(Tokens.IDENT)){ - identifier = tokenStream.token().value; - - //The value 'auto' may not be used as a page name and MUST be treated as a syntax error. - if (identifier.toLowerCase() === "auto"){ - this._unexpectedToken(tokenStream.token()); - } - } - - //see if there's a colon upcoming - if (tokenStream.peek() == Tokens.COLON){ - pseudoPage = this._pseudo_page(); - } - - this._readWhitespace(); - - this.fire({ - type: "startpage", - id: identifier, - pseudo: pseudoPage, - line: line, - col: col - }); - - this._readDeclarations(true, true); - - this.fire({ - type: "endpage", - id: identifier, - pseudo: pseudoPage, - line: line, - col: col - }); - - }, - - //CSS3 Paged Media - _margin: function(){ - /* - * margin : - * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S* - * ; - */ - var tokenStream = this._tokenStream, - line, - col, - marginSym = this._margin_sym(); - - if (marginSym){ - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - - this.fire({ - type: "startpagemargin", - margin: marginSym, - line: line, - col: col - }); - - this._readDeclarations(true); - - this.fire({ - type: "endpagemargin", - margin: marginSym, - line: line, - col: col - }); - return true; - } else { - return false; - } - }, - - //CSS3 Paged Media - _margin_sym: function(){ - - /* - * margin_sym : - * TOPLEFTCORNER_SYM | - * TOPLEFT_SYM | - * TOPCENTER_SYM | - * TOPRIGHT_SYM | - * TOPRIGHTCORNER_SYM | - * BOTTOMLEFTCORNER_SYM | - * BOTTOMLEFT_SYM | - * BOTTOMCENTER_SYM | - * BOTTOMRIGHT_SYM | - * BOTTOMRIGHTCORNER_SYM | - * LEFTTOP_SYM | - * LEFTMIDDLE_SYM | - * LEFTBOTTOM_SYM | - * RIGHTTOP_SYM | - * RIGHTMIDDLE_SYM | - * RIGHTBOTTOM_SYM - * ; - */ - - var tokenStream = this._tokenStream; - - if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM, - Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM, - Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM, - Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM, - Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM, - Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM, - Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) - { - return SyntaxUnit.fromToken(tokenStream.token()); - } else { - return null; - } - - }, - - _pseudo_page: function(){ - /* - * pseudo_page - * : ':' IDENT - * ; - */ - - var tokenStream = this._tokenStream; - - tokenStream.mustMatch(Tokens.COLON); - tokenStream.mustMatch(Tokens.IDENT); - - //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed - - return tokenStream.token().value; - }, - - _font_face: function(){ - /* - * font_face - * : FONT_FACE_SYM S* - * '{' S* declaration [ ';' S* declaration ]* '}' S* - * ; - */ - var tokenStream = this._tokenStream, - line, - col; - - //look for @page - tokenStream.mustMatch(Tokens.FONT_FACE_SYM); - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - - this._readWhitespace(); - - this.fire({ - type: "startfontface", - line: line, - col: col - }); - - this._readDeclarations(true); - - this.fire({ - type: "endfontface", - line: line, - col: col - }); - }, - - _viewport: function(){ - /* - * viewport - * : VIEWPORT_SYM S* - * '{' S* declaration? [ ';' S* declaration? ]* '}' S* - * ; - */ - var tokenStream = this._tokenStream, - line, - col; - - tokenStream.mustMatch(Tokens.VIEWPORT_SYM); - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - - this._readWhitespace(); - - this.fire({ - type: "startviewport", - line: line, - col: col - }); - - this._readDeclarations(true); - - this.fire({ - type: "endviewport", - line: line, - col: col - }); - - }, - - _operator: function(inFunction){ - - /* - * operator (outside function) - * : '/' S* | ',' S* | /( empty )/ - * operator (inside function) - * : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/ - * ; - */ - - var tokenStream = this._tokenStream, - token = null; - - if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) || - (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))){ - token = tokenStream.token(); - this._readWhitespace(); - } - return token ? PropertyValuePart.fromToken(token) : null; - - }, - - _combinator: function(){ - - /* - * combinator - * : PLUS S* | GREATER S* | TILDE S* | S+ - * ; - */ - - var tokenStream = this._tokenStream, - value = null, - token; - - if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){ - token = tokenStream.token(); - value = new Combinator(token.value, token.startLine, token.startCol); - this._readWhitespace(); - } - - return value; - }, - - _unary_operator: function(){ - - /* - * unary_operator - * : '-' | '+' - * ; - */ - - var tokenStream = this._tokenStream; - - if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])){ - return tokenStream.token().value; - } else { - return null; - } - }, - - _property: function(){ - - /* - * property - * : IDENT S* - * ; - */ - - var tokenStream = this._tokenStream, - value = null, - hack = null, - tokenValue, - token, - line, - col; - - //check for star hack - throws error if not allowed - if (tokenStream.peek() == Tokens.STAR && this.options.starHack){ - tokenStream.get(); - token = tokenStream.token(); - hack = token.value; - line = token.startLine; - col = token.startCol; - } - - if(tokenStream.match(Tokens.IDENT)){ - token = tokenStream.token(); - tokenValue = token.value; - - //check for underscore hack - no error if not allowed because it's valid CSS syntax - if (tokenValue.charAt(0) == "_" && this.options.underscoreHack){ - hack = "_"; - tokenValue = tokenValue.substring(1); - } - - value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol)); - this._readWhitespace(); - } - - return value; - }, - - //Augmented with CSS3 Selectors - _ruleset: function(){ - /* - * ruleset - * : selectors_group - * '{' S* declaration? [ ';' S* declaration? ]* '}' S* - * ; - */ - - var tokenStream = this._tokenStream, - tt, - selectors; - - - /* - * Error Recovery: If even a single selector fails to parse, - * then the entire ruleset should be thrown away. - */ - try { - selectors = this._selectors_group(); - } catch (ex){ - if (ex instanceof SyntaxError && !this.options.strict){ - - //fire error event - this.fire({ - type: "error", - error: ex, - message: ex.message, - line: ex.line, - col: ex.col - }); - - //skip over everything until closing brace - tt = tokenStream.advance([Tokens.RBRACE]); - if (tt == Tokens.RBRACE){ - //if there's a right brace, the rule is finished so don't do anything - } else { - //otherwise, rethrow the error because it wasn't handled properly - throw ex; - } - - } else { - //not a syntax error, rethrow it - throw ex; - } - - //trigger parser to continue - return true; - } - - //if it got here, all selectors parsed - if (selectors){ - - this.fire({ - type: "startrule", - selectors: selectors, - line: selectors[0].line, - col: selectors[0].col - }); - - this._readDeclarations(true); - - this.fire({ - type: "endrule", - selectors: selectors, - line: selectors[0].line, - col: selectors[0].col - }); - - } - - return selectors; - - }, - - //CSS3 Selectors - _selectors_group: function(){ - - /* - * selectors_group - * : selector [ COMMA S* selector ]* - * ; - */ - var tokenStream = this._tokenStream, - selectors = [], - selector; - - selector = this._selector(); - if (selector !== null){ - - selectors.push(selector); - while(tokenStream.match(Tokens.COMMA)){ - this._readWhitespace(); - selector = this._selector(); - if (selector !== null){ - selectors.push(selector); - } else { - this._unexpectedToken(tokenStream.LT(1)); - } - } - } - - return selectors.length ? selectors : null; - }, - - //CSS3 Selectors - _selector: function(){ - /* - * selector - * : simple_selector_sequence [ combinator simple_selector_sequence ]* - * ; - */ - - var tokenStream = this._tokenStream, - selector = [], - nextSelector = null, - combinator = null, - ws = null; - - //if there's no simple selector, then there's no selector - nextSelector = this._simple_selector_sequence(); - if (nextSelector === null){ - return null; - } - - selector.push(nextSelector); - - do { - - //look for a combinator - combinator = this._combinator(); - - if (combinator !== null){ - selector.push(combinator); - nextSelector = this._simple_selector_sequence(); - - //there must be a next selector - if (nextSelector === null){ - this._unexpectedToken(tokenStream.LT(1)); - } else { - - //nextSelector is an instance of SelectorPart - selector.push(nextSelector); - } - } else { - - //if there's not whitespace, we're done - if (this._readWhitespace()){ - - //add whitespace separator - ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol); - - //combinator is not required - combinator = this._combinator(); - - //selector is required if there's a combinator - nextSelector = this._simple_selector_sequence(); - if (nextSelector === null){ - if (combinator !== null){ - this._unexpectedToken(tokenStream.LT(1)); - } - } else { - - if (combinator !== null){ - selector.push(combinator); - } else { - selector.push(ws); - } - - selector.push(nextSelector); - } - } else { - break; - } - - } - } while(true); - - return new Selector(selector, selector[0].line, selector[0].col); - }, - - //CSS3 Selectors - _simple_selector_sequence: function(){ - /* - * simple_selector_sequence - * : [ type_selector | universal ] - * [ HASH | class | attrib | pseudo | negation ]* - * | [ HASH | class | attrib | pseudo | negation ]+ - * ; - */ - - var tokenStream = this._tokenStream, - - //parts of a simple selector - elementName = null, - modifiers = [], - - //complete selector text - selectorText= "", - - //the different parts after the element name to search for - components = [ - //HASH - function(){ - return tokenStream.match(Tokens.HASH) ? - new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) : - null; - }, - this._class, - this._attrib, - this._pseudo, - this._negation - ], - i = 0, - len = components.length, - component = null, - found = false, - line, - col; - - - //get starting line and column for the selector - line = tokenStream.LT(1).startLine; - col = tokenStream.LT(1).startCol; - - elementName = this._type_selector(); - if (!elementName){ - elementName = this._universal(); - } - - if (elementName !== null){ - selectorText += elementName; - } - - while(true){ - - //whitespace means we're done - if (tokenStream.peek() === Tokens.S){ - break; - } - - //check for each component - while(i < len && component === null){ - component = components[i++].call(this); - } - - if (component === null){ - - //we don't have a selector - if (selectorText === ""){ - return null; - } else { - break; - } - } else { - i = 0; - modifiers.push(component); - selectorText += component.toString(); - component = null; - } - } - - - return selectorText !== "" ? - new SelectorPart(elementName, modifiers, selectorText, line, col) : - null; - }, - - //CSS3 Selectors - _type_selector: function(){ - /* - * type_selector - * : [ namespace_prefix ]? element_name - * ; - */ - - var tokenStream = this._tokenStream, - ns = this._namespace_prefix(), - elementName = this._element_name(); - - if (!elementName){ - /* - * Need to back out the namespace that was read due to both - * type_selector and universal reading namespace_prefix - * first. Kind of hacky, but only way I can figure out - * right now how to not change the grammar. - */ - if (ns){ - tokenStream.unget(); - if (ns.length > 1){ - tokenStream.unget(); - } - } - - return null; - } else { - if (ns){ - elementName.text = ns + elementName.text; - elementName.col -= ns.length; - } - return elementName; - } - }, - - //CSS3 Selectors - _class: function(){ - /* - * class - * : '.' IDENT - * ; - */ - - var tokenStream = this._tokenStream, - token; - - if (tokenStream.match(Tokens.DOT)){ - tokenStream.mustMatch(Tokens.IDENT); - token = tokenStream.token(); - return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1); - } else { - return null; - } - - }, - - //CSS3 Selectors - _element_name: function(){ - /* - * element_name - * : IDENT - * ; - */ - - var tokenStream = this._tokenStream, - token; - - if (tokenStream.match(Tokens.IDENT)){ - token = tokenStream.token(); - return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol); - - } else { - return null; - } - }, - - //CSS3 Selectors - _namespace_prefix: function(){ - /* - * namespace_prefix - * : [ IDENT | '*' ]? '|' - * ; - */ - var tokenStream = this._tokenStream, - value = ""; - - //verify that this is a namespace prefix - if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE){ - - if(tokenStream.match([Tokens.IDENT, Tokens.STAR])){ - value += tokenStream.token().value; - } - - tokenStream.mustMatch(Tokens.PIPE); - value += "|"; - - } - - return value.length ? value : null; - }, - - //CSS3 Selectors - _universal: function(){ - /* - * universal - * : [ namespace_prefix ]? '*' - * ; - */ - var tokenStream = this._tokenStream, - value = "", - ns; - - ns = this._namespace_prefix(); - if(ns){ - value += ns; - } - - if(tokenStream.match(Tokens.STAR)){ - value += "*"; - } - - return value.length ? value : null; - - }, - - //CSS3 Selectors - _attrib: function(){ - /* - * attrib - * : '[' S* [ namespace_prefix ]? IDENT S* - * [ [ PREFIXMATCH | - * SUFFIXMATCH | - * SUBSTRINGMATCH | - * '=' | - * INCLUDES | - * DASHMATCH ] S* [ IDENT | STRING ] S* - * ]? ']' - * ; - */ - - var tokenStream = this._tokenStream, - value = null, - ns, - token; - - if (tokenStream.match(Tokens.LBRACKET)){ - token = tokenStream.token(); - value = token.value; - value += this._readWhitespace(); - - ns = this._namespace_prefix(); - - if (ns){ - value += ns; - } - - tokenStream.mustMatch(Tokens.IDENT); - value += tokenStream.token().value; - value += this._readWhitespace(); - - if(tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH, - Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])){ - - value += tokenStream.token().value; - value += this._readWhitespace(); - - tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]); - value += tokenStream.token().value; - value += this._readWhitespace(); - } - - tokenStream.mustMatch(Tokens.RBRACKET); - - return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol); - } else { - return null; - } - }, - - //CSS3 Selectors - _pseudo: function(){ - - /* - * pseudo - * : ':' ':'? [ IDENT | functional_pseudo ] - * ; - */ - - var tokenStream = this._tokenStream, - pseudo = null, - colons = ":", - line, - col; - - if (tokenStream.match(Tokens.COLON)){ - - if (tokenStream.match(Tokens.COLON)){ - colons += ":"; - } - - if (tokenStream.match(Tokens.IDENT)){ - pseudo = tokenStream.token().value; - line = tokenStream.token().startLine; - col = tokenStream.token().startCol - colons.length; - } else if (tokenStream.peek() == Tokens.FUNCTION){ - line = tokenStream.LT(1).startLine; - col = tokenStream.LT(1).startCol - colons.length; - pseudo = this._functional_pseudo(); - } - - if (pseudo){ - pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col); - } - } - - return pseudo; - }, - - //CSS3 Selectors - _functional_pseudo: function(){ - /* - * functional_pseudo - * : FUNCTION S* expression ')' - * ; - */ - - var tokenStream = this._tokenStream, - value = null; - - if(tokenStream.match(Tokens.FUNCTION)){ - value = tokenStream.token().value; - value += this._readWhitespace(); - value += this._expression(); - tokenStream.mustMatch(Tokens.RPAREN); - value += ")"; - } - - return value; - }, - - //CSS3 Selectors - _expression: function(){ - /* - * expression - * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ - * ; - */ - - var tokenStream = this._tokenStream, - value = ""; - - while(tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION, - Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH, - Tokens.FREQ, Tokens.ANGLE, Tokens.TIME, - Tokens.RESOLUTION, Tokens.SLASH])){ - - value += tokenStream.token().value; - value += this._readWhitespace(); - } - - return value.length ? value : null; - - }, - - //CSS3 Selectors - _negation: function(){ - /* - * negation - * : NOT S* negation_arg S* ')' - * ; - */ - - var tokenStream = this._tokenStream, - line, - col, - value = "", - arg, - subpart = null; - - if (tokenStream.match(Tokens.NOT)){ - value = tokenStream.token().value; - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - value += this._readWhitespace(); - arg = this._negation_arg(); - value += arg; - value += this._readWhitespace(); - tokenStream.match(Tokens.RPAREN); - value += tokenStream.token().value; - - subpart = new SelectorSubPart(value, "not", line, col); - subpart.args.push(arg); - } - - return subpart; - }, - - //CSS3 Selectors - _negation_arg: function(){ - /* - * negation_arg - * : type_selector | universal | HASH | class | attrib | pseudo - * ; - */ - - var tokenStream = this._tokenStream, - args = [ - this._type_selector, - this._universal, - function(){ - return tokenStream.match(Tokens.HASH) ? - new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) : - null; - }, - this._class, - this._attrib, - this._pseudo - ], - arg = null, - i = 0, - len = args.length, - elementName, - line, - col, - part; - - line = tokenStream.LT(1).startLine; - col = tokenStream.LT(1).startCol; - - while(i < len && arg === null){ - - arg = args[i].call(this); - i++; - } - - //must be a negation arg - if (arg === null){ - this._unexpectedToken(tokenStream.LT(1)); - } - - //it's an element name - if (arg.type == "elementName"){ - part = new SelectorPart(arg, [], arg.toString(), line, col); - } else { - part = new SelectorPart(null, [arg], arg.toString(), line, col); - } - - return part; - }, - - _declaration: function(){ - - /* - * declaration - * : property ':' S* expr prio? - * | /( empty )/ - * ; - */ - - var tokenStream = this._tokenStream, - property = null, - expr = null, - prio = null, - error = null, - invalid = null, - propertyName= ""; - - property = this._property(); - if (property !== null){ - - tokenStream.mustMatch(Tokens.COLON); - this._readWhitespace(); - - expr = this._expr(); - - //if there's no parts for the value, it's an error - if (!expr || expr.length === 0){ - this._unexpectedToken(tokenStream.LT(1)); - } - - prio = this._prio(); - - /* - * If hacks should be allowed, then only check the root - * property. If hacks should not be allowed, treat - * _property or *property as invalid properties. - */ - propertyName = property.toString(); - if (this.options.starHack && property.hack == "*" || - this.options.underscoreHack && property.hack == "_") { - - propertyName = property.text; - } - - try { - this._validateProperty(propertyName, expr); - } catch (ex) { - invalid = ex; - } - - this.fire({ - type: "property", - property: property, - value: expr, - important: prio, - line: property.line, - col: property.col, - invalid: invalid - }); - - return true; - } else { - return false; - } - }, - - _prio: function(){ - /* - * prio - * : IMPORTANT_SYM S* - * ; - */ - - var tokenStream = this._tokenStream, - result = tokenStream.match(Tokens.IMPORTANT_SYM); - - this._readWhitespace(); - return result; - }, - - _expr: function(inFunction){ - /* - * expr - * : term [ operator term ]* - * ; - */ - - var tokenStream = this._tokenStream, - values = [], - //valueParts = [], - value = null, - operator = null; - - value = this._term(); - if (value !== null){ - - values.push(value); - - do { - operator = this._operator(inFunction); - - //if there's an operator, keep building up the value parts - if (operator){ - values.push(operator); - } /*else { - //if there's not an operator, you have a full value - values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); - valueParts = []; - }*/ - - value = this._term(); - - if (value === null){ - break; - } else { - values.push(value); - } - } while(true); - } - - //cleanup - /*if (valueParts.length){ - values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); - }*/ - - return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null; - }, - - _term: function(){ - - /* - * term - * : unary_operator? - * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* | - * TIME S* | FREQ S* | function | ie_function ] - * | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor - * ; - */ - - var tokenStream = this._tokenStream, - unary = null, - value = null, - token, - line, - col; - - //returns the operator or null - unary = this._unary_operator(); - if (unary !== null){ - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - } - - //exception for IE filters - if (tokenStream.peek() == Tokens.IE_FUNCTION && this.options.ieFilters){ - - value = this._ie_function(); - if (unary === null){ - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - } - - //see if there's a simple match - } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH, - Tokens.ANGLE, Tokens.TIME, - Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])){ - - value = tokenStream.token().value; - if (unary === null){ - line = tokenStream.token().startLine; - col = tokenStream.token().startCol; - } - this._readWhitespace(); - } else { - - //see if it's a color - token = this._hexcolor(); - if (token === null){ - - //if there's no unary, get the start of the next token for line/col info - if (unary === null){ - line = tokenStream.LT(1).startLine; - col = tokenStream.LT(1).startCol; - } - - //has to be a function - if (value === null){ - - /* - * This checks for alpha(opacity=0) style of IE - * functions. IE_FUNCTION only presents progid: style. - */ - if (tokenStream.LA(3) == Tokens.EQUALS && this.options.ieFilters){ - value = this._ie_function(); - } else { - value = this._function(); - } - } - - /*if (value === null){ - return null; - //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + "."); - }*/ - - } else { - value = token.value; - if (unary === null){ - line = token.startLine; - col = token.startCol; - } - } - - } - - return value !== null ? - new PropertyValuePart(unary !== null ? unary + value : value, line, col) : - null; - - }, - - _function: function(){ - - /* - * function - * : FUNCTION S* expr ')' S* - * ; - */ - - var tokenStream = this._tokenStream, - functionText = null, - expr = null, - lt; - - if (tokenStream.match(Tokens.FUNCTION)){ - functionText = tokenStream.token().value; - this._readWhitespace(); - expr = this._expr(true); - functionText += expr; - - //START: Horrible hack in case it's an IE filter - if (this.options.ieFilters && tokenStream.peek() == Tokens.EQUALS){ - do { - - if (this._readWhitespace()){ - functionText += tokenStream.token().value; - } - - //might be second time in the loop - if (tokenStream.LA(0) == Tokens.COMMA){ - functionText += tokenStream.token().value; - } - - tokenStream.match(Tokens.IDENT); - functionText += tokenStream.token().value; - - tokenStream.match(Tokens.EQUALS); - functionText += tokenStream.token().value; - - //functionText += this._term(); - lt = tokenStream.peek(); - while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){ - tokenStream.get(); - functionText += tokenStream.token().value; - lt = tokenStream.peek(); - } - } while(tokenStream.match([Tokens.COMMA, Tokens.S])); - } - - //END: Horrible Hack - - tokenStream.match(Tokens.RPAREN); - functionText += ")"; - this._readWhitespace(); - } - - return functionText; - }, - - _ie_function: function(){ - - /* (My own extension) - * ie_function - * : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S* - * ; - */ - - var tokenStream = this._tokenStream, - functionText = null, - expr = null, - lt; - - //IE function can begin like a regular function, too - if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])){ - functionText = tokenStream.token().value; - - do { - - if (this._readWhitespace()){ - functionText += tokenStream.token().value; - } - - //might be second time in the loop - if (tokenStream.LA(0) == Tokens.COMMA){ - functionText += tokenStream.token().value; - } - - tokenStream.match(Tokens.IDENT); - functionText += tokenStream.token().value; - - tokenStream.match(Tokens.EQUALS); - functionText += tokenStream.token().value; - - //functionText += this._term(); - lt = tokenStream.peek(); - while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){ - tokenStream.get(); - functionText += tokenStream.token().value; - lt = tokenStream.peek(); - } - } while(tokenStream.match([Tokens.COMMA, Tokens.S])); - - tokenStream.match(Tokens.RPAREN); - functionText += ")"; - this._readWhitespace(); - } - - return functionText; - }, - - _hexcolor: function(){ - /* - * There is a constraint on the color that it must - * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) - * after the "#"; e.g., "#000" is OK, but "#abcd" is not. - * - * hexcolor - * : HASH S* - * ; - */ - - var tokenStream = this._tokenStream, - token = null, - color; - - if(tokenStream.match(Tokens.HASH)){ - - //need to do some validation here - - token = tokenStream.token(); - color = token.value; - if (!/#[a-f0-9]{3,6}/i.test(color)){ - throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol); - } - this._readWhitespace(); - } - - return token; - }, - - //----------------------------------------------------------------- - // Animations methods - //----------------------------------------------------------------- - - _keyframes: function(){ - - /* - * keyframes: - * : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' { - * ; - */ - var tokenStream = this._tokenStream, - token, - tt, - name, - prefix = ""; - - tokenStream.mustMatch(Tokens.KEYFRAMES_SYM); - token = tokenStream.token(); - if (/^@\-([^\-]+)\-/.test(token.value)) { - prefix = RegExp.$1; - } - - this._readWhitespace(); - name = this._keyframe_name(); - - this._readWhitespace(); - tokenStream.mustMatch(Tokens.LBRACE); - - this.fire({ - type: "startkeyframes", - name: name, - prefix: prefix, - line: token.startLine, - col: token.startCol - }); - - this._readWhitespace(); - tt = tokenStream.peek(); - - //check for key - while(tt == Tokens.IDENT || tt == Tokens.PERCENTAGE) { - this._keyframe_rule(); - this._readWhitespace(); - tt = tokenStream.peek(); - } - - this.fire({ - type: "endkeyframes", - name: name, - prefix: prefix, - line: token.startLine, - col: token.startCol - }); - - this._readWhitespace(); - tokenStream.mustMatch(Tokens.RBRACE); - - }, - - _keyframe_name: function(){ - - /* - * keyframe_name: - * : IDENT - * | STRING - * ; - */ - var tokenStream = this._tokenStream, - token; - - tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]); - return SyntaxUnit.fromToken(tokenStream.token()); - }, - - _keyframe_rule: function(){ - - /* - * keyframe_rule: - * : key_list S* - * '{' S* declaration [ ';' S* declaration ]* '}' S* - * ; - */ - var tokenStream = this._tokenStream, - token, - keyList = this._key_list(); - - this.fire({ - type: "startkeyframerule", - keys: keyList, - line: keyList[0].line, - col: keyList[0].col - }); - - this._readDeclarations(true); - - this.fire({ - type: "endkeyframerule", - keys: keyList, - line: keyList[0].line, - col: keyList[0].col - }); - - }, - - _key_list: function(){ - - /* - * key_list: - * : key [ S* ',' S* key]* - * ; - */ - var tokenStream = this._tokenStream, - token, - key, - keyList = []; - - //must be least one key - keyList.push(this._key()); - - this._readWhitespace(); - - while(tokenStream.match(Tokens.COMMA)){ - this._readWhitespace(); - keyList.push(this._key()); - this._readWhitespace(); - } - - return keyList; - }, - - _key: function(){ - /* - * There is a restriction that IDENT can be only "from" or "to". - * - * key - * : PERCENTAGE - * | IDENT - * ; - */ - - var tokenStream = this._tokenStream, - token; - - if (tokenStream.match(Tokens.PERCENTAGE)){ - return SyntaxUnit.fromToken(tokenStream.token()); - } else if (tokenStream.match(Tokens.IDENT)){ - token = tokenStream.token(); - - if (/from|to/i.test(token.value)){ - return SyntaxUnit.fromToken(token); - } - - tokenStream.unget(); - } - - //if it gets here, there wasn't a valid token, so time to explode - this._unexpectedToken(tokenStream.LT(1)); - }, - - //----------------------------------------------------------------- - // Helper methods - //----------------------------------------------------------------- - - /** - * Not part of CSS grammar, but useful for skipping over - * combination of white space and HTML-style comments. - * @return {void} - * @method _skipCruft - * @private - */ - _skipCruft: function(){ - while(this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])){ - //noop - } - }, - - /** - * Not part of CSS grammar, but this pattern occurs frequently - * in the official CSS grammar. Split out here to eliminate - * duplicate code. - * @param {Boolean} checkStart Indicates if the rule should check - * for the left brace at the beginning. - * @param {Boolean} readMargins Indicates if the rule should check - * for margin patterns. - * @return {void} - * @method _readDeclarations - * @private - */ - _readDeclarations: function(checkStart, readMargins){ - /* - * Reads the pattern - * S* '{' S* declaration [ ';' S* declaration ]* '}' S* - * or - * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S* - * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect. - * A semicolon is only necessary following a declaration is there's another declaration - * or margin afterwards. - */ - var tokenStream = this._tokenStream, - tt; - - - this._readWhitespace(); - - if (checkStart){ - tokenStream.mustMatch(Tokens.LBRACE); - } - - this._readWhitespace(); - - try { - - while(true){ - - if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())){ - //noop - } else if (this._declaration()){ - if (!tokenStream.match(Tokens.SEMICOLON)){ - break; - } - } else { - break; - } - - //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){ - // break; - //} - this._readWhitespace(); - } - - tokenStream.mustMatch(Tokens.RBRACE); - this._readWhitespace(); - - } catch (ex) { - if (ex instanceof SyntaxError && !this.options.strict){ - - //fire error event - this.fire({ - type: "error", - error: ex, - message: ex.message, - line: ex.line, - col: ex.col - }); - - //see if there's another declaration - tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]); - if (tt == Tokens.SEMICOLON){ - //if there's a semicolon, then there might be another declaration - this._readDeclarations(false, readMargins); - } else if (tt != Tokens.RBRACE){ - //if there's a right brace, the rule is finished so don't do anything - //otherwise, rethrow the error because it wasn't handled properly - throw ex; - } - - } else { - //not a syntax error, rethrow it - throw ex; - } - } - - }, - - /** - * In some cases, you can end up with two white space tokens in a - * row. Instead of making a change in every function that looks for - * white space, this function is used to match as much white space - * as necessary. - * @method _readWhitespace - * @return {String} The white space if found, empty string if not. - * @private - */ - _readWhitespace: function(){ - - var tokenStream = this._tokenStream, - ws = ""; - - while(tokenStream.match(Tokens.S)){ - ws += tokenStream.token().value; - } - - return ws; - }, - - - /** - * Throws an error when an unexpected token is found. - * @param {Object} token The token that was found. - * @method _unexpectedToken - * @return {void} - * @private - */ - _unexpectedToken: function(token){ - throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol); - }, - - /** - * Helper method used for parsing subparts of a style sheet. - * @return {void} - * @method _verifyEnd - * @private - */ - _verifyEnd: function(){ - if (this._tokenStream.LA(1) != Tokens.EOF){ - this._unexpectedToken(this._tokenStream.LT(1)); - } - }, - - //----------------------------------------------------------------- - // Validation methods - //----------------------------------------------------------------- - _validateProperty: function(property, value){ - Validation.validate(property, value); - }, - - //----------------------------------------------------------------- - // Parsing methods - //----------------------------------------------------------------- - - parse: function(input){ - this._tokenStream = new TokenStream(input, Tokens); - this._stylesheet(); - }, - - parseStyleSheet: function(input){ - //just passthrough - return this.parse(input); - }, - - parseMediaQuery: function(input){ - this._tokenStream = new TokenStream(input, Tokens); - var result = this._media_query(); - - //if there's anything more, then it's an invalid selector - this._verifyEnd(); - - //otherwise return result - return result; - }, - - /** - * Parses a property value (everything after the semicolon). - * @return {parserlib.css.PropertyValue} The property value. - * @throws parserlib.util.SyntaxError If an unexpected token is found. - * @method parserPropertyValue - */ - parsePropertyValue: function(input){ - - this._tokenStream = new TokenStream(input, Tokens); - this._readWhitespace(); - - var result = this._expr(); - - //okay to have a trailing white space - this._readWhitespace(); - - //if there's anything more, then it's an invalid selector - this._verifyEnd(); - - //otherwise return result - return result; - }, - - /** - * Parses a complete CSS rule, including selectors and - * properties. - * @param {String} input The text to parser. - * @return {Boolean} True if the parse completed successfully, false if not. - * @method parseRule - */ - parseRule: function(input){ - this._tokenStream = new TokenStream(input, Tokens); - - //skip any leading white space - this._readWhitespace(); - - var result = this._ruleset(); - - //skip any trailing white space - this._readWhitespace(); - - //if there's anything more, then it's an invalid selector - this._verifyEnd(); - - //otherwise return result - return result; - }, - - /** - * Parses a single CSS selector (no comma) - * @param {String} input The text to parse as a CSS selector. - * @return {Selector} An object representing the selector. - * @throws parserlib.util.SyntaxError If an unexpected token is found. - * @method parseSelector - */ - parseSelector: function(input){ - - this._tokenStream = new TokenStream(input, Tokens); - - //skip any leading white space - this._readWhitespace(); - - var result = this._selector(); - - //skip any trailing white space - this._readWhitespace(); - - //if there's anything more, then it's an invalid selector - this._verifyEnd(); - - //otherwise return result - return result; - }, - - /** - * Parses an HTML style attribute: a set of CSS declarations - * separated by semicolons. - * @param {String} input The text to parse as a style attribute - * @return {void} - * @method parseStyleAttribute - */ - parseStyleAttribute: function(input){ - input += "}"; // for error recovery in _readDeclarations() - this._tokenStream = new TokenStream(input, Tokens); - this._readDeclarations(); - } - }; - - //copy over onto prototype - for (prop in additions){ - if (additions.hasOwnProperty(prop)){ - proto[prop] = additions[prop]; - } - } - - return proto; -}(); - - -/* -nth - : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? | - ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S* - ; -*/ - -/*global Validation, ValidationTypes, ValidationError*/ -var Properties = { - - //A - "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ", - "alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", - "animation" : 1, - "animation-delay" : { multi: "