├── .gitignore ├── Guardfile ├── app ├── static │ ├── favicon.ico │ ├── apple-touch-icon.png │ ├── apple-touch-icon-precomposed.png │ ├── images │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ ├── ui-icons_cd0a0a_256x240.png │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ ├── apple-touch-icon-57x57-precomposed.png │ ├── apple-touch-icon-72x72-precomposed.png │ └── apple-touch-icon-114x114-precomposed.png ├── plugins │ └── loader.js ├── lib │ ├── app.js │ └── ember-jquery-ui.js └── css │ ├── style.css │ └── jquery-ui.css ├── .travis.yml ├── Gemfile ├── config.ru ├── LICENSE ├── README.md ├── tests ├── index.html └── qunit │ ├── run-qunit.js │ ├── qunit.css │ └── qunit.js ├── Rakefile ├── Gemfile.lock ├── index.html └── Assetfile /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | tmp/ 3 | assets/ 4 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rake, :task => :test do 2 | watch(%r{^app/.+\.js$}) 3 | end 4 | -------------------------------------------------------------------------------- /app/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/favicon.ico -------------------------------------------------------------------------------- /app/static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/apple-touch-icon.png -------------------------------------------------------------------------------- /app/static/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /app/static/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /app/static/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /app/static/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /app/static/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /app/static/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /app/static/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/apple-touch-icon-57x57-precomposed.png -------------------------------------------------------------------------------- /app/static/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /app/static/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /app/static/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangratz/ember-jquery-ui/master/app/static/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 1.9.3 3 | bundler_args: --without development 4 | before_script: 5 | - "export DISPLAY=:99.0" 6 | - "sh -e /etc/init.d/xvfb start" 7 | script: "bundle exec rake test" -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gem 'colored' 4 | 5 | gem 'guard' 6 | gem 'guard-rake' 7 | 8 | gem 'kicker' 9 | 10 | gem 'rack' 11 | gem 'rack-rewrite' 12 | # gem 'rack-streaming-proxy' 13 | 14 | gem 'sass' 15 | gem 'compass' 16 | 17 | gem 'uglifier' 18 | gem 'yui-compressor' 19 | 20 | gem 'rake-pipeline', :git => 'https://github.com/livingsocial/rake-pipeline.git' 21 | gem 'rake-pipeline-web-filters', :git => 'https://github.com/wycats/rake-pipeline-web-filters.git' 22 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'rake-pipeline' 2 | require 'rake-pipeline/middleware' 3 | use Rake::Pipeline::Middleware, 'Assetfile' 4 | 5 | # require 'rack/streaming_proxy' 6 | # use Rack::StreamingProxy do |request| 7 | # if request.path.start_with?('/proxy') 8 | # "http://127.0.0.1:8080#{request.path}" 9 | # end 10 | # end 11 | 12 | require 'rack-rewrite' 13 | use Rack::Rewrite do 14 | rewrite %r{^(.*)\/$}, '$1/index.html' 15 | end 16 | 17 | run Rack::Directory.new('.') 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Clemens Müller , 2012 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ember-jQuery-UI [![Build Status](https://secure.travis-ci.org/pangratz/ember-jquery-ui.png)](http://travis-ci.org/pangratz/ember-jquery-ui) 2 | ============== 3 | 4 | Use jQuery UI widgets inside Ember.js. 5 | 6 | 7 | Thanks 8 | ------ 9 | 10 | * This project is based on the excellent work by [Luke Melia](https://github.com/lukemelia) on the [jquery-ui-ember](https://github.com/lukemelia/jquery-ui-ember) repository. 11 | 12 | * This project's layout is based on the fabulous [interline/ember-skelton](https://github.com/interline/ember-skeleton) 13 | 14 | 15 | Usage 16 | ----- 17 | 18 | The widgets are located in [app/lib/ember-jquery-ui.js](https://github.com/pangratz/ember-jquery-ui/blob/master/app/lib/ember-jquery-ui.js). Include this file along with jQuery UI JavaScript and CSS and you're good to go. 19 | 20 | A basic example is hosted at [http://pangratz.github.com/ember-jquery-ui](http://pangratz.github.com/ember-jquery-ui). 21 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | QUnit Test Suite 6 | 7 | 8 | 9 | 10 |
11 |
test markup
12 | 13 | 14 | 15 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | APPNAME = 'ember-jquery-ui' 2 | 3 | require 'colored' 4 | require 'rake-pipeline' 5 | 6 | desc "Build #{APPNAME}" 7 | task :build do 8 | Rake::Pipeline::Project.new('Assetfile').invoke 9 | end 10 | 11 | desc "Run tests with PhantomJS" 12 | task :test => :build do 13 | unless system("which phantomjs > /dev/null 2>&1") 14 | abort "PhantomJS is not installed. Download from http://phantomjs.org/" 15 | end 16 | 17 | cmd = "phantomjs tests/qunit/run-qunit.js \"file://#{File.dirname(__FILE__)}/tests/index.html\"" 18 | 19 | # Run the tests 20 | puts "Running #{APPNAME} tests" 21 | success = system(cmd) 22 | 23 | if success 24 | puts "Tests Passed".green 25 | else 26 | puts "Tests Failed".red 27 | exit(1) 28 | end 29 | end 30 | 31 | desc "Automatically run tests (Mac OS X only)" 32 | task :autotest do 33 | system("kicker -e 'rake test' app") 34 | end 35 | 36 | desc "Deploy app on GitHub pages" 37 | task :deploy do 38 | `rm -rf build` 39 | `mkdir build` 40 | origin = `git config remote.origin.url`.chomp 41 | ENV["RAKEP_MODE"] = "production" 42 | Rake::Task["build"].invoke 43 | Dir.chdir "build" do 44 | `git init` 45 | `git remote add origin #{origin}` 46 | `git checkout -b gh-pages` 47 | `cp ../index.html .` 48 | `cp -r ../assets .` 49 | `rm assets/app-tests.js` 50 | `git add .` 51 | `git commit -m 'Site updated at #{Time.now.utc}'` 52 | `git push -f origin gh-pages` 53 | end 54 | `rm -rf build` 55 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/livingsocial/rake-pipeline.git 3 | revision: b70ca6cad7655e58d13031f3e24df7dfc74f9030 4 | specs: 5 | rake-pipeline (0.6.0) 6 | rake (~> 0.9.0) 7 | thor 8 | 9 | GIT 10 | remote: https://github.com/wycats/rake-pipeline-web-filters.git 11 | revision: ba0b8a00356b4c854930a8e849b5629d51ffd70f 12 | specs: 13 | rake-pipeline-web-filters (0.6.0) 14 | rack 15 | rake-pipeline (~> 0.6) 16 | 17 | GEM 18 | remote: http://rubygems.org/ 19 | specs: 20 | POpen4 (0.1.4) 21 | Platform (>= 0.4.0) 22 | open4 23 | Platform (0.4.0) 24 | chunky_png (1.2.5) 25 | colored (1.2) 26 | compass (0.12.1) 27 | chunky_png (~> 1.2) 28 | fssm (>= 0.2.7) 29 | sass (~> 3.1) 30 | execjs (1.3.0) 31 | multi_json (~> 1.0) 32 | ffi (1.0.11) 33 | fssm (0.2.8.1) 34 | guard (1.0.1) 35 | ffi (>= 0.5.0) 36 | thor (~> 0.14.6) 37 | guard-rake (0.0.5) 38 | guard 39 | rake 40 | kicker (2.5.0) 41 | rb-fsevent 42 | multi_json (1.2.0) 43 | open4 (1.3.0) 44 | rack (1.4.1) 45 | rack-rewrite (1.2.1) 46 | rake (0.9.2.2) 47 | rb-fsevent (0.9.1) 48 | sass (3.1.15) 49 | thor (0.14.6) 50 | uglifier (1.2.4) 51 | execjs (>= 0.3.0) 52 | multi_json (>= 1.0.2) 53 | yui-compressor (0.9.6) 54 | POpen4 (>= 0.1.4) 55 | 56 | PLATFORMS 57 | ruby 58 | 59 | DEPENDENCIES 60 | colored 61 | compass 62 | guard 63 | guard-rake 64 | kicker 65 | rack 66 | rack-rewrite 67 | rake-pipeline! 68 | rake-pipeline-web-filters! 69 | sass 70 | uglifier 71 | yui-compressor 72 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/plugins/loader.js: -------------------------------------------------------------------------------- 1 | (function(window) { 2 | function requireWrapper(self) { 3 | var require = function() { 4 | return self.require.apply(self, arguments); 5 | }; 6 | require.exists = function() { 7 | return self.exists.apply(self, arguments); 8 | }; 9 | return require; 10 | } 11 | 12 | var Context = function() { 13 | return this; 14 | }; 15 | 16 | var Loader = function() { 17 | this.modules = {}; 18 | this.loaded = {}; 19 | this.exports = {}; 20 | return this; 21 | }; 22 | 23 | Loader.prototype.require = function(name) { 24 | if (!this.loaded[name]) { 25 | var module = this.modules[name]; 26 | if (module) { 27 | var require = requireWrapper(this); 28 | try { 29 | this.exports[name] = module.call(new Context(), require); 30 | return this.exports[name]; 31 | } finally { 32 | this.loaded[name] = true; 33 | } 34 | } else { 35 | throw "The module '" + name + "' has not been registered"; 36 | } 37 | } 38 | return this.exports[name]; 39 | }; 40 | 41 | Loader.prototype.register = function(name, module) { 42 | if (this.exists(name)) { 43 | throw "The module '"+ "' has already been registered"; 44 | } 45 | this.modules[name] = module; 46 | return true; 47 | }; 48 | 49 | Loader.prototype.unregister = function(name) { 50 | var loaded = !!this.loaded[name]; 51 | if (loaded) { 52 | delete this.exports[name]; 53 | delete this.modules[name]; 54 | delete this.loaded[name]; 55 | } 56 | return loaded; 57 | }; 58 | 59 | Loader.prototype.exists = function(name) { 60 | return name in this.modules; 61 | }; 62 | 63 | window.loader = new Loader(); 64 | })(this); 65 | -------------------------------------------------------------------------------- /tests/qunit/run-qunit.js: -------------------------------------------------------------------------------- 1 | // PhantomJS QUnit Test Runner 2 | 3 | var args = phantom.args; 4 | if (args.length < 1 || args.length > 2) { 5 | console.log("Usage: " + phantom.scriptName + " "); 6 | phantom.exit(1); 7 | } 8 | 9 | var page = require('webpage').create(); 10 | 11 | var depRe = /^DEPRECATION:/; 12 | page.onConsoleMessage = function(msg) { 13 | if (!depRe.test(msg)) console.log(msg); 14 | }; 15 | 16 | page.open(args[0], function(status) { 17 | if (status !== 'success') { 18 | console.error("Unable to access network"); 19 | phantom.exit(1); 20 | } else { 21 | page.evaluate(addLogging); 22 | 23 | var timeout = parseInt(args[1] || 30000, 10); 24 | var start = Date.now(); 25 | var interval = setInterval(function() { 26 | if (Date.now() > start + timeout) { 27 | console.error("Tests timed out"); 28 | phantom.exit(1); 29 | } else { 30 | var qunitDone = page.evaluate(function() { 31 | return window.qunitDone; 32 | }); 33 | 34 | if (qunitDone) { 35 | clearInterval(interval); 36 | if (qunitDone.failed > 0) { 37 | phantom.exit(1); 38 | } else { 39 | phantom.exit(); 40 | } 41 | } 42 | } 43 | }, 500); 44 | } 45 | }); 46 | 47 | function addLogging() { 48 | var testErrors = []; 49 | var assertionErrors = []; 50 | 51 | QUnit.moduleDone(function(context) { 52 | if (context.failed) { 53 | var msg = "Module Failed: " + context.name + "\n" + testErrors.join("\n"); 54 | console.error(msg); 55 | testErrors = []; 56 | } 57 | }); 58 | 59 | QUnit.testDone(function(context) { 60 | if (context.failed) { 61 | var msg = " Test Failed: " + context.name + assertionErrors.join(" "); 62 | testErrors.push(msg); 63 | assertionErrors = []; 64 | } 65 | }); 66 | 67 | QUnit.log(function(context) { 68 | if (context.result) return; 69 | 70 | var msg = "\n Assertion Failed:"; 71 | if (context.message) { 72 | msg += " " + context.message; 73 | } 74 | 75 | if (context.expected) { 76 | msg += "\n Expected: " + context.expected + ", Actual: " + context.actual; 77 | } 78 | 79 | assertionErrors.push(msg); 80 | }); 81 | 82 | QUnit.done(function(context) { 83 | var stats = [ 84 | "Time: " + context.runtime + "ms", 85 | "Total: " + context.total, 86 | "Passed: " + context.passed, 87 | "Failed: " + context.failed 88 | ]; 89 | console.log(stats.join(", ")); 90 | window.qunitDone = context; 91 | }); 92 | } 93 | -------------------------------------------------------------------------------- /app/lib/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Application written by Luke Melia, @lukemelia, https://github.com/lukemelia 3 | */ 4 | require('jquery'); 5 | require('jquery-ui'); 6 | require('ember'); 7 | 8 | require('ember-jquery-ui/ember-jquery-ui'); 9 | 10 | App = Em.Application.create(); 11 | 12 | // Create a simple controller to hold values that will be shared across 13 | // views. 14 | App.controller = Em.Object.create({ 15 | progress: 0, 16 | menuDisabled: true 17 | }); 18 | 19 | // Create a subclass of `JQ.Button` to define behavior for our button. 20 | App.Button = JQ.Button.extend({ 21 | // When the button is clicked... 22 | click: function() { 23 | // Disable the button. 24 | this.set('disabled', true); 25 | 26 | // Increment the progress bar. 27 | this.increment(); 28 | }, 29 | 30 | increment: function() { 31 | var self = this; 32 | 33 | // Get the current progress value from the controller. 34 | var val = App.controller.get('progress'); 35 | 36 | if(val < 100) { 37 | // If the value is less than 100, increment it. 38 | App.controller.set('progress', val + 1); 39 | 40 | // Schedule another increment call from 30ms. 41 | setTimeout(function() { self.increment(); }, 30); 42 | } 43 | } 44 | }); 45 | 46 | // Create a subclass of `JQ.ProgressBar` to define behavior for our 47 | // progress bar. 48 | App.ProgressBar = JQ.ProgressBar.extend({ 49 | // When the jQuery UI progress bar reaches 100%, it will invoke the 50 | // `complete` event. Recall that JQ.Widget registers a callback for 51 | // the `complete` event in `didInsertElement`, which calls the 52 | // `complete` method. 53 | complete: function() { 54 | // When the progress bar finishes, update App.controller with the 55 | // list of people. Because our template binds the JQ.Menu to this 56 | // value, it will automatically populate with the new people and 57 | // refresh the menu. 58 | App.controller.set('people', [ 59 | Em.Object.create({ 60 | name: "Tom DAAAAALE" 61 | }), 62 | Em.Object.create({ 63 | name: "Yehuda Katz" 64 | }), 65 | Em.Object.create({ 66 | name: "Majd Potatoes" 67 | }) 68 | ]); 69 | 70 | // Set the `menuDisabled` property of our controller to false. 71 | // Because the JQ.Menu binds its `disabled` property to 72 | // App.controller.menuDisabled, this will enable it. 73 | App.controller.set('menuDisabled', false); 74 | } 75 | }); 76 | 77 | /** 78 | Template: 79 | 80 | {{view App.Button label="Click to Load People"}} 81 |

82 | {{view App.ProgressBar valueBinding="App.controller.progress"}} 83 |

84 | {{#collection JQ.Menu 85 | contentBinding="App.controller.people" 86 | disabledBinding="App.controller.menuDisabled"}} 87 | 88 | {{content.name}} 89 | 90 | {{else}} 91 | LIST NOT LOADED 92 | {{/collection}} 93 | */ 94 | -------------------------------------------------------------------------------- /Assetfile: -------------------------------------------------------------------------------- 1 | APPNAME = 'ember-jquery-ui' 2 | 3 | require 'json' 4 | require 'rake-pipeline-web-filters' 5 | 6 | WebFilters = Rake::Pipeline::Web::Filters 7 | 8 | class LoaderFilter < WebFilters::MinispadeFilter 9 | def generate_output(inputs, output) 10 | inputs.each do |input| 11 | code = input.read 12 | module_id = @module_id_generator.call(input) 13 | contents = "function(require) {\n#{code}\n}" 14 | ret = "\nloader.register('#{module_id}', #{contents});\n" 15 | output.write ret 16 | end 17 | end 18 | end 19 | 20 | class EmberAssertFilter < Filter 21 | def generate_output(inputs, output) 22 | inputs.each do |input| 23 | result = input.read 24 | result.gsub!(/ember_assert\((.*)\);/, '') 25 | output.write(result) 26 | end 27 | end 28 | end 29 | 30 | class HandlebarsFilter < Filter 31 | def generate_output(inputs, output) 32 | inputs.each do |input| 33 | code = input.read.to_json 34 | name = File.basename(input.path, '.handlebars') 35 | output.write "\nreturn Ember.Handlebars.compile(#{code});\n" 36 | end 37 | end 38 | end 39 | 40 | output 'assets' 41 | 42 | input 'app' do 43 | match 'lib/**/*.js' do 44 | filter LoaderFilter, 45 | :module_id_generator => proc { |input| 46 | input.path.sub(/^lib\//, "#{APPNAME}/").sub(/\.js$/, '') 47 | } 48 | 49 | if ENV['RAKEP_MODE'] == 'production' 50 | filter EmberAssertFilter 51 | uglify {|input| input} 52 | end 53 | concat 'app.js' 54 | end 55 | 56 | match 'vendor/**/*.js' do 57 | filter LoaderFilter, 58 | :module_id_generator => proc { |input| 59 | input.path.sub(/^vendor\//, '').sub(/\.js$/, '') 60 | } 61 | 62 | if ENV['RAKEP_MODE'] == 'production' 63 | filter EmberAssertFilter 64 | uglify {|input| input} 65 | end 66 | concat %w[ 67 | vendor/jquery.js 68 | vendor/jquery-ui.js 69 | vendor/ember.js 70 | ], 'app.js' 71 | end 72 | 73 | match 'modules/**/*.js' do 74 | if ENV['RAKEP_MODE'] == 'production' 75 | filter EmberAssertFilter 76 | uglify {|input| input} 77 | end 78 | concat 'app.js' 79 | end 80 | 81 | match 'plugins/**/*.js' do 82 | if ENV['RAKEP_MODE'] == 'production' 83 | uglify {|input| input} 84 | end 85 | concat do |input| 86 | input.sub(/plugins\//, '') 87 | end 88 | end 89 | 90 | match 'templates/**/*.handlebars' do 91 | filter HandlebarsFilter 92 | filter LoaderFilter, 93 | :module_id_generator => proc { |input| 94 | input.path.sub(/^templates\//, "#{APPNAME}/~templates/").sub(/\.handlebars$/, '') 95 | } 96 | if ENV['RAKEP_MODE'] == 'production' 97 | uglify {|input| input} 98 | end 99 | concat 'app.js' 100 | end 101 | 102 | match 'tests/**/*.js' do 103 | filter LoaderFilter, 104 | :module_id_generator => proc { |input| 105 | input.path.sub(/^lib\//, "#{APPNAME}/").sub(/\.js$/, '') 106 | } 107 | concat 'app-tests.js' 108 | end 109 | 110 | match 'css/**/*.css' do 111 | if ENV['RAKEP_MODE'] == 'production' 112 | yui_css 113 | end 114 | concat ['jquery-ui.css', 'style.css'], 'app.css' 115 | end 116 | 117 | match 'css/**/*.scss' do 118 | sass 119 | if ENV['RAKEP_MODE'] == 'production' 120 | yui_css 121 | end 122 | concat 'app.css' 123 | end 124 | 125 | match "static/**/*" do 126 | concat do |input| 127 | input.sub(/static\//, '') 128 | end 129 | end 130 | end 131 | 132 | # vim: filetype=ruby 133 | -------------------------------------------------------------------------------- /app/lib/ember-jquery-ui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JQ stuff initially written by Luke Melia, @lukemelia, https://github.com/lukemelia 3 | * Extended by Clemens Müller, @pangratz, https://github.com/pangratz 4 | */ 5 | 6 | // Put jQuery UI inside its own namespace 7 | JQ = Ember.Namespace.create(); 8 | 9 | // Create a new mixin for jQuery UI widgets using the Ember 10 | // mixin syntax. 11 | JQ.Widget = Em.Mixin.create({ 12 | // When Ember creates the view's DOM element, it will call this 13 | // method. 14 | didInsertElement: function() { 15 | // Make jQuery UI options available as Ember properties 16 | var options = this._gatherOptions(); 17 | 18 | // Make sure that jQuery UI events trigger methods on this view. 19 | this._gatherEvents(options); 20 | 21 | // Create a new instance of the jQuery UI widget based on its `uiType` 22 | // and the current element. 23 | var ui = this.$()[this.get('uiType')](options); 24 | 25 | // Save off the instance of the jQuery UI widget as the `ui` property 26 | // on this Ember view. 27 | this.set('ui', ui); 28 | }, 29 | 30 | // When Ember tears down the view's DOM element, it will call 31 | // this method. 32 | willDestroyElement: function() { 33 | var ui = this.get('ui'); 34 | 35 | if (ui) { 36 | // Tear down any observers that were created to make jQuery UI 37 | // options available as Ember properties. 38 | var observers = this._observers; 39 | for (var prop in observers) { 40 | if (observers.hasOwnProperty(prop)) { 41 | this.removeObserver(prop, observers[prop]); 42 | } 43 | } 44 | this.$()[this.get('uiType')]('destroy'); 45 | } 46 | }, 47 | 48 | // Each jQuery UI widget has a series of options that can be configured. 49 | // For instance, to disable a button, you call 50 | // `button.options('disabled', true)` in jQuery UI. To make this compatible 51 | // with Ember bindings, any time the Ember property for a 52 | // given jQuery UI option changes, we update the jQuery UI widget. 53 | _gatherOptions: function() { 54 | var uiOptions = this.get('uiOptions'), options = {}; 55 | 56 | // The view can specify a list of jQuery UI options that should be treated 57 | // as Ember properties. 58 | uiOptions.forEach(function(key) { 59 | options[key] = this.get(key); 60 | 61 | // Set up an observer on the Ember property. When it changes, 62 | // call jQuery UI's `setOption` method to reflect the property onto 63 | // the jQuery UI widget. 64 | var observer = function() { 65 | var value = this.get(key); 66 | this.$()[this.get('uiType')]('option', key, value); 67 | }; 68 | 69 | this.addObserver(key, observer); 70 | 71 | // Insert the observer in a Hash so we can remove it later. 72 | this._observers = this._observers || {}; 73 | this._observers[key] = observer; 74 | }, this); 75 | 76 | return options; 77 | }, 78 | 79 | // Each jQuery UI widget has a number of custom events that they can 80 | // trigger. For instance, the progressbar widget triggers a `complete` 81 | // event when the progress bar finishes. Make these events behave like 82 | // normal Ember events. For instance, a subclass of JQ.ProgressBar 83 | // could implement the `complete` method to be notified when the jQuery 84 | // UI widget triggered the event. 85 | _gatherEvents: function(options) { 86 | var uiEvents = this.get('uiEvents') || [], self = this; 87 | 88 | uiEvents.forEach(function(event) { 89 | var callback = self[event]; 90 | 91 | if (callback) { 92 | // You can register a handler for a jQuery UI event by passing 93 | // it in along with the creation options. Update the options hash 94 | // to include any event callbacks. 95 | options[event] = function(event, ui) { callback.call(self, event, ui); }; 96 | } 97 | }); 98 | } 99 | }); 100 | 101 | // Create a new Ember view for the jQuery UI Button widget 102 | JQ.Button = Em.View.extend(JQ.Widget, { 103 | uiType: 'button', 104 | uiOptions: ['label', 'disabled'], 105 | 106 | tagName: 'button' 107 | }); 108 | 109 | // Create a new Ember view for the jQuery UI Menu widget (new 110 | // in jQuery UI 1.9). Because it wraps a collection, we extend from 111 | // Ember's CollectionView rather than a normal view. 112 | // 113 | // This means that you should use `#collection` in your template to 114 | // create this view. 115 | JQ.Menu = Em.CollectionView.extend(JQ.Widget, { 116 | uiType: 'menu', 117 | uiOptions: ['disabled'], 118 | uiEvents: ['select'], 119 | 120 | tagName: 'ul', 121 | 122 | // Whenever the underlying Array for this `CollectionView` changes, 123 | // refresh the jQuery UI widget. 124 | arrayDidChange: function(content, start, removed, added) { 125 | this._super(content, start, removed, added); 126 | 127 | var ui = this.$(); 128 | var type = this.get('uiType'); 129 | if(ui) { 130 | // Schedule the refresh for after Ember has completed it's 131 | // render cycle 132 | Em.run.schedule('render', function(){ 133 | ui[type]('refresh'); 134 | }); 135 | } 136 | } 137 | }); 138 | 139 | // Create a new Ember view for the jQuery UI Progress Bar widget 140 | JQ.ProgressBar = Em.View.extend(JQ.Widget, { 141 | uiType: 'progressbar', 142 | uiOptions: ['value', 'max'], 143 | uiEvents: ['change', 'complete'] 144 | }); 145 | -------------------------------------------------------------------------------- /tests/qunit/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.7.0pre - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | * Pulled Live from Git Tue May 22 16:55:01 UTC 2012 10 | * Last Commit: f08dc894391c856fc9cfe715ec2198fab9af60d9 11 | */ 12 | 13 | /** Font Family and Sizes */ 14 | 15 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 16 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 17 | } 18 | 19 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 20 | #qunit-tests { font-size: smaller; } 21 | 22 | 23 | /** Resets */ 24 | 25 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | 31 | /** Header */ 32 | 33 | #qunit-header { 34 | padding: 0.5em 0 0.5em 1em; 35 | 36 | color: #8699a4; 37 | background-color: #0d3349; 38 | 39 | font-size: 1.5em; 40 | line-height: 1em; 41 | font-weight: normal; 42 | 43 | border-radius: 15px 15px 0 0; 44 | -moz-border-radius: 15px 15px 0 0; 45 | -webkit-border-top-right-radius: 15px; 46 | -webkit-border-top-left-radius: 15px; 47 | } 48 | 49 | #qunit-header a { 50 | text-decoration: none; 51 | color: #c2ccd1; 52 | } 53 | 54 | #qunit-header a:hover, 55 | #qunit-header a:focus { 56 | color: #fff; 57 | } 58 | 59 | #qunit-header label { 60 | display: inline-block; 61 | padding-left: 0.5em; 62 | } 63 | 64 | #qunit-banner { 65 | height: 5px; 66 | } 67 | 68 | #qunit-testrunner-toolbar { 69 | padding: 0.5em 0 0.5em 2em; 70 | color: #5E740B; 71 | background-color: #eee; 72 | } 73 | 74 | #qunit-userAgent { 75 | padding: 0.5em 0 0.5em 2.5em; 76 | background-color: #2b81af; 77 | color: #fff; 78 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 79 | } 80 | 81 | 82 | /** Tests: Pass/Fail */ 83 | 84 | #qunit-tests { 85 | list-style-position: inside; 86 | } 87 | 88 | #qunit-tests li { 89 | padding: 0.4em 0.5em 0.4em 2.5em; 90 | border-bottom: 1px solid #fff; 91 | list-style-position: inside; 92 | } 93 | 94 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 95 | display: none; 96 | } 97 | 98 | #qunit-tests li strong { 99 | cursor: pointer; 100 | } 101 | 102 | #qunit-tests li a { 103 | padding: 0.5em; 104 | color: #c2ccd1; 105 | text-decoration: none; 106 | } 107 | #qunit-tests li a:hover, 108 | #qunit-tests li a:focus { 109 | color: #000; 110 | } 111 | 112 | #qunit-tests ol { 113 | margin-top: 0.5em; 114 | padding: 0.5em; 115 | 116 | background-color: #fff; 117 | 118 | border-radius: 15px; 119 | -moz-border-radius: 15px; 120 | -webkit-border-radius: 15px; 121 | 122 | box-shadow: inset 0px 2px 13px #999; 123 | -moz-box-shadow: inset 0px 2px 13px #999; 124 | -webkit-box-shadow: inset 0px 2px 13px #999; 125 | } 126 | 127 | #qunit-tests table { 128 | border-collapse: collapse; 129 | margin-top: .2em; 130 | } 131 | 132 | #qunit-tests th { 133 | text-align: right; 134 | vertical-align: top; 135 | padding: 0 .5em 0 0; 136 | } 137 | 138 | #qunit-tests td { 139 | vertical-align: top; 140 | } 141 | 142 | #qunit-tests pre { 143 | margin: 0; 144 | white-space: pre-wrap; 145 | word-wrap: break-word; 146 | } 147 | 148 | #qunit-tests del { 149 | background-color: #e0f2be; 150 | color: #374e0c; 151 | text-decoration: none; 152 | } 153 | 154 | #qunit-tests ins { 155 | background-color: #ffcaca; 156 | color: #500; 157 | text-decoration: none; 158 | } 159 | 160 | /*** Test Counts */ 161 | 162 | #qunit-tests b.counts { color: black; } 163 | #qunit-tests b.passed { color: #5E740B; } 164 | #qunit-tests b.failed { color: #710909; } 165 | 166 | #qunit-tests li li { 167 | margin: 0.5em; 168 | padding: 0.4em 0.5em 0.4em 0.5em; 169 | background-color: #fff; 170 | border-bottom: none; 171 | list-style-position: inside; 172 | } 173 | 174 | /*** Passing Styles */ 175 | 176 | #qunit-tests li li.pass { 177 | color: #5E740B; 178 | background-color: #fff; 179 | border-left: 26px solid #C6E746; 180 | } 181 | 182 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 183 | #qunit-tests .pass .test-name { color: #366097; } 184 | 185 | #qunit-tests .pass .test-actual, 186 | #qunit-tests .pass .test-expected { color: #999999; } 187 | 188 | #qunit-banner.qunit-pass { background-color: #C6E746; } 189 | 190 | /*** Failing Styles */ 191 | 192 | #qunit-tests li li.fail { 193 | color: #710909; 194 | background-color: #fff; 195 | border-left: 26px solid #EE5757; 196 | white-space: pre; 197 | } 198 | 199 | #qunit-tests > li:last-child { 200 | border-radius: 0 0 15px 15px; 201 | -moz-border-radius: 0 0 15px 15px; 202 | -webkit-border-bottom-right-radius: 15px; 203 | -webkit-border-bottom-left-radius: 15px; 204 | } 205 | 206 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 207 | #qunit-tests .fail .test-name, 208 | #qunit-tests .fail .module-name { color: #000000; } 209 | 210 | #qunit-tests .fail .test-actual { color: #EE5757; } 211 | #qunit-tests .fail .test-expected { color: green; } 212 | 213 | #qunit-banner.qunit-fail { background-color: #EE5757; } 214 | 215 | 216 | /** Result */ 217 | 218 | #qunit-testresult { 219 | padding: 0.5em 0.5em 0.5em 2.5em; 220 | 221 | color: #2b81af; 222 | background-color: #D2E0E6; 223 | 224 | border-bottom: 1px solid white; 225 | } 226 | #qunit-testresult .module-name { 227 | font-weight: bold; 228 | } 229 | 230 | /** Fixture */ 231 | 232 | #qunit-fixture { 233 | position: absolute; 234 | top: -10000px; 235 | left: -10000px; 236 | width: 1000px; 237 | height: 1000px; 238 | } 239 | -------------------------------------------------------------------------------- /app/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | /* ==== Scroll down to find where to put your styles :) ==== */ 3 | 4 | /* HTML5 ✰ Boilerplate */ 5 | 6 | html, body, div, span, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, 9 | small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, figcaption, figure, 13 | footer, header, hgroup, menu, nav, section, summary, 14 | time, mark, audio, video { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-size: 100%; 19 | font: inherit; 20 | vertical-align: baseline; 21 | } 22 | 23 | article, aside, details, figcaption, figure, 24 | footer, header, hgroup, menu, nav, section { 25 | display: block; 26 | } 27 | 28 | blockquote, q { quotes: none; } 29 | blockquote:before, blockquote:after, 30 | q:before, q:after { content: ''; content: none; } 31 | ins { background-color: #ff9; color: #000; text-decoration: none; } 32 | mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } 33 | del { text-decoration: line-through; } 34 | abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } 35 | table { border-collapse: collapse; border-spacing: 0; } 36 | hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } 37 | input, select { vertical-align: middle; } 38 | 39 | body { font:13px/1.231 sans-serif; *font-size:small; } 40 | select, input, textarea, button { font:99% sans-serif; } 41 | pre, code, kbd, samp { font-family: monospace, sans-serif; } 42 | 43 | html { overflow-y: scroll; } 44 | a:hover, a:active { outline: none; } 45 | ul, ol { margin-left: 2em; } 46 | ol { list-style-type: decimal; } 47 | nav ul, nav li { margin: 0; list-style:none; list-style-image: none; } 48 | small { font-size: 85%; } 49 | strong, th { font-weight: bold; } 50 | td { vertical-align: top; } 51 | 52 | sub, sup { font-size: 75%; line-height: 0; position: relative; } 53 | sup { top: -0.5em; } 54 | sub { bottom: -0.25em; } 55 | 56 | pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; } 57 | textarea { overflow: auto; } 58 | .ie6 legend, .ie7 legend { margin-left: -7px; } 59 | input[type="radio"] { vertical-align: text-bottom; } 60 | input[type="checkbox"] { vertical-align: bottom; } 61 | .ie7 input[type="checkbox"] { vertical-align: baseline; } 62 | .ie6 input { vertical-align: text-bottom; } 63 | label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } 64 | button, input, select, textarea { margin: 0; } 65 | input:valid, textarea:valid { } 66 | input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; } 67 | .no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; } 68 | 69 | ::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; } 70 | ::selection { background:#FF5E99; color:#fff; text-shadow: none; } 71 | a:link { -webkit-tap-highlight-color: #FF5E99; } 72 | 73 | button { width: auto; overflow: visible; } 74 | .ie7 img { -ms-interpolation-mode: bicubic; } 75 | 76 | body, select, input, textarea { color: #444; } 77 | h1, h2, h3, h4, h5, h6 { font-weight: bold; } 78 | a, a:active, a:visited { color: #607890; } 79 | a:hover { color: #036; } 80 | 81 | /* 82 | // ========================================== \\ 83 | || || 84 | || Your styles ! || 85 | || || 86 | \\ ========================================== // 87 | */ 88 | 89 | body { 90 | max-width: 320px; 91 | margin: 10px; 92 | } 93 | 94 | button.ember-view { 95 | width: 100%; 96 | } 97 | 98 | div.ember-view { 99 | margin-top: 10px; 100 | margin-bottom: 10px; 101 | } 102 | 103 | p.annotated { 104 | margin-top: 30px; 105 | text-align: center; 106 | } 107 | 108 | p.annotated a { 109 | color: #66c; 110 | text-decoration: none; 111 | } 112 | 113 | p.annotated a:hover { 114 | color: #339; 115 | text-decoration: underline; 116 | } 117 | 118 | 119 | 120 | 121 | .ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; } 122 | .hidden { display: none; visibility: hidden; } 123 | .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } 124 | .visuallyhidden.focusable:active, 125 | .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } 126 | .invisible { visibility: hidden; } 127 | .clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; } 128 | .clearfix:after { clear: both; } 129 | .clearfix { zoom: 1; } 130 | 131 | 132 | @media all and (orientation:portrait) { 133 | 134 | } 135 | 136 | @media all and (orientation:landscape) { 137 | 138 | } 139 | 140 | @media screen and (max-device-width: 480px) { 141 | 142 | /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */ 143 | } 144 | 145 | 146 | @media print { 147 | * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; 148 | -ms-filter: none !important; } 149 | a, a:visited { color: #444 !important; text-decoration: underline; } 150 | a[href]:after { content: " (" attr(href) ")"; } 151 | abbr[title]:after { content: " (" attr(title) ")"; } 152 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 153 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 154 | thead { display: table-header-group; } 155 | tr, img { page-break-inside: avoid; } 156 | @page { margin: 0.5cm; } 157 | p, h2, h3 { orphans: 3; widows: 3; } 158 | h2, h3{ page-break-after: avoid; } 159 | } -------------------------------------------------------------------------------- /app/css/jquery-ui.css: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI CSS Framework 1.8.18 3 | * 4 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | * http://docs.jquery.com/UI/Theming/API 9 | */ 10 | 11 | /* Layout helpers 12 | ----------------------------------*/ 13 | .ui-helper-hidden { display: none; } 14 | .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } 15 | .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } 16 | .ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } 17 | .ui-helper-clearfix:after { clear: both; } 18 | .ui-helper-clearfix { zoom: 1; } 19 | .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } 20 | 21 | 22 | /* Interaction Cues 23 | ----------------------------------*/ 24 | .ui-state-disabled { cursor: default !important; } 25 | 26 | 27 | /* Icons 28 | ----------------------------------*/ 29 | 30 | /* states and images */ 31 | .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } 32 | 33 | 34 | /* Misc visuals 35 | ----------------------------------*/ 36 | 37 | /* Overlays */ 38 | .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 39 | /* 40 | * jQuery UI Accordion 1.8.18 41 | * 42 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 43 | * Dual licensed under the MIT or GPL Version 2 licenses. 44 | * http://jquery.org/license 45 | * 46 | * http://docs.jquery.com/UI/Accordion#theming 47 | */ 48 | /* IE/Win - Fix animation bug - #4615 */ 49 | .ui-accordion { width: 100%; } 50 | .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } 51 | .ui-accordion .ui-accordion-li-fix { display: inline; } 52 | .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } 53 | .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } 54 | .ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } 55 | .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } 56 | .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } 57 | .ui-accordion .ui-accordion-content-active { display: block; } 58 | /* 59 | * jQuery UI Autocomplete 1.8.18 60 | * 61 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 62 | * Dual licensed under the MIT or GPL Version 2 licenses. 63 | * http://jquery.org/license 64 | * 65 | * http://docs.jquery.com/UI/Autocomplete#theming 66 | */ 67 | .ui-autocomplete { position: absolute; cursor: default; } 68 | 69 | /* workarounds */ 70 | * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ 71 | 72 | /* 73 | * jQuery UI Menu 1.8.18 74 | * 75 | * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 76 | * Dual licensed under the MIT or GPL Version 2 licenses. 77 | * http://jquery.org/license 78 | * 79 | * http://docs.jquery.com/UI/Menu#theming 80 | */ 81 | .ui-menu { 82 | list-style:none; 83 | padding: 2px; 84 | margin: 0; 85 | display:block; 86 | float: left; 87 | } 88 | .ui-menu .ui-menu { 89 | margin-top: -3px; 90 | } 91 | .ui-menu .ui-menu-item { 92 | margin:0; 93 | padding: 0; 94 | zoom: 1; 95 | float: left; 96 | clear: left; 97 | width: 100%; 98 | } 99 | .ui-menu .ui-menu-item a { 100 | text-decoration:none; 101 | display:block; 102 | padding:.2em .4em; 103 | line-height:1.5; 104 | zoom:1; 105 | } 106 | .ui-menu .ui-menu-item a.ui-state-hover, 107 | .ui-menu .ui-menu-item a.ui-state-active { 108 | font-weight: normal; 109 | margin: -1px; 110 | } 111 | /* 112 | * jQuery UI Button 1.8.18 113 | * 114 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 115 | * Dual licensed under the MIT or GPL Version 2 licenses. 116 | * http://jquery.org/license 117 | * 118 | * http://docs.jquery.com/UI/Button#theming 119 | */ 120 | .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: hidden; *overflow: visible; } /* the overflow property removes extra width in IE */ 121 | .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ 122 | button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ 123 | .ui-button-icons-only { width: 3.4em; } 124 | button.ui-button-icons-only { width: 3.7em; } 125 | 126 | /*button text element */ 127 | .ui-button .ui-button-text { display: block; line-height: 1.4; } 128 | .ui-button-text-only .ui-button-text { padding: .4em 1em; } 129 | .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } 130 | .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } 131 | .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } 132 | .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } 133 | /* no icon support for input elements, provide padding by default */ 134 | input.ui-button { padding: .4em 1em; } 135 | 136 | /*button icon element(s) */ 137 | .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } 138 | .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } 139 | .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } 140 | .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } 141 | .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } 142 | 143 | /*button sets*/ 144 | .ui-buttonset { margin-right: 7px; } 145 | .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } 146 | 147 | /* workarounds */ 148 | button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ 149 | /* 150 | * jQuery UI Datepicker 1.8.18 151 | * 152 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 153 | * Dual licensed under the MIT or GPL Version 2 licenses. 154 | * http://jquery.org/license 155 | * 156 | * http://docs.jquery.com/UI/Datepicker#theming 157 | */ 158 | .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } 159 | .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } 160 | .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } 161 | .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } 162 | .ui-datepicker .ui-datepicker-prev { left:2px; } 163 | .ui-datepicker .ui-datepicker-next { right:2px; } 164 | .ui-datepicker .ui-datepicker-prev-hover { left:1px; } 165 | .ui-datepicker .ui-datepicker-next-hover { right:1px; } 166 | .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } 167 | .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } 168 | .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } 169 | .ui-datepicker select.ui-datepicker-month-year {width: 100%;} 170 | .ui-datepicker select.ui-datepicker-month, 171 | .ui-datepicker select.ui-datepicker-year { width: 49%;} 172 | .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } 173 | .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } 174 | .ui-datepicker td { border: 0; padding: 1px; } 175 | .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } 176 | .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } 177 | .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } 178 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } 179 | 180 | /* with multiple calendars */ 181 | .ui-datepicker.ui-datepicker-multi { width:auto; } 182 | .ui-datepicker-multi .ui-datepicker-group { float:left; } 183 | .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } 184 | .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } 185 | .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } 186 | .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } 187 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } 188 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } 189 | .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } 190 | .ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } 191 | 192 | /* RTL support */ 193 | .ui-datepicker-rtl { direction: rtl; } 194 | .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } 195 | .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } 196 | .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } 197 | .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } 198 | .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } 199 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } 200 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } 201 | .ui-datepicker-rtl .ui-datepicker-group { float:right; } 202 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 203 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } 204 | 205 | /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ 206 | .ui-datepicker-cover { 207 | display: none; /*sorry for IE5*/ 208 | display/**/: block; /*sorry for IE5*/ 209 | position: absolute; /*must have*/ 210 | z-index: -1; /*must have*/ 211 | filter: mask(); /*must have*/ 212 | top: -4px; /*must have*/ 213 | left: -4px; /*must have*/ 214 | width: 200px; /*must have*/ 215 | height: 200px; /*must have*/ 216 | }/* 217 | * jQuery UI Dialog 1.8.18 218 | * 219 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 220 | * Dual licensed under the MIT or GPL Version 2 licenses. 221 | * http://jquery.org/license 222 | * 223 | * http://docs.jquery.com/UI/Dialog#theming 224 | */ 225 | .ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } 226 | .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } 227 | .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } 228 | .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } 229 | .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } 230 | .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } 231 | .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } 232 | .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } 233 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } 234 | .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } 235 | .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } 236 | .ui-draggable .ui-dialog-titlebar { cursor: move; } 237 | /* 238 | * jQuery UI Progressbar 1.8.18 239 | * 240 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 241 | * Dual licensed under the MIT or GPL Version 2 licenses. 242 | * http://jquery.org/license 243 | * 244 | * http://docs.jquery.com/UI/Progressbar#theming 245 | */ 246 | .ui-progressbar { height:2em; text-align: left; overflow: hidden; } 247 | .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* 248 | * jQuery UI Resizable 1.8.18 249 | * 250 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 251 | * Dual licensed under the MIT or GPL Version 2 licenses. 252 | * http://jquery.org/license 253 | * 254 | * http://docs.jquery.com/UI/Resizable#theming 255 | */ 256 | .ui-resizable { position: relative;} 257 | .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; } 258 | .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } 259 | .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } 260 | .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } 261 | .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } 262 | .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } 263 | .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } 264 | .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } 265 | .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } 266 | .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* 267 | * jQuery UI Selectable 1.8.18 268 | * 269 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 270 | * Dual licensed under the MIT or GPL Version 2 licenses. 271 | * http://jquery.org/license 272 | * 273 | * http://docs.jquery.com/UI/Selectable#theming 274 | */ 275 | .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } 276 | /* 277 | * jQuery UI Slider 1.8.18 278 | * 279 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 280 | * Dual licensed under the MIT or GPL Version 2 licenses. 281 | * http://jquery.org/license 282 | * 283 | * http://docs.jquery.com/UI/Slider#theming 284 | */ 285 | .ui-slider { position: relative; text-align: left; } 286 | .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } 287 | .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } 288 | 289 | .ui-slider-horizontal { height: .8em; } 290 | .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } 291 | .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } 292 | .ui-slider-horizontal .ui-slider-range-min { left: 0; } 293 | .ui-slider-horizontal .ui-slider-range-max { right: 0; } 294 | 295 | .ui-slider-vertical { width: .8em; height: 100px; } 296 | .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } 297 | .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } 298 | .ui-slider-vertical .ui-slider-range-min { bottom: 0; } 299 | .ui-slider-vertical .ui-slider-range-max { top: 0; }/* 300 | * jQuery UI Tabs 1.8.18 301 | * 302 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 303 | * Dual licensed under the MIT or GPL Version 2 licenses. 304 | * http://jquery.org/license 305 | * 306 | * http://docs.jquery.com/UI/Tabs#theming 307 | */ 308 | .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 309 | .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } 310 | .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } 311 | .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } 312 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } 313 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } 314 | .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ 315 | .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } 316 | .ui-tabs .ui-tabs-hide { display: none !important; } 317 | /* 318 | * jQuery UI CSS Framework 1.8.18 319 | * 320 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 321 | * Dual licensed under the MIT or GPL Version 2 licenses. 322 | * http://jquery.org/license 323 | * 324 | * http://docs.jquery.com/UI/Theming/API 325 | * 326 | * To view and modify this theme, visit http://jqueryui.com/themeroller/ 327 | */ 328 | 329 | 330 | /* Component containers 331 | ----------------------------------*/ 332 | .ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; } 333 | .ui-widget .ui-widget { font-size: 1em; } 334 | .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; } 335 | .ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; } 336 | .ui-widget-content a { color: #222222/*{fcContent}*/; } 337 | .ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; } 338 | .ui-widget-header a { color: #222222/*{fcHeader}*/; } 339 | 340 | /* Interaction states 341 | ----------------------------------*/ 342 | .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; } 343 | .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; } 344 | .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; } 345 | .ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; } 346 | .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; } 347 | .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; } 348 | .ui-widget :active { outline: none; } 349 | 350 | /* Interaction Cues 351 | ----------------------------------*/ 352 | .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; } 353 | .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; } 354 | .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; } 355 | .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; } 356 | .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; } 357 | .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } 358 | .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } 359 | .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } 360 | 361 | /* Icons 362 | ----------------------------------*/ 363 | 364 | /* states and images */ 365 | .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } 366 | .ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } 367 | .ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; } 368 | .ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; } 369 | .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; } 370 | .ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; } 371 | .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; } 372 | .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; } 373 | 374 | /* positioning */ 375 | .ui-icon-carat-1-n { background-position: 0 0; } 376 | .ui-icon-carat-1-ne { background-position: -16px 0; } 377 | .ui-icon-carat-1-e { background-position: -32px 0; } 378 | .ui-icon-carat-1-se { background-position: -48px 0; } 379 | .ui-icon-carat-1-s { background-position: -64px 0; } 380 | .ui-icon-carat-1-sw { background-position: -80px 0; } 381 | .ui-icon-carat-1-w { background-position: -96px 0; } 382 | .ui-icon-carat-1-nw { background-position: -112px 0; } 383 | .ui-icon-carat-2-n-s { background-position: -128px 0; } 384 | .ui-icon-carat-2-e-w { background-position: -144px 0; } 385 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 386 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 387 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 388 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 389 | .ui-icon-triangle-1-s { background-position: -64px -16px; } 390 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 391 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 392 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 393 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 394 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 395 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 396 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 397 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 398 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 399 | .ui-icon-arrow-1-s { background-position: -64px -32px; } 400 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 401 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 402 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 403 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 404 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 405 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 406 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 407 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 408 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 409 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 410 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 411 | .ui-icon-arrowthick-1-n { background-position: 0 -48px; } 412 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 413 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 414 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 415 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 416 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 417 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 418 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 419 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 420 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 421 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 422 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 423 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 424 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 425 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 426 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 427 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 428 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 429 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 430 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 431 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 432 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 433 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 434 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 435 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 436 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 437 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 438 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 439 | .ui-icon-arrow-4 { background-position: 0 -80px; } 440 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 441 | .ui-icon-extlink { background-position: -32px -80px; } 442 | .ui-icon-newwin { background-position: -48px -80px; } 443 | .ui-icon-refresh { background-position: -64px -80px; } 444 | .ui-icon-shuffle { background-position: -80px -80px; } 445 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 446 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 447 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 448 | .ui-icon-folder-open { background-position: -16px -96px; } 449 | .ui-icon-document { background-position: -32px -96px; } 450 | .ui-icon-document-b { background-position: -48px -96px; } 451 | .ui-icon-note { background-position: -64px -96px; } 452 | .ui-icon-mail-closed { background-position: -80px -96px; } 453 | .ui-icon-mail-open { background-position: -96px -96px; } 454 | .ui-icon-suitcase { background-position: -112px -96px; } 455 | .ui-icon-comment { background-position: -128px -96px; } 456 | .ui-icon-person { background-position: -144px -96px; } 457 | .ui-icon-print { background-position: -160px -96px; } 458 | .ui-icon-trash { background-position: -176px -96px; } 459 | .ui-icon-locked { background-position: -192px -96px; } 460 | .ui-icon-unlocked { background-position: -208px -96px; } 461 | .ui-icon-bookmark { background-position: -224px -96px; } 462 | .ui-icon-tag { background-position: -240px -96px; } 463 | .ui-icon-home { background-position: 0 -112px; } 464 | .ui-icon-flag { background-position: -16px -112px; } 465 | .ui-icon-calendar { background-position: -32px -112px; } 466 | .ui-icon-cart { background-position: -48px -112px; } 467 | .ui-icon-pencil { background-position: -64px -112px; } 468 | .ui-icon-clock { background-position: -80px -112px; } 469 | .ui-icon-disk { background-position: -96px -112px; } 470 | .ui-icon-calculator { background-position: -112px -112px; } 471 | .ui-icon-zoomin { background-position: -128px -112px; } 472 | .ui-icon-zoomout { background-position: -144px -112px; } 473 | .ui-icon-search { background-position: -160px -112px; } 474 | .ui-icon-wrench { background-position: -176px -112px; } 475 | .ui-icon-gear { background-position: -192px -112px; } 476 | .ui-icon-heart { background-position: -208px -112px; } 477 | .ui-icon-star { background-position: -224px -112px; } 478 | .ui-icon-link { background-position: -240px -112px; } 479 | .ui-icon-cancel { background-position: 0 -128px; } 480 | .ui-icon-plus { background-position: -16px -128px; } 481 | .ui-icon-plusthick { background-position: -32px -128px; } 482 | .ui-icon-minus { background-position: -48px -128px; } 483 | .ui-icon-minusthick { background-position: -64px -128px; } 484 | .ui-icon-close { background-position: -80px -128px; } 485 | .ui-icon-closethick { background-position: -96px -128px; } 486 | .ui-icon-key { background-position: -112px -128px; } 487 | .ui-icon-lightbulb { background-position: -128px -128px; } 488 | .ui-icon-scissors { background-position: -144px -128px; } 489 | .ui-icon-clipboard { background-position: -160px -128px; } 490 | .ui-icon-copy { background-position: -176px -128px; } 491 | .ui-icon-contact { background-position: -192px -128px; } 492 | .ui-icon-image { background-position: -208px -128px; } 493 | .ui-icon-video { background-position: -224px -128px; } 494 | .ui-icon-script { background-position: -240px -128px; } 495 | .ui-icon-alert { background-position: 0 -144px; } 496 | .ui-icon-info { background-position: -16px -144px; } 497 | .ui-icon-notice { background-position: -32px -144px; } 498 | .ui-icon-help { background-position: -48px -144px; } 499 | .ui-icon-check { background-position: -64px -144px; } 500 | .ui-icon-bullet { background-position: -80px -144px; } 501 | .ui-icon-radio-off { background-position: -96px -144px; } 502 | .ui-icon-radio-on { background-position: -112px -144px; } 503 | .ui-icon-pin-w { background-position: -128px -144px; } 504 | .ui-icon-pin-s { background-position: -144px -144px; } 505 | .ui-icon-play { background-position: 0 -160px; } 506 | .ui-icon-pause { background-position: -16px -160px; } 507 | .ui-icon-seek-next { background-position: -32px -160px; } 508 | .ui-icon-seek-prev { background-position: -48px -160px; } 509 | .ui-icon-seek-end { background-position: -64px -160px; } 510 | .ui-icon-seek-start { background-position: -80px -160px; } 511 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 512 | .ui-icon-seek-first { background-position: -80px -160px; } 513 | .ui-icon-stop { background-position: -96px -160px; } 514 | .ui-icon-eject { background-position: -112px -160px; } 515 | .ui-icon-volume-off { background-position: -128px -160px; } 516 | .ui-icon-volume-on { background-position: -144px -160px; } 517 | .ui-icon-power { background-position: 0 -176px; } 518 | .ui-icon-signal-diag { background-position: -16px -176px; } 519 | .ui-icon-signal { background-position: -32px -176px; } 520 | .ui-icon-battery-0 { background-position: -48px -176px; } 521 | .ui-icon-battery-1 { background-position: -64px -176px; } 522 | .ui-icon-battery-2 { background-position: -80px -176px; } 523 | .ui-icon-battery-3 { background-position: -96px -176px; } 524 | .ui-icon-circle-plus { background-position: 0 -192px; } 525 | .ui-icon-circle-minus { background-position: -16px -192px; } 526 | .ui-icon-circle-close { background-position: -32px -192px; } 527 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 528 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 529 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 530 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 531 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 532 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 533 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 534 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 535 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 536 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 537 | .ui-icon-circle-check { background-position: -208px -192px; } 538 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 539 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 540 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 541 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 542 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 543 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 544 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 545 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 546 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 547 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 548 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 549 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 550 | 551 | 552 | /* Misc visuals 553 | ----------------------------------*/ 554 | 555 | /* Corner radius */ 556 | .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; -khtml-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; } 557 | .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; -khtml-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } 558 | .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; -khtml-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } 559 | .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; -khtml-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } 560 | 561 | /* Overlays */ 562 | .ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; } 563 | .ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -khtml-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } -------------------------------------------------------------------------------- /tests/qunit/qunit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.7.0pre - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | * Pulled Live from Git Tue May 22 16:55:01 UTC 2012 10 | * Last Commit: f08dc894391c856fc9cfe715ec2198fab9af60d9 11 | */ 12 | 13 | (function( window ) { 14 | 15 | var QUnit, 16 | config, 17 | testId = 0, 18 | toString = Object.prototype.toString, 19 | hasOwn = Object.prototype.hasOwnProperty, 20 | defined = { 21 | setTimeout: typeof window.setTimeout !== "undefined", 22 | sessionStorage: (function() { 23 | var x = "qunit-test-string"; 24 | try { 25 | sessionStorage.setItem( x, x ); 26 | sessionStorage.removeItem( x ); 27 | return true; 28 | } catch( e ) { 29 | return false; 30 | } 31 | }()) 32 | }; 33 | 34 | function Test( settings ) { 35 | extend( this, settings ); 36 | this.assertions = []; 37 | this.testNumber = ++Test.count; 38 | } 39 | 40 | Test.count = 0; 41 | 42 | Test.prototype = { 43 | init: function() { 44 | var a, b, li, 45 | tests = id( "qunit-tests" ); 46 | 47 | if ( tests ) { 48 | b = document.createElement( "strong" ); 49 | b.innerHTML = this.name; 50 | 51 | // `a` initialized at top of scope 52 | a = document.createElement( "a" ); 53 | a.innerHTML = "Rerun"; 54 | a.href = QUnit.url({ testNumber: this.testNumber }); 55 | 56 | li = document.createElement( "li" ); 57 | li.appendChild( b ); 58 | li.appendChild( a ); 59 | li.className = "running"; 60 | li.id = this.id = "qunit-test-output" + testId++; 61 | 62 | tests.appendChild( li ); 63 | } 64 | }, 65 | setup: function() { 66 | if ( this.module !== config.previousModule ) { 67 | if ( config.previousModule ) { 68 | runLoggingCallbacks( "moduleDone", QUnit, { 69 | name: config.previousModule, 70 | failed: config.moduleStats.bad, 71 | passed: config.moduleStats.all - config.moduleStats.bad, 72 | total: config.moduleStats.all 73 | }); 74 | } 75 | config.previousModule = this.module; 76 | config.moduleStats = { all: 0, bad: 0 }; 77 | runLoggingCallbacks( "moduleStart", QUnit, { 78 | name: this.module 79 | }); 80 | } else if ( config.autorun ) { 81 | runLoggingCallbacks( "moduleStart", QUnit, { 82 | name: this.module 83 | }); 84 | } 85 | 86 | config.current = this; 87 | 88 | this.testEnvironment = extend({ 89 | setup: function() {}, 90 | teardown: function() {} 91 | }, this.moduleTestEnvironment ); 92 | 93 | runLoggingCallbacks( "testStart", QUnit, { 94 | name: this.testName, 95 | module: this.module 96 | }); 97 | 98 | // allow utility functions to access the current test environment 99 | // TODO why?? 100 | QUnit.current_testEnvironment = this.testEnvironment; 101 | 102 | if ( !config.pollution ) { 103 | saveGlobal(); 104 | } 105 | if ( config.notrycatch ) { 106 | this.testEnvironment.setup.call( this.testEnvironment ); 107 | return; 108 | } 109 | try { 110 | this.testEnvironment.setup.call( this.testEnvironment ); 111 | } catch( e ) { 112 | QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); 113 | } 114 | }, 115 | run: function() { 116 | config.current = this; 117 | 118 | var running = id( "qunit-testresult" ); 119 | 120 | if ( running ) { 121 | running.innerHTML = "Running:
" + this.name; 122 | } 123 | 124 | if ( this.async ) { 125 | QUnit.stop(); 126 | } 127 | 128 | if ( config.notrycatch ) { 129 | this.callback.call( this.testEnvironment, QUnit.assert ); 130 | return; 131 | } 132 | 133 | try { 134 | this.callback.call( this.testEnvironment, QUnit.assert ); 135 | } catch( e ) { 136 | QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + ": " + e.message, extractStacktrace( e, 1 ) ); 137 | // else next test will carry the responsibility 138 | saveGlobal(); 139 | 140 | // Restart the tests if they're blocking 141 | if ( config.blocking ) { 142 | QUnit.start(); 143 | } 144 | } 145 | }, 146 | teardown: function() { 147 | config.current = this; 148 | if ( config.notrycatch ) { 149 | this.testEnvironment.teardown.call( this.testEnvironment ); 150 | return; 151 | } else { 152 | try { 153 | this.testEnvironment.teardown.call( this.testEnvironment ); 154 | } catch( e ) { 155 | QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) ); 156 | } 157 | } 158 | checkPollution(); 159 | }, 160 | finish: function() { 161 | config.current = this; 162 | if ( this.expected != null && this.expected != this.assertions.length ) { 163 | QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); 164 | } else if ( this.expected == null && !this.assertions.length ) { 165 | QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); 166 | } 167 | 168 | var assertion, a, b, i, li, ol, 169 | test = this, 170 | good = 0, 171 | bad = 0, 172 | tests = id( "qunit-tests" ); 173 | 174 | config.stats.all += this.assertions.length; 175 | config.moduleStats.all += this.assertions.length; 176 | 177 | if ( tests ) { 178 | ol = document.createElement( "ol" ); 179 | 180 | for ( i = 0; i < this.assertions.length; i++ ) { 181 | assertion = this.assertions[i]; 182 | 183 | li = document.createElement( "li" ); 184 | li.className = assertion.result ? "pass" : "fail"; 185 | li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); 186 | ol.appendChild( li ); 187 | 188 | if ( assertion.result ) { 189 | good++; 190 | } else { 191 | bad++; 192 | config.stats.bad++; 193 | config.moduleStats.bad++; 194 | } 195 | } 196 | 197 | // store result when possible 198 | if ( QUnit.config.reorder && defined.sessionStorage ) { 199 | if ( bad ) { 200 | sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); 201 | } else { 202 | sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); 203 | } 204 | } 205 | 206 | if ( bad === 0 ) { 207 | ol.style.display = "none"; 208 | } 209 | 210 | // `b` initialized at top of scope 211 | b = document.createElement( "strong" ); 212 | b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; 213 | 214 | addEvent(b, "click", function() { 215 | var next = b.nextSibling.nextSibling, 216 | display = next.style.display; 217 | next.style.display = display === "none" ? "block" : "none"; 218 | }); 219 | 220 | addEvent(b, "dblclick", function( e ) { 221 | var target = e && e.target ? e.target : window.event.srcElement; 222 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { 223 | target = target.parentNode; 224 | } 225 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 226 | window.location = QUnit.url({ testNumber: test.testNumber }); 227 | } 228 | }); 229 | 230 | // `li` initialized at top of scope 231 | li = id( this.id ); 232 | li.className = bad ? "fail" : "pass"; 233 | li.removeChild( li.firstChild ); 234 | a = li.firstChild; 235 | li.appendChild( b ); 236 | li.appendChild ( a ); 237 | li.appendChild( ol ); 238 | 239 | } else { 240 | for ( i = 0; i < this.assertions.length; i++ ) { 241 | if ( !this.assertions[i].result ) { 242 | bad++; 243 | config.stats.bad++; 244 | config.moduleStats.bad++; 245 | } 246 | } 247 | } 248 | 249 | runLoggingCallbacks( "testDone", QUnit, { 250 | name: this.testName, 251 | module: this.module, 252 | failed: bad, 253 | passed: this.assertions.length - bad, 254 | total: this.assertions.length 255 | }); 256 | 257 | QUnit.reset(); 258 | }, 259 | 260 | queue: function() { 261 | var bad, 262 | test = this; 263 | 264 | synchronize(function() { 265 | test.init(); 266 | }); 267 | function run() { 268 | // each of these can by async 269 | synchronize(function() { 270 | test.setup(); 271 | }); 272 | synchronize(function() { 273 | test.run(); 274 | }); 275 | synchronize(function() { 276 | test.teardown(); 277 | }); 278 | synchronize(function() { 279 | test.finish(); 280 | }); 281 | } 282 | 283 | // `bad` initialized at top of scope 284 | // defer when previous test run passed, if storage is available 285 | bad = QUnit.config.reorder && defined.sessionStorage && 286 | +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); 287 | 288 | if ( bad ) { 289 | run(); 290 | } else { 291 | synchronize( run, true ); 292 | } 293 | } 294 | }; 295 | 296 | // Root QUnit object. 297 | // `QUnit` initialized at top of scope 298 | QUnit = { 299 | 300 | // call on start of module test to prepend name to all tests 301 | module: function( name, testEnvironment ) { 302 | config.currentModule = name; 303 | config.currentModuleTestEnviroment = testEnvironment; 304 | }, 305 | 306 | asyncTest: function( testName, expected, callback ) { 307 | if ( arguments.length === 2 ) { 308 | callback = expected; 309 | expected = null; 310 | } 311 | 312 | QUnit.test( testName, expected, callback, true ); 313 | }, 314 | 315 | test: function( testName, expected, callback, async ) { 316 | var test, 317 | name = "" + escapeInnerText( testName ) + ""; 318 | 319 | if ( arguments.length === 2 ) { 320 | callback = expected; 321 | expected = null; 322 | } 323 | 324 | if ( config.currentModule ) { 325 | name = "" + config.currentModule + ": " + name; 326 | } 327 | 328 | test = new Test({ 329 | name: name, 330 | testName: testName, 331 | expected: expected, 332 | async: async, 333 | callback: callback, 334 | module: config.currentModule, 335 | moduleTestEnvironment: config.currentModuleTestEnviroment, 336 | stack: sourceFromStacktrace( 2 ) 337 | }); 338 | 339 | if ( !validTest( test ) ) { 340 | return; 341 | } 342 | 343 | test.queue(); 344 | }, 345 | 346 | // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 347 | expect: function( asserts ) { 348 | config.current.expected = asserts; 349 | }, 350 | 351 | start: function( count ) { 352 | config.semaphore -= count || 1; 353 | // don't start until equal number of stop-calls 354 | if ( config.semaphore > 0 ) { 355 | return; 356 | } 357 | // ignore if start is called more often then stop 358 | if ( config.semaphore < 0 ) { 359 | config.semaphore = 0; 360 | } 361 | // A slight delay, to avoid any current callbacks 362 | if ( defined.setTimeout ) { 363 | window.setTimeout(function() { 364 | if ( config.semaphore > 0 ) { 365 | return; 366 | } 367 | if ( config.timeout ) { 368 | clearTimeout( config.timeout ); 369 | } 370 | 371 | config.blocking = false; 372 | process( true ); 373 | }, 13); 374 | } else { 375 | config.blocking = false; 376 | process( true ); 377 | } 378 | }, 379 | 380 | stop: function( count ) { 381 | config.semaphore += count || 1; 382 | config.blocking = true; 383 | 384 | if ( config.testTimeout && defined.setTimeout ) { 385 | clearTimeout( config.timeout ); 386 | config.timeout = window.setTimeout(function() { 387 | QUnit.ok( false, "Test timed out" ); 388 | config.semaphore = 1; 389 | QUnit.start(); 390 | }, config.testTimeout ); 391 | } 392 | } 393 | }; 394 | 395 | // Asssert helpers 396 | // All of these must call either QUnit.push() or manually do: 397 | // - runLoggingCallbacks( "log", .. ); 398 | // - config.current.assertions.push({ .. }); 399 | QUnit.assert = { 400 | /** 401 | * Asserts rough true-ish result. 402 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 403 | */ 404 | ok: function( result, msg ) { 405 | if ( !config.current ) { 406 | throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); 407 | } 408 | result = !!result; 409 | 410 | var source, 411 | details = { 412 | result: result, 413 | message: msg 414 | }; 415 | 416 | msg = escapeInnerText( msg || (result ? "okay" : "failed" ) ); 417 | msg = "" + msg + ""; 418 | 419 | if ( !result ) { 420 | source = sourceFromStacktrace( 2 ); 421 | if ( source ) { 422 | details.source = source; 423 | msg += "
Source:
" + escapeInnerText( source ) + "
"; 424 | } 425 | } 426 | runLoggingCallbacks( "log", QUnit, details ); 427 | config.current.assertions.push({ 428 | result: result, 429 | message: msg 430 | }); 431 | }, 432 | 433 | /** 434 | * Assert that the first two arguments are equal, with an optional message. 435 | * Prints out both actual and expected values. 436 | * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); 437 | */ 438 | equal: function( actual, expected, message ) { 439 | QUnit.push( expected == actual, actual, expected, message ); 440 | }, 441 | 442 | notEqual: function( actual, expected, message ) { 443 | QUnit.push( expected != actual, actual, expected, message ); 444 | }, 445 | 446 | deepEqual: function( actual, expected, message ) { 447 | QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); 448 | }, 449 | 450 | notDeepEqual: function( actual, expected, message ) { 451 | QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); 452 | }, 453 | 454 | strictEqual: function( actual, expected, message ) { 455 | QUnit.push( expected === actual, actual, expected, message ); 456 | }, 457 | 458 | notStrictEqual: function( actual, expected, message ) { 459 | QUnit.push( expected !== actual, actual, expected, message ); 460 | }, 461 | 462 | raises: function( block, expected, message ) { 463 | var actual, 464 | ok = false; 465 | 466 | if ( typeof expected === "string" ) { 467 | message = expected; 468 | expected = null; 469 | } 470 | 471 | try { 472 | block.call( config.current.testEnvironment ); 473 | } catch (e) { 474 | actual = e; 475 | } 476 | 477 | if ( actual ) { 478 | // we don't want to validate thrown error 479 | if ( !expected ) { 480 | ok = true; 481 | // expected is a regexp 482 | } else if ( QUnit.objectType( expected ) === "regexp" ) { 483 | ok = expected.test( actual ); 484 | // expected is a constructor 485 | } else if ( actual instanceof expected ) { 486 | ok = true; 487 | // expected is a validation function which returns true is validation passed 488 | } else if ( expected.call( {}, actual ) === true ) { 489 | ok = true; 490 | } 491 | } 492 | 493 | QUnit.push( ok, actual, null, message ); 494 | } 495 | }; 496 | 497 | // @deprecated: Kept assertion helpers in root for backwards compatibility 498 | extend( QUnit, QUnit.assert ); 499 | 500 | /** 501 | * @deprecated: Kept for backwards compatibility 502 | * next step: remove entirely 503 | */ 504 | QUnit.equals = function() { 505 | QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); 506 | }; 507 | QUnit.same = function() { 508 | QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); 509 | }; 510 | 511 | // We want access to the constructor's prototype 512 | (function() { 513 | function F() {} 514 | F.prototype = QUnit; 515 | QUnit = new F(); 516 | // Make F QUnit's constructor so that we can add to the prototype later 517 | QUnit.constructor = F; 518 | }()); 519 | 520 | /** 521 | * Config object: Maintain internal state 522 | * Later exposed as QUnit.config 523 | * `config` initialized at top of scope 524 | */ 525 | config = { 526 | // The queue of tests to run 527 | queue: [], 528 | 529 | // block until document ready 530 | blocking: true, 531 | 532 | // when enabled, show only failing tests 533 | // gets persisted through sessionStorage and can be changed in UI via checkbox 534 | hidepassed: false, 535 | 536 | // by default, run previously failed tests first 537 | // very useful in combination with "Hide passed tests" checked 538 | reorder: true, 539 | 540 | // by default, modify document.title when suite is done 541 | altertitle: true, 542 | 543 | urlConfig: [ "noglobals", "notrycatch" ], 544 | 545 | // logging callback queues 546 | begin: [], 547 | done: [], 548 | log: [], 549 | testStart: [], 550 | testDone: [], 551 | moduleStart: [], 552 | moduleDone: [] 553 | }; 554 | 555 | // Initialize more QUnit.config and QUnit.urlParams 556 | (function() { 557 | var i, 558 | location = window.location || { search: "", protocol: "file:" }, 559 | params = location.search.slice( 1 ).split( "&" ), 560 | length = params.length, 561 | urlParams = {}, 562 | current; 563 | 564 | if ( params[ 0 ] ) { 565 | for ( i = 0; i < length; i++ ) { 566 | current = params[ i ].split( "=" ); 567 | current[ 0 ] = decodeURIComponent( current[ 0 ] ); 568 | // allow just a key to turn on a flag, e.g., test.html?noglobals 569 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; 570 | urlParams[ current[ 0 ] ] = current[ 1 ]; 571 | } 572 | } 573 | 574 | QUnit.urlParams = urlParams; 575 | config.filter = urlParams.filter; 576 | config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; 577 | 578 | // Figure out if we're running the tests from a server or not 579 | QUnit.isLocal = location.protocol === "file:"; 580 | }()); 581 | 582 | // Export global variables, unless an 'exports' object exists, 583 | // in that case we assume we're in CommonJS (dealt with on the bottom of the script) 584 | if ( typeof exports === "undefined" ) { 585 | extend( window, QUnit ); 586 | 587 | // Expose QUnit object 588 | window.QUnit = QUnit; 589 | } 590 | 591 | // Extend QUnit object, 592 | // these after set here because they should not be exposed as global functions 593 | extend( QUnit, { 594 | config: config, 595 | 596 | // Initialize the configuration options 597 | init: function() { 598 | extend( config, { 599 | stats: { all: 0, bad: 0 }, 600 | moduleStats: { all: 0, bad: 0 }, 601 | started: +new Date(), 602 | updateRate: 1000, 603 | blocking: false, 604 | autostart: true, 605 | autorun: false, 606 | filter: "", 607 | queue: [], 608 | semaphore: 0 609 | }); 610 | 611 | var tests, banner, result, 612 | qunit = id( "qunit" ); 613 | 614 | if ( qunit ) { 615 | qunit.innerHTML = 616 | "

" + escapeInnerText( document.title ) + "

" + 617 | "

" + 618 | "
" + 619 | "

" + 620 | "
    "; 621 | } 622 | 623 | tests = id( "qunit-tests" ); 624 | banner = id( "qunit-banner" ); 625 | result = id( "qunit-testresult" ); 626 | 627 | if ( tests ) { 628 | tests.innerHTML = ""; 629 | } 630 | 631 | if ( banner ) { 632 | banner.className = ""; 633 | } 634 | 635 | if ( result ) { 636 | result.parentNode.removeChild( result ); 637 | } 638 | 639 | if ( tests ) { 640 | result = document.createElement( "p" ); 641 | result.id = "qunit-testresult"; 642 | result.className = "result"; 643 | tests.parentNode.insertBefore( result, tests ); 644 | result.innerHTML = "Running...
     "; 645 | } 646 | }, 647 | 648 | // Resets the test setup. Useful for tests that modify the DOM. 649 | // If jQuery is available, uses jQuery's html(), otherwise just innerHTML. 650 | reset: function() { 651 | var fixture; 652 | 653 | if ( window.jQuery ) { 654 | jQuery( "#qunit-fixture" ).html( config.fixture ); 655 | } else { 656 | fixture = id( "qunit-fixture" ); 657 | if ( fixture ) { 658 | fixture.innerHTML = config.fixture; 659 | } 660 | } 661 | }, 662 | 663 | // Trigger an event on an element. 664 | // @example triggerEvent( document.body, "click" ); 665 | triggerEvent: function( elem, type, event ) { 666 | if ( document.createEvent ) { 667 | event = document.createEvent( "MouseEvents" ); 668 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 669 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 670 | 671 | elem.dispatchEvent( event ); 672 | } else if ( elem.fireEvent ) { 673 | elem.fireEvent( "on" + type ); 674 | } 675 | }, 676 | 677 | // Safe object type checking 678 | is: function( type, obj ) { 679 | return QUnit.objectType( obj ) == type; 680 | }, 681 | 682 | objectType: function( obj ) { 683 | if ( typeof obj === "undefined" ) { 684 | return "undefined"; 685 | // consider: typeof null === object 686 | } 687 | if ( obj === null ) { 688 | return "null"; 689 | } 690 | 691 | var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ""; 692 | 693 | switch ( type ) { 694 | case "Number": 695 | if ( isNaN(obj) ) { 696 | return "nan"; 697 | } 698 | return "number"; 699 | case "String": 700 | case "Boolean": 701 | case "Array": 702 | case "Date": 703 | case "RegExp": 704 | case "Function": 705 | return type.toLowerCase(); 706 | } 707 | if ( typeof obj === "object" ) { 708 | return "object"; 709 | } 710 | return undefined; 711 | }, 712 | 713 | push: function( result, actual, expected, message ) { 714 | if ( !config.current ) { 715 | throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); 716 | } 717 | 718 | var output, source, 719 | details = { 720 | result: result, 721 | message: message, 722 | actual: actual, 723 | expected: expected 724 | }; 725 | 726 | message = escapeInnerText( message ) || ( result ? "okay" : "failed" ); 727 | message = "" + message + ""; 728 | output = message; 729 | 730 | if ( !result ) { 731 | expected = escapeInnerText( QUnit.jsDump.parse(expected) ); 732 | actual = escapeInnerText( QUnit.jsDump.parse(actual) ); 733 | output += ""; 734 | 735 | if ( actual != expected ) { 736 | output += ""; 737 | output += ""; 738 | } 739 | 740 | source = sourceFromStacktrace(); 741 | 742 | if ( source ) { 743 | details.source = source; 744 | output += ""; 745 | } 746 | 747 | output += "
    Expected:
    " + expected + "
    Result:
    " + actual + "
    Diff:
    " + QUnit.diff( expected, actual ) + "
    Source:
    " + escapeInnerText( source ) + "
    "; 748 | } 749 | 750 | runLoggingCallbacks( "log", QUnit, details ); 751 | 752 | config.current.assertions.push({ 753 | result: !!result, 754 | message: output 755 | }); 756 | }, 757 | 758 | pushFailure: function( message, source ) { 759 | var output, 760 | details = { 761 | result: false, 762 | message: message 763 | }; 764 | 765 | message = escapeInnerText(message ) || "error"; 766 | message = "" + message + ""; 767 | output = message; 768 | 769 | if ( source ) { 770 | details.source = source; 771 | output += "
    Source:
    " + escapeInnerText( source ) + "
    "; 772 | } 773 | 774 | runLoggingCallbacks( "log", QUnit, details ); 775 | 776 | config.current.assertions.push({ 777 | result: false, 778 | message: output 779 | }); 780 | }, 781 | 782 | url: function( params ) { 783 | params = extend( extend( {}, QUnit.urlParams ), params ); 784 | var key, 785 | querystring = "?"; 786 | 787 | for ( key in params ) { 788 | if ( !hasOwn.call( params, key ) ) { 789 | continue; 790 | } 791 | querystring += encodeURIComponent( key ) + "=" + 792 | encodeURIComponent( params[ key ] ) + "&"; 793 | } 794 | return window.location.pathname + querystring.slice( 0, -1 ); 795 | }, 796 | 797 | extend: extend, 798 | id: id, 799 | addEvent: addEvent 800 | // load, equiv, jsDump, diff: Attached later 801 | }); 802 | 803 | /** 804 | * @deprecated: Created for backwards compatibility with test runner that set the hook function 805 | * into QUnit.{hook}, instead of invoking it and passing the hook function. 806 | * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. 807 | * Doing this allows us to tell if the following methods have been overwritten on the actual 808 | * QUnit object. 809 | */ 810 | extend( QUnit.constructor.prototype, { 811 | 812 | // Logging callbacks; all receive a single argument with the listed properties 813 | // run test/logs.html for any related changes 814 | begin: registerLoggingCallback( "begin" ), 815 | 816 | // done: { failed, passed, total, runtime } 817 | done: registerLoggingCallback( "done" ), 818 | 819 | // log: { result, actual, expected, message } 820 | log: registerLoggingCallback( "log" ), 821 | 822 | // testStart: { name } 823 | testStart: registerLoggingCallback( "testStart" ), 824 | 825 | // testDone: { name, failed, passed, total } 826 | testDone: registerLoggingCallback( "testDone" ), 827 | 828 | // moduleStart: { name } 829 | moduleStart: registerLoggingCallback( "moduleStart" ), 830 | 831 | // moduleDone: { name, failed, passed, total } 832 | moduleDone: registerLoggingCallback( "moduleDone" ) 833 | }); 834 | 835 | if ( typeof document === "undefined" || document.readyState === "complete" ) { 836 | config.autorun = true; 837 | } 838 | 839 | QUnit.load = function() { 840 | runLoggingCallbacks( "begin", QUnit, {} ); 841 | 842 | // Initialize the config, saving the execution queue 843 | var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, 844 | urlConfigHtml = "", 845 | oldconfig = extend( {}, config ); 846 | 847 | QUnit.init(); 848 | extend(config, oldconfig); 849 | 850 | config.blocking = false; 851 | 852 | len = config.urlConfig.length; 853 | 854 | for ( i = 0; i < len; i++ ) { 855 | val = config.urlConfig[i]; 856 | config[val] = QUnit.urlParams[val]; 857 | urlConfigHtml += ""; 858 | } 859 | 860 | // `userAgent` initialized at top of scope 861 | userAgent = id( "qunit-userAgent" ); 862 | if ( userAgent ) { 863 | userAgent.innerHTML = navigator.userAgent; 864 | } 865 | 866 | // `banner` initialized at top of scope 867 | banner = id( "qunit-header" ); 868 | if ( banner ) { 869 | banner.innerHTML = "" + banner.innerHTML + " " + urlConfigHtml; 870 | addEvent( banner, "change", function( event ) { 871 | var params = {}; 872 | params[ event.target.name ] = event.target.checked ? true : undefined; 873 | window.location = QUnit.url( params ); 874 | }); 875 | } 876 | 877 | // `toolbar` initialized at top of scope 878 | toolbar = id( "qunit-testrunner-toolbar" ); 879 | if ( toolbar ) { 880 | // `filter` initialized at top of scope 881 | filter = document.createElement( "input" ); 882 | filter.type = "checkbox"; 883 | filter.id = "qunit-filter-pass"; 884 | 885 | addEvent( filter, "click", function() { 886 | var tmp, 887 | ol = document.getElementById( "qunit-tests" ); 888 | 889 | if ( filter.checked ) { 890 | ol.className = ol.className + " hidepass"; 891 | } else { 892 | tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; 893 | ol.className = tmp.replace( / hidepass /, " " ); 894 | } 895 | if ( defined.sessionStorage ) { 896 | if (filter.checked) { 897 | sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); 898 | } else { 899 | sessionStorage.removeItem( "qunit-filter-passed-tests" ); 900 | } 901 | } 902 | }); 903 | 904 | if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { 905 | filter.checked = true; 906 | // `ol` initialized at top of scope 907 | ol = document.getElementById( "qunit-tests" ); 908 | ol.className = ol.className + " hidepass"; 909 | } 910 | toolbar.appendChild( filter ); 911 | 912 | // `label` initialized at top of scope 913 | label = document.createElement( "label" ); 914 | label.setAttribute( "for", "qunit-filter-pass" ); 915 | label.innerHTML = "Hide passed tests"; 916 | toolbar.appendChild( label ); 917 | } 918 | 919 | // `main` initialized at top of scope 920 | main = id( "qunit-fixture" ); 921 | if ( main ) { 922 | config.fixture = main.innerHTML; 923 | } 924 | 925 | if ( config.autostart ) { 926 | QUnit.start(); 927 | } 928 | }; 929 | 930 | addEvent( window, "load", QUnit.load ); 931 | 932 | // addEvent(window, "error" ) gives us a useless event object 933 | window.onerror = function( message, file, line ) { 934 | if ( QUnit.config.current ) { 935 | QUnit.pushFailure( message, file + ":" + line ); 936 | } else { 937 | QUnit.test( "global failure", function() { 938 | QUnit.pushFailure( message, file + ":" + line ); 939 | }); 940 | } 941 | }; 942 | 943 | function done() { 944 | config.autorun = true; 945 | 946 | // Log the last module results 947 | if ( config.currentModule ) { 948 | runLoggingCallbacks( "moduleDone", QUnit, { 949 | name: config.currentModule, 950 | failed: config.moduleStats.bad, 951 | passed: config.moduleStats.all - config.moduleStats.bad, 952 | total: config.moduleStats.all 953 | }); 954 | } 955 | 956 | var i, key, 957 | banner = id( "qunit-banner" ), 958 | tests = id( "qunit-tests" ), 959 | runtime = +new Date() - config.started, 960 | passed = config.stats.all - config.stats.bad, 961 | html = [ 962 | "Tests completed in ", 963 | runtime, 964 | " milliseconds.
    ", 965 | "", 966 | passed, 967 | " tests of ", 968 | config.stats.all, 969 | " passed, ", 970 | config.stats.bad, 971 | " failed." 972 | ].join( "" ); 973 | 974 | if ( banner ) { 975 | banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); 976 | } 977 | 978 | if ( tests ) { 979 | id( "qunit-testresult" ).innerHTML = html; 980 | } 981 | 982 | if ( config.altertitle && typeof document !== "undefined" && document.title ) { 983 | // show ✖ for good, ✔ for bad suite result in title 984 | // use escape sequences in case file gets loaded with non-utf-8-charset 985 | document.title = [ 986 | ( config.stats.bad ? "\u2716" : "\u2714" ), 987 | document.title.replace( /^[\u2714\u2716] /i, "" ) 988 | ].join( " " ); 989 | } 990 | 991 | // clear own sessionStorage items if all tests passed 992 | if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { 993 | // `key` & `i` initialized at top of scope 994 | for ( i = 0; i < sessionStorage.length; i++ ) { 995 | key = sessionStorage.key( i++ ); 996 | if ( key.indexOf( "qunit-test-" ) === 0 ) { 997 | sessionStorage.removeItem( key ); 998 | } 999 | } 1000 | } 1001 | 1002 | runLoggingCallbacks( "done", QUnit, { 1003 | failed: config.stats.bad, 1004 | passed: passed, 1005 | total: config.stats.all, 1006 | runtime: runtime 1007 | }); 1008 | } 1009 | 1010 | function validTest( test ) { 1011 | var include, 1012 | filter = config.filter, 1013 | fullName = test.module + ": " + test.testName; 1014 | 1015 | if ( config.testNumber ) { 1016 | return test.testNumber === config.testNumber; 1017 | } 1018 | 1019 | if ( !filter ) { 1020 | return true; 1021 | } 1022 | 1023 | include = filter.charAt( 0 ) !== "!"; 1024 | if ( !include ) { 1025 | filter = filter.slice( 1 ); 1026 | } 1027 | 1028 | // If the filter matches, we need to honour include 1029 | if ( fullName.indexOf( filter ) !== -1 ) { 1030 | return include; 1031 | } 1032 | 1033 | // Otherwise, do the opposite 1034 | return !include; 1035 | } 1036 | 1037 | // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) 1038 | // Later Safari and IE10 are supposed to support error.stack as well 1039 | // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack 1040 | function extractStacktrace( e, offset ) { 1041 | offset = offset || 3; 1042 | 1043 | var stack; 1044 | 1045 | if ( e.stacktrace ) { 1046 | // Opera 1047 | return e.stacktrace.split( "\n" )[ offset + 3 ]; 1048 | } else if ( e.stack ) { 1049 | // Firefox, Chrome 1050 | stack = e.stack.split( "\n" ); 1051 | if (/^error$/i.test( stack[0] ) ) { 1052 | stack.shift(); 1053 | } 1054 | return stack[ offset ]; 1055 | } else if ( e.sourceURL ) { 1056 | // Safari, PhantomJS 1057 | // hopefully one day Safari provides actual stacktraces 1058 | // exclude useless self-reference for generated Error objects 1059 | if ( /qunit.js$/.test( e.sourceURL ) ) { 1060 | return; 1061 | } 1062 | // for actual exceptions, this is useful 1063 | return e.sourceURL + ":" + e.line; 1064 | } 1065 | } 1066 | function sourceFromStacktrace( offset ) { 1067 | try { 1068 | throw new Error(); 1069 | } catch ( e ) { 1070 | return extractStacktrace( e, offset ); 1071 | } 1072 | } 1073 | 1074 | function escapeInnerText( s ) { 1075 | if ( !s ) { 1076 | return ""; 1077 | } 1078 | s = s + ""; 1079 | return s.replace( /[\&<>]/g, function( s ) { 1080 | switch( s ) { 1081 | case "&": return "&"; 1082 | case "<": return "<"; 1083 | case ">": return ">"; 1084 | default: return s; 1085 | } 1086 | }); 1087 | } 1088 | 1089 | function synchronize( callback, last ) { 1090 | config.queue.push( callback ); 1091 | 1092 | if ( config.autorun && !config.blocking ) { 1093 | process( last ); 1094 | } 1095 | } 1096 | 1097 | function process( last ) { 1098 | function next() { 1099 | process( last ); 1100 | } 1101 | var start = new Date().getTime(); 1102 | config.depth = config.depth ? config.depth + 1 : 1; 1103 | 1104 | while ( config.queue.length && !config.blocking ) { 1105 | if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { 1106 | config.queue.shift()(); 1107 | } else { 1108 | window.setTimeout( next, 13 ); 1109 | break; 1110 | } 1111 | } 1112 | config.depth--; 1113 | if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { 1114 | done(); 1115 | } 1116 | } 1117 | 1118 | function saveGlobal() { 1119 | config.pollution = []; 1120 | 1121 | if ( config.noglobals ) { 1122 | for ( var key in window ) { 1123 | // in Opera sometimes DOM element ids show up here, ignore them 1124 | if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) { 1125 | continue; 1126 | } 1127 | config.pollution.push( key ); 1128 | } 1129 | } 1130 | } 1131 | 1132 | function checkPollution( name ) { 1133 | var newGlobals, 1134 | deletedGlobals, 1135 | old = config.pollution; 1136 | 1137 | saveGlobal(); 1138 | 1139 | newGlobals = diff( config.pollution, old ); 1140 | if ( newGlobals.length > 0 ) { 1141 | QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); 1142 | } 1143 | 1144 | deletedGlobals = diff( old, config.pollution ); 1145 | if ( deletedGlobals.length > 0 ) { 1146 | QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); 1147 | } 1148 | } 1149 | 1150 | // returns a new Array with the elements that are in a but not in b 1151 | function diff( a, b ) { 1152 | var i, j, 1153 | result = a.slice(); 1154 | 1155 | for ( i = 0; i < result.length; i++ ) { 1156 | for ( j = 0; j < b.length; j++ ) { 1157 | if ( result[i] === b[j] ) { 1158 | result.splice( i, 1 ); 1159 | i--; 1160 | break; 1161 | } 1162 | } 1163 | } 1164 | return result; 1165 | } 1166 | 1167 | function extend( a, b ) { 1168 | for ( var prop in b ) { 1169 | if ( b[ prop ] === undefined ) { 1170 | delete a[ prop ]; 1171 | 1172 | // Avoid "Member not found" error in IE8 caused by setting window.constructor 1173 | } else if ( prop !== "constructor" || a !== window ) { 1174 | a[ prop ] = b[ prop ]; 1175 | } 1176 | } 1177 | 1178 | return a; 1179 | } 1180 | 1181 | function addEvent( elem, type, fn ) { 1182 | if ( elem.addEventListener ) { 1183 | elem.addEventListener( type, fn, false ); 1184 | } else if ( elem.attachEvent ) { 1185 | elem.attachEvent( "on" + type, fn ); 1186 | } else { 1187 | fn(); 1188 | } 1189 | } 1190 | 1191 | function id( name ) { 1192 | return !!( typeof document !== "undefined" && document && document.getElementById ) && 1193 | document.getElementById( name ); 1194 | } 1195 | 1196 | function registerLoggingCallback( key ) { 1197 | return function( callback ) { 1198 | config[key].push( callback ); 1199 | }; 1200 | } 1201 | 1202 | // Supports deprecated method of completely overwriting logging callbacks 1203 | function runLoggingCallbacks( key, scope, args ) { 1204 | //debugger; 1205 | var i, callbacks; 1206 | if ( QUnit.hasOwnProperty( key ) ) { 1207 | QUnit[ key ].call(scope, args ); 1208 | } else { 1209 | callbacks = config[ key ]; 1210 | for ( i = 0; i < callbacks.length; i++ ) { 1211 | callbacks[ i ].call( scope, args ); 1212 | } 1213 | } 1214 | } 1215 | 1216 | // Test for equality any JavaScript type. 1217 | // Author: Philippe Rathé 1218 | QUnit.equiv = (function() { 1219 | 1220 | // Call the o related callback with the given arguments. 1221 | function bindCallbacks( o, callbacks, args ) { 1222 | var prop = QUnit.objectType( o ); 1223 | if ( prop ) { 1224 | if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { 1225 | return callbacks[ prop ].apply( callbacks, args ); 1226 | } else { 1227 | return callbacks[ prop ]; // or undefined 1228 | } 1229 | } 1230 | } 1231 | 1232 | // the real equiv function 1233 | var innerEquiv, 1234 | // stack to decide between skip/abort functions 1235 | callers = [], 1236 | // stack to avoiding loops from circular referencing 1237 | parents = [], 1238 | 1239 | getProto = Object.getPrototypeOf || function ( obj ) { 1240 | return obj.__proto__; 1241 | }, 1242 | callbacks = (function () { 1243 | 1244 | // for string, boolean, number and null 1245 | function useStrictEquality( b, a ) { 1246 | if ( b instanceof a.constructor || a instanceof b.constructor ) { 1247 | // to catch short annotaion VS 'new' annotation of a 1248 | // declaration 1249 | // e.g. var i = 1; 1250 | // var j = new Number(1); 1251 | return a == b; 1252 | } else { 1253 | return a === b; 1254 | } 1255 | } 1256 | 1257 | return { 1258 | "string": useStrictEquality, 1259 | "boolean": useStrictEquality, 1260 | "number": useStrictEquality, 1261 | "null": useStrictEquality, 1262 | "undefined": useStrictEquality, 1263 | 1264 | "nan": function( b ) { 1265 | return isNaN( b ); 1266 | }, 1267 | 1268 | "date": function( b, a ) { 1269 | return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); 1270 | }, 1271 | 1272 | "regexp": function( b, a ) { 1273 | return QUnit.objectType( b ) === "regexp" && 1274 | // the regex itself 1275 | a.source === b.source && 1276 | // and its modifers 1277 | a.global === b.global && 1278 | // (gmi) ... 1279 | a.ignoreCase === b.ignoreCase && 1280 | a.multiline === b.multiline; 1281 | }, 1282 | 1283 | // - skip when the property is a method of an instance (OOP) 1284 | // - abort otherwise, 1285 | // initial === would have catch identical references anyway 1286 | "function": function() { 1287 | var caller = callers[callers.length - 1]; 1288 | return caller !== Object && typeof caller !== "undefined"; 1289 | }, 1290 | 1291 | "array": function( b, a ) { 1292 | var i, j, len, loop; 1293 | 1294 | // b could be an object literal here 1295 | if ( QUnit.objectType( b ) !== "array" ) { 1296 | return false; 1297 | } 1298 | 1299 | len = a.length; 1300 | if ( len !== b.length ) { 1301 | // safe and faster 1302 | return false; 1303 | } 1304 | 1305 | // track reference to avoid circular references 1306 | parents.push( a ); 1307 | for ( i = 0; i < len; i++ ) { 1308 | loop = false; 1309 | for ( j = 0; j < parents.length; j++ ) { 1310 | if ( parents[j] === a[i] ) { 1311 | loop = true;// dont rewalk array 1312 | } 1313 | } 1314 | if ( !loop && !innerEquiv(a[i], b[i]) ) { 1315 | parents.pop(); 1316 | return false; 1317 | } 1318 | } 1319 | parents.pop(); 1320 | return true; 1321 | }, 1322 | 1323 | "object": function( b, a ) { 1324 | var i, j, loop, 1325 | // Default to true 1326 | eq = true, 1327 | aProperties = [], 1328 | bProperties = []; 1329 | 1330 | // comparing constructors is more strict than using 1331 | // instanceof 1332 | if ( a.constructor !== b.constructor ) { 1333 | // Allow objects with no prototype to be equivalent to 1334 | // objects with Object as their constructor. 1335 | if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || 1336 | ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { 1337 | return false; 1338 | } 1339 | } 1340 | 1341 | // stack constructor before traversing properties 1342 | callers.push( a.constructor ); 1343 | // track reference to avoid circular references 1344 | parents.push( a ); 1345 | 1346 | for ( i in a ) { // be strict: don't ensures hasOwnProperty 1347 | // and go deep 1348 | loop = false; 1349 | for ( j = 0; j < parents.length; j++ ) { 1350 | if ( parents[j] === a[i] ) { 1351 | // don't go down the same path twice 1352 | loop = true; 1353 | } 1354 | } 1355 | aProperties.push(i); // collect a's properties 1356 | 1357 | if (!loop && !innerEquiv( a[i], b[i] ) ) { 1358 | eq = false; 1359 | break; 1360 | } 1361 | } 1362 | 1363 | callers.pop(); // unstack, we are done 1364 | parents.pop(); 1365 | 1366 | for ( i in b ) { 1367 | bProperties.push( i ); // collect b's properties 1368 | } 1369 | 1370 | // Ensures identical properties name 1371 | return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); 1372 | } 1373 | }; 1374 | }()); 1375 | 1376 | innerEquiv = function() { // can take multiple arguments 1377 | var args = [].slice.apply( arguments ); 1378 | if ( args.length < 2 ) { 1379 | return true; // end transition 1380 | } 1381 | 1382 | return (function( a, b ) { 1383 | if ( a === b ) { 1384 | return true; // catch the most you can 1385 | } else if ( a === null || b === null || typeof a === "undefined" || 1386 | typeof b === "undefined" || 1387 | QUnit.objectType(a) !== QUnit.objectType(b) ) { 1388 | return false; // don't lose time with error prone cases 1389 | } else { 1390 | return bindCallbacks(a, callbacks, [ b, a ]); 1391 | } 1392 | 1393 | // apply transition with (1..n) arguments 1394 | }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) ); 1395 | }; 1396 | 1397 | return innerEquiv; 1398 | }()); 1399 | 1400 | /** 1401 | * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | 1402 | * http://flesler.blogspot.com Licensed under BSD 1403 | * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 1404 | * 1405 | * @projectDescription Advanced and extensible data dumping for Javascript. 1406 | * @version 1.0.0 1407 | * @author Ariel Flesler 1408 | * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} 1409 | */ 1410 | QUnit.jsDump = (function() { 1411 | function quote( str ) { 1412 | return '"' + str.toString().replace( /"/g, '\\"' ) + '"'; 1413 | } 1414 | function literal( o ) { 1415 | return o + ""; 1416 | } 1417 | function join( pre, arr, post ) { 1418 | var s = jsDump.separator(), 1419 | base = jsDump.indent(), 1420 | inner = jsDump.indent(1); 1421 | if ( arr.join ) { 1422 | arr = arr.join( "," + s + inner ); 1423 | } 1424 | if ( !arr ) { 1425 | return pre + post; 1426 | } 1427 | return [ pre, inner + arr, base + post ].join(s); 1428 | } 1429 | function array( arr, stack ) { 1430 | var i = arr.length, ret = new Array(i); 1431 | this.up(); 1432 | while ( i-- ) { 1433 | ret[i] = this.parse( arr[i] , undefined , stack); 1434 | } 1435 | this.down(); 1436 | return join( "[", ret, "]" ); 1437 | } 1438 | 1439 | var reName = /^function (\w+)/, 1440 | jsDump = { 1441 | parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance 1442 | stack = stack || [ ]; 1443 | var inStack, res, 1444 | parser = this.parsers[ type || this.typeOf(obj) ]; 1445 | 1446 | type = typeof parser; 1447 | inStack = inArray( obj, stack ); 1448 | 1449 | if ( inStack != -1 ) { 1450 | return "recursion(" + (inStack - stack.length) + ")"; 1451 | } 1452 | //else 1453 | if ( type == "function" ) { 1454 | stack.push( obj ); 1455 | res = parser.call( this, obj, stack ); 1456 | stack.pop(); 1457 | return res; 1458 | } 1459 | // else 1460 | return ( type == "string" ) ? parser : this.parsers.error; 1461 | }, 1462 | typeOf: function( obj ) { 1463 | var type; 1464 | if ( obj === null ) { 1465 | type = "null"; 1466 | } else if ( typeof obj === "undefined" ) { 1467 | type = "undefined"; 1468 | } else if ( QUnit.is( "RegExp", obj) ) { 1469 | type = "regexp"; 1470 | } else if ( QUnit.is( "Date", obj) ) { 1471 | type = "date"; 1472 | } else if ( QUnit.is( "Function", obj) ) { 1473 | type = "function"; 1474 | } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { 1475 | type = "window"; 1476 | } else if ( obj.nodeType === 9 ) { 1477 | type = "document"; 1478 | } else if ( obj.nodeType ) { 1479 | type = "node"; 1480 | } else if ( 1481 | // native arrays 1482 | toString.call( obj ) === "[object Array]" || 1483 | // NodeList objects 1484 | ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) 1485 | ) { 1486 | type = "array"; 1487 | } else { 1488 | type = typeof obj; 1489 | } 1490 | return type; 1491 | }, 1492 | separator: function() { 1493 | return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; 1494 | }, 1495 | indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing 1496 | if ( !this.multiline ) { 1497 | return ""; 1498 | } 1499 | var chr = this.indentChar; 1500 | if ( this.HTML ) { 1501 | chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); 1502 | } 1503 | return new Array( this._depth_ + (extra||0) ).join(chr); 1504 | }, 1505 | up: function( a ) { 1506 | this._depth_ += a || 1; 1507 | }, 1508 | down: function( a ) { 1509 | this._depth_ -= a || 1; 1510 | }, 1511 | setParser: function( name, parser ) { 1512 | this.parsers[name] = parser; 1513 | }, 1514 | // The next 3 are exposed so you can use them 1515 | quote: quote, 1516 | literal: literal, 1517 | join: join, 1518 | // 1519 | _depth_: 1, 1520 | // This is the list of parsers, to modify them, use jsDump.setParser 1521 | parsers: { 1522 | window: "[Window]", 1523 | document: "[Document]", 1524 | error: "[ERROR]", //when no parser is found, shouldn"t happen 1525 | unknown: "[Unknown]", 1526 | "null": "null", 1527 | "undefined": "undefined", 1528 | "function": function( fn ) { 1529 | var ret = "function", 1530 | name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE 1531 | 1532 | if ( name ) { 1533 | ret += " " + name; 1534 | } 1535 | ret += "( "; 1536 | 1537 | ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); 1538 | return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); 1539 | }, 1540 | array: array, 1541 | nodelist: array, 1542 | "arguments": array, 1543 | object: function( map, stack ) { 1544 | var ret = [ ], keys, key, val, i; 1545 | QUnit.jsDump.up(); 1546 | if ( Object.keys ) { 1547 | keys = Object.keys( map ); 1548 | } else { 1549 | keys = []; 1550 | for ( key in map ) { 1551 | keys.push( key ); 1552 | } 1553 | } 1554 | keys.sort(); 1555 | for ( i = 0; i < keys.length; i++ ) { 1556 | key = keys[ i ]; 1557 | val = map[ key ]; 1558 | ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); 1559 | } 1560 | QUnit.jsDump.down(); 1561 | return join( "{", ret, "}" ); 1562 | }, 1563 | node: function( node ) { 1564 | var a, val, 1565 | open = QUnit.jsDump.HTML ? "<" : "<", 1566 | close = QUnit.jsDump.HTML ? ">" : ">", 1567 | tag = node.nodeName.toLowerCase(), 1568 | ret = open + tag; 1569 | 1570 | for ( a in QUnit.jsDump.DOMAttrs ) { 1571 | val = node[ QUnit.jsDump.DOMAttrs[a] ]; 1572 | if ( val ) { 1573 | ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" ); 1574 | } 1575 | } 1576 | return ret + close + open + "/" + tag + close; 1577 | }, 1578 | functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function 1579 | var args, 1580 | l = fn.length; 1581 | 1582 | if ( !l ) { 1583 | return ""; 1584 | } 1585 | 1586 | args = new Array(l); 1587 | while ( l-- ) { 1588 | args[l] = String.fromCharCode(97+l);//97 is 'a' 1589 | } 1590 | return " " + args.join( ", " ) + " "; 1591 | }, 1592 | key: quote, //object calls it internally, the key part of an item in a map 1593 | functionCode: "[code]", //function calls it internally, it's the content of the function 1594 | attribute: quote, //node calls it internally, it's an html attribute value 1595 | string: quote, 1596 | date: quote, 1597 | regexp: literal, //regex 1598 | number: literal, 1599 | "boolean": literal 1600 | }, 1601 | DOMAttrs: { 1602 | //attributes to dump from nodes, name=>realName 1603 | id: "id", 1604 | name: "name", 1605 | "class": "className" 1606 | }, 1607 | HTML: false,//if true, entities are escaped ( <, >, \t, space and \n ) 1608 | indentChar: " ",//indentation unit 1609 | multiline: true //if true, items in a collection, are separated by a \n, else just a space. 1610 | }; 1611 | 1612 | return jsDump; 1613 | }()); 1614 | 1615 | // from Sizzle.js 1616 | function getText( elems ) { 1617 | var i, elem, 1618 | ret = ""; 1619 | 1620 | for ( i = 0; elems[i]; i++ ) { 1621 | elem = elems[i]; 1622 | 1623 | // Get the text from text nodes and CDATA nodes 1624 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 1625 | ret += elem.nodeValue; 1626 | 1627 | // Traverse everything else, except comment nodes 1628 | } else if ( elem.nodeType !== 8 ) { 1629 | ret += getText( elem.childNodes ); 1630 | } 1631 | } 1632 | 1633 | return ret; 1634 | } 1635 | 1636 | // from jquery.js 1637 | function inArray( elem, array ) { 1638 | if ( array.indexOf ) { 1639 | return array.indexOf( elem ); 1640 | } 1641 | 1642 | for ( var i = 0, length = array.length; i < length; i++ ) { 1643 | if ( array[ i ] === elem ) { 1644 | return i; 1645 | } 1646 | } 1647 | 1648 | return -1; 1649 | } 1650 | 1651 | /* 1652 | * Javascript Diff Algorithm 1653 | * By John Resig (http://ejohn.org/) 1654 | * Modified by Chu Alan "sprite" 1655 | * 1656 | * Released under the MIT license. 1657 | * 1658 | * More Info: 1659 | * http://ejohn.org/projects/javascript-diff-algorithm/ 1660 | * 1661 | * Usage: QUnit.diff(expected, actual) 1662 | * 1663 | * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" 1664 | */ 1665 | QUnit.diff = (function() { 1666 | function diff( o, n ) { 1667 | var i, 1668 | ns = {}, 1669 | os = {}; 1670 | 1671 | for ( i = 0; i < n.length; i++ ) { 1672 | if ( ns[ n[i] ] == null ) { 1673 | ns[ n[i] ] = { 1674 | rows: [], 1675 | o: null 1676 | }; 1677 | } 1678 | ns[ n[i] ].rows.push( i ); 1679 | } 1680 | 1681 | for ( i = 0; i < o.length; i++ ) { 1682 | if ( os[ o[i] ] == null ) { 1683 | os[ o[i] ] = { 1684 | rows: [], 1685 | n: null 1686 | }; 1687 | } 1688 | os[ o[i] ].rows.push( i ); 1689 | } 1690 | 1691 | for ( i in ns ) { 1692 | if ( !hasOwn.call( ns, i ) ) { 1693 | continue; 1694 | } 1695 | if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) { 1696 | n[ ns[i].rows[0] ] = { 1697 | text: n[ ns[i].rows[0] ], 1698 | row: os[i].rows[0] 1699 | }; 1700 | o[ os[i].rows[0] ] = { 1701 | text: o[ os[i].rows[0] ], 1702 | row: ns[i].rows[0] 1703 | }; 1704 | } 1705 | } 1706 | 1707 | for ( i = 0; i < n.length - 1; i++ ) { 1708 | if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && 1709 | n[ i + 1 ] == o[ n[i].row + 1 ] ) { 1710 | 1711 | n[ i + 1 ] = { 1712 | text: n[ i + 1 ], 1713 | row: n[i].row + 1 1714 | }; 1715 | o[ n[i].row + 1 ] = { 1716 | text: o[ n[i].row + 1 ], 1717 | row: i + 1 1718 | }; 1719 | } 1720 | } 1721 | 1722 | for ( i = n.length - 1; i > 0; i-- ) { 1723 | if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && 1724 | n[ i - 1 ] == o[ n[i].row - 1 ]) { 1725 | 1726 | n[ i - 1 ] = { 1727 | text: n[ i - 1 ], 1728 | row: n[i].row - 1 1729 | }; 1730 | o[ n[i].row - 1 ] = { 1731 | text: o[ n[i].row - 1 ], 1732 | row: i - 1 1733 | }; 1734 | } 1735 | } 1736 | 1737 | return { 1738 | o: o, 1739 | n: n 1740 | }; 1741 | } 1742 | 1743 | return function( o, n ) { 1744 | o = o.replace( /\s+$/, "" ); 1745 | n = n.replace( /\s+$/, "" ); 1746 | 1747 | var i, pre, 1748 | str = "", 1749 | out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), 1750 | oSpace = o.match(/\s+/g), 1751 | nSpace = n.match(/\s+/g); 1752 | 1753 | if ( oSpace == null ) { 1754 | oSpace = [ " " ]; 1755 | } 1756 | else { 1757 | oSpace.push( " " ); 1758 | } 1759 | 1760 | if ( nSpace == null ) { 1761 | nSpace = [ " " ]; 1762 | } 1763 | else { 1764 | nSpace.push( " " ); 1765 | } 1766 | 1767 | if ( out.n.length === 0 ) { 1768 | for ( i = 0; i < out.o.length; i++ ) { 1769 | str += "" + out.o[i] + oSpace[i] + ""; 1770 | } 1771 | } 1772 | else { 1773 | if ( out.n[0].text == null ) { 1774 | for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { 1775 | str += "" + out.o[n] + oSpace[n] + ""; 1776 | } 1777 | } 1778 | 1779 | for ( i = 0; i < out.n.length; i++ ) { 1780 | if (out.n[i].text == null) { 1781 | str += "" + out.n[i] + nSpace[i] + ""; 1782 | } 1783 | else { 1784 | // `pre` initialized at top of scope 1785 | pre = ""; 1786 | 1787 | for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { 1788 | pre += "" + out.o[n] + oSpace[n] + ""; 1789 | } 1790 | str += " " + out.n[i].text + nSpace[i] + pre; 1791 | } 1792 | } 1793 | } 1794 | 1795 | return str; 1796 | }; 1797 | }()); 1798 | 1799 | // for CommonJS enviroments, export everything 1800 | if ( typeof exports !== "undefined" ) { 1801 | extend(exports, QUnit); 1802 | } 1803 | 1804 | // get at whatever the global object is, like window in browsers 1805 | }( (function() {return this;}.call()) )); 1806 | --------------------------------------------------------------------------------