mirror of
https://github.com/zhigang1992/bootstrap-tokenfield.git
synced 2026-01-12 17:12:44 +08:00
Re-factor tests to run on node
This commit is contained in:
18
bower.json
18
bower.json
@@ -3,10 +3,16 @@
|
||||
"description": "Advanced tagging/tokenizing plugin for input fields with a focus on keyboard and copy-paste support.",
|
||||
"version": "0.11.0",
|
||||
"authors": [
|
||||
{ "name": "ragulka", "homepage": "https://github.com/ragulka" },
|
||||
{ "name": "sliptree", "homepage": "https://github.com/sliptree" }
|
||||
{
|
||||
"name": "ragulka",
|
||||
"homepage": "https://github.com/ragulka"
|
||||
},
|
||||
{
|
||||
"name": "sliptree",
|
||||
"homepage": "https://github.com/sliptree"
|
||||
}
|
||||
],
|
||||
"homepage": "http://sliptree.github.io/bootstrap-tokenfield/",
|
||||
"homepage": "http://sliptree.github.io/bootstrap-tokenfield/",
|
||||
"main": [
|
||||
"dist/bootstrap-tokenfield.js",
|
||||
"dist/bootstrap-tokenfield.css"
|
||||
@@ -20,8 +26,8 @@
|
||||
"token",
|
||||
"input",
|
||||
"select",
|
||||
"form"
|
||||
],
|
||||
"form"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "~2.1.0",
|
||||
"bootstrap": "~3.1.1"
|
||||
@@ -31,4 +37,4 @@
|
||||
"chai": "~1.7.*",
|
||||
"typeahead.js": "~0.10.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
js/bootstrap-tokenfield.js
vendored
21
js/bootstrap-tokenfield.js
vendored
@@ -10,13 +10,26 @@
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node/CommonJS
|
||||
factory(require('jquery'));
|
||||
// For CommonJS and CommonJS-like environments where a proper window is present,
|
||||
// execute the factory and get jQuery
|
||||
// For environments that do not inherently posses a window with a document
|
||||
// (such as Node.js), expose a jQuery-making factory as module.exports
|
||||
// This accentuates the need for the creation of a real window
|
||||
// e.g. var jQuery = require("jquery")(window);
|
||||
// See ticket #14549 for more info
|
||||
module.exports = global.window && global.window.$ ?
|
||||
factory( global.window.$ ) :
|
||||
function( input ) {
|
||||
if ( !input.$ && !input.fn ) {
|
||||
throw new Error( "Tokenfield requires a window object with jQuery or a jQuery instance" );
|
||||
}
|
||||
return factory( input.$ || input );
|
||||
};
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
}(function ($, window) {
|
||||
|
||||
"use strict"; // jshint ;_;
|
||||
|
||||
@@ -58,7 +71,7 @@
|
||||
});
|
||||
|
||||
// Store original input width
|
||||
var elRules = (typeof window.getMatchedCSSRules === 'function') ? window.getMatchedCSSRules( element ) : null
|
||||
var elRules = (window && typeof window.getMatchedCSSRules === 'function') ? window.getMatchedCSSRules( element ) : null
|
||||
, elStyleWidth = element.style.width
|
||||
, elCSSWidth
|
||||
, elWidth = this.$element.width()
|
||||
|
||||
10
package.json
10
package.json
@@ -33,9 +33,11 @@
|
||||
"url": "https://github.com/jquery/jquery/blob/2.1.0/MIT-LICENSE.txt"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"test": "mocha --ui bdd --recursive --reporter spec --require must"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-less": "~0.1.7",
|
||||
"grunt-contrib-less": "~0.8.3",
|
||||
"grunt-contrib-uglify": "~0.2.7",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
@@ -43,6 +45,10 @@
|
||||
"grunt-jekyll": "~0.4.1",
|
||||
"mocha": "^1.17.1",
|
||||
"must": "^0.11.0",
|
||||
"jsdom": "^0.10.1"
|
||||
"jsdom": "^0.10.1",
|
||||
"jquery-simulate-ext": "git://github.com/j-ulrich/jquery-simulate-ext"
|
||||
},
|
||||
"dependencies": {
|
||||
"jquery": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
79
test/jsdom-patch.js
Normal file
79
test/jsdom-patch.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Patch from zombie.js for jsdom https://github.com/assaf/zombie/blob/master/src/zombie/dom_focus.coffee
|
||||
*
|
||||
* Adds focus() and blur() methods and events to dom elements
|
||||
*/
|
||||
|
||||
var FOCUS_ELEMENTS, HTML, elementType, setAttribute, setFocus, _i, _j, _len, _len1, _ref, _ref1;
|
||||
|
||||
HTML = require("jsdom").dom.level3.html;
|
||||
|
||||
FOCUS_ELEMENTS = ["INPUT", "SELECT", "TEXTAREA", "BUTTON", "ANCHOR"];
|
||||
|
||||
HTML.HTMLDocument.prototype.__defineGetter__("activeElement", function() {
|
||||
return this._inFocus || this.body;
|
||||
});
|
||||
|
||||
setFocus = function(document, element) {
|
||||
var inFocus, onblur, onfocus;
|
||||
inFocus = document._inFocus;
|
||||
if (element !== inFocus) {
|
||||
if (inFocus) {
|
||||
onblur = document.createEvent("HTMLEvents");
|
||||
onblur.initEvent("blur", false, false);
|
||||
inFocus.dispatchEvent(onblur);
|
||||
}
|
||||
if (element) {
|
||||
onfocus = document.createEvent("HTMLEvents");
|
||||
onfocus.initEvent("focus", false, false);
|
||||
element.dispatchEvent(onfocus);
|
||||
document._inFocus = element;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HTML.HTMLElement.prototype.focus = function() {};
|
||||
|
||||
HTML.HTMLElement.prototype.blur = function() {};
|
||||
|
||||
_ref = [HTML.HTMLInputElement, HTML.HTMLSelectElement, HTML.HTMLTextAreaElement, HTML.HTMLButtonElement, HTML.HTMLAnchorElement];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
elementType = _ref[_i];
|
||||
elementType.prototype.focus = function() {
|
||||
return setFocus(this.ownerDocument, this);
|
||||
};
|
||||
elementType.prototype.blur = function() {
|
||||
return setFocus(this.ownerDocument, null);
|
||||
};
|
||||
setAttribute = elementType.prototype.setAttribute;
|
||||
elementType.prototype.setAttribute = function(name, value) {
|
||||
var document;
|
||||
setAttribute.call(this, name, value);
|
||||
if (name === "autofocus") {
|
||||
document = this.ownerDocument;
|
||||
if (~FOCUS_ELEMENTS.indexOf(this.tagName) && !document._inFocus) {
|
||||
return this.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_ref1 = [HTML.HTMLInputElement, HTML.HTMLTextAreaElement, HTML.HTMLSelectElement];
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
elementType = _ref1[_j];
|
||||
elementType.prototype._eventDefaults.focus = function(event) {
|
||||
var element;
|
||||
element = event.target;
|
||||
return element._focusValue = element.value || '';
|
||||
};
|
||||
elementType.prototype._eventDefaults.blur = function(event) {
|
||||
var change, element, focusValue;
|
||||
element = event.target;
|
||||
focusValue = element._focusValue;
|
||||
if (focusValue !== element.value) {
|
||||
change = element.ownerDocument.createEvent("HTMLEvents");
|
||||
change.initEvent("change", false, false);
|
||||
return element.dispatchEvent(change);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,38 @@
|
||||
// Global tokenfield test object
|
||||
var TFT = {};
|
||||
var jsdom = require('jsdom');
|
||||
|
||||
require('./jsdom-patch');
|
||||
|
||||
before(function (done) {
|
||||
jsdom.env({
|
||||
html: '<html><body></body></html>',
|
||||
done: function (err, window) {
|
||||
|
||||
// Set clientTop and clientLeft to 0 so that offset() works
|
||||
window.document.documentElement.clientTop = 0;
|
||||
window.document.documentElement.clientLeft = 0;
|
||||
|
||||
// Expose jQuery and require tokenfield
|
||||
window.$ = global.$ = global.jQuery = require('jquery')(window);
|
||||
require('../js/bootstrap-tokenfield')(window);
|
||||
|
||||
// Globalize window, document, navigator
|
||||
global.window = window;
|
||||
global.document = window.document;
|
||||
global.navigator = window.navigator;
|
||||
|
||||
// Provide a focus method on DOM elements if it does not exist.
|
||||
// Helps to avoid issues with the simulate-ext plugin
|
||||
window.HTMLDivElement.prototype.focus = window.HTMLDivElement.prototype.focus || function() {};
|
||||
|
||||
// Global configuration object for our tests
|
||||
global.TFT = window.TFT = {};
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Global tokenfield test object
|
||||
beforeEach(function() {
|
||||
var template = TFT.template || '<input type="text" class="tokenize" value="" />',
|
||||
options = TFT.options || null;
|
||||
@@ -15,6 +47,10 @@ beforeEach(function() {
|
||||
this.$input = this.$field.data('bs.tokenfield').$input;
|
||||
this.$wrapper = this.$field.data('bs.tokenfield').$wrapper;
|
||||
this.$copyHelper = this.$field.data('bs.tokenfield').$copyHelper;
|
||||
|
||||
// Set an initial empty value for input (bypasses bililiteRange error)
|
||||
this.$input.val('');
|
||||
this.$copyHelper.val('');
|
||||
});
|
||||
|
||||
afterEach( function() {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
describe('Integration', function() {
|
||||
|
||||
before(function() {
|
||||
require('../node_modules/jquery-simulate-ext/libs/bililiteRange');
|
||||
require('../node_modules/jquery-simulate-ext/libs/jquery.simulate');
|
||||
require('../node_modules/jquery-simulate-ext/src/jquery.simulate.ext');
|
||||
require('../node_modules/jquery-simulate-ext/src/jquery.simulate.key-combo');
|
||||
require('../node_modules/jquery-simulate-ext/src/jquery.simulate.key-sequence');
|
||||
});
|
||||
|
||||
describe('Using an alternative delimiter', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red;green;blue;yellow" />'
|
||||
@@ -13,14 +21,14 @@ describe('Integration', function() {
|
||||
delete TFT.options;
|
||||
});
|
||||
|
||||
it('should create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.should.equal(4);
|
||||
it('must create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.must.equal(4);
|
||||
});
|
||||
|
||||
it('should create a token when the delimiting key is pressed and use the first delimiter for setting original input value', function() {
|
||||
it('must create a token when the delimiting key is pressed and use the first delimiter for setting original input value', function() {
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "purple;olive;" })
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.should.equal(6);
|
||||
this.$field.val().should.equal('red; green; blue; yellow; purple; olive');
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.must.equal(6);
|
||||
this.$field.val().must.equal('red; green; blue; yellow; purple; olive');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -38,14 +46,14 @@ describe('Integration', function() {
|
||||
delete TFT.options;
|
||||
});
|
||||
|
||||
it('should create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.should.equal(4);
|
||||
it('must create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.must.equal(4);
|
||||
});
|
||||
|
||||
it('should create a token when the delimiting key is pressed and use the first delimiter for setting original input value', function() {
|
||||
it('must create a token when the delimiting key is pressed and use the first delimiter for setting original input value', function() {
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "purple olive." });
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.should.equal(6);
|
||||
this.$field.val().should.equal('red green blue yellow purple olive');
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.must.equal(6);
|
||||
this.$field.val().must.equal('red green blue yellow purple olive');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,8 +70,8 @@ describe('Integration', function() {
|
||||
delete TFT.options;
|
||||
});
|
||||
|
||||
it('should create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.should.equal(5);
|
||||
it('must create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.must.equal(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,8 +88,8 @@ describe('Integration', function() {
|
||||
delete TFT.options;
|
||||
});
|
||||
|
||||
it('should create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.should.equal(13);
|
||||
it('must create tokens by splitting the original value with delimiters', function() {
|
||||
this.$field.data('bs.tokenfield').$wrapper.find('.token').length.must.equal(13);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -97,9 +105,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should select all tokens", function() {
|
||||
it("must select all tokens", function() {
|
||||
this.$input.focus().simulate("key-combo", { combo: "ctrl+a" });
|
||||
this.$field.tokenfield('getTokens', true).length.should.equal(3);
|
||||
this.$field.tokenfield('getTokens', true).length.must.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,9 +120,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should select all tokens", function() {
|
||||
it("must select all tokens", function() {
|
||||
this.$input.focus().simulate("key-combo", { combo: "meta+a" });
|
||||
this.$field.tokenfield('getTokens', true).length.should.equal(3);
|
||||
this.$field.tokenfield('getTokens', true).length.must.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -129,27 +137,27 @@ describe('Integration', function() {
|
||||
});
|
||||
|
||||
describe('and there are more tokens to the right of selected token', function() {
|
||||
it("should delete the selected token and move focus to the next token", function() {
|
||||
it("must delete the selected token and move focus to the next token", function() {
|
||||
// Mark green as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{del}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).should.equal('blue');
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).must.equal('blue');
|
||||
});
|
||||
})
|
||||
|
||||
describe('and there are no more tokens to the right of the selected token', function() {
|
||||
it("should delete the selected token and move focus to input", function() {
|
||||
it("must delete the selected token and move focus to input", function() {
|
||||
// Mark green as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{del}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).should.equal('');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).must.equal('');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -164,7 +172,7 @@ describe('Integration', function() {
|
||||
});
|
||||
|
||||
describe('and there are more tokens to the right of selected tokens', function() {
|
||||
it("should delete the selected tokens and move focus to the next token of the rightmost selected token", function() {
|
||||
it("must delete the selected tokens and move focus to the next token of the rightmost selected token", function() {
|
||||
// Mark green and yellow as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
@@ -172,13 +180,13 @@ describe('Integration', function() {
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{del}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).should.equal('purple');
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).must.equal('purple');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and there are no more tokens to the right of selected tokens', function() {
|
||||
it("should delete the selected tokens and move focus input", function() {
|
||||
it("must delete the selected tokens and move focus input", function() {
|
||||
// Mark green and yellow as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
@@ -186,9 +194,9 @@ describe('Integration', function() {
|
||||
.filter(':has(.token-label:contains(purple))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{del}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList').should.equal('red, blue, yellow');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList').must.equal('red, blue, yellow');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
|
||||
});
|
||||
});
|
||||
@@ -206,27 +214,27 @@ describe('Integration', function() {
|
||||
});
|
||||
|
||||
describe('and there are more tokens to the left of selected token', function() {
|
||||
it("should delete the selected token and move focus to the previous token", function() {
|
||||
it("must delete the selected token and move focus to the previous token", function() {
|
||||
// Mark green as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(blue))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{backspace}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).should.equal('green');
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).must.equal('green');
|
||||
});
|
||||
})
|
||||
|
||||
describe('and there are no more tokens to the left of the selected token', function() {
|
||||
it("should delete the selected token and move focus to input", function() {
|
||||
it("must delete the selected token and move focus to input", function() {
|
||||
// Mark green as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(red))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{backspace}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).should.equal('');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).must.equal('');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -241,7 +249,7 @@ describe('Integration', function() {
|
||||
});
|
||||
|
||||
describe('and there are more tokens to the left of selected tokens', function() {
|
||||
it("should delete the selected tokens and move focus to the previous token of the leftmost selected token", function() {
|
||||
it("must delete the selected tokens and move focus to the previous token of the leftmost selected token", function() {
|
||||
// Mark green and yellow as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
@@ -249,13 +257,13 @@ describe('Integration', function() {
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{backspace}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).should.equal('red');
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList', null, null, true).must.equal('red');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and there are no more tokens to the left of selected tokens', function() {
|
||||
it("should delete the selected tokens and move focus input", function() {
|
||||
it("must delete the selected tokens and move focus input", function() {
|
||||
// Mark green and yellow as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(red))').addClass('active');
|
||||
@@ -263,9 +271,9 @@ describe('Integration', function() {
|
||||
.filter(':has(.token-label:contains(purple))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{backspace}" });
|
||||
this.$field.tokenfield('getTokens').length.should.equal(3);
|
||||
this.$field.tokenfield('getTokensList').should.equal('green, blue, yellow');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokens').length.must.equal(3);
|
||||
this.$field.tokenfield('getTokensList').must.equal('green, blue, yellow');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -279,9 +287,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the last token", function() {
|
||||
it("must move focus to the last token", function() {
|
||||
this.$input.simulate("key-sequence", { sequence: "{backspace}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('blue');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -296,9 +304,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the last token", function() {
|
||||
it("must move focus to the last token", function() {
|
||||
this.$input.simulate("key-sequence", { sequence: "{leftarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('blue');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -311,12 +319,12 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the previous token", function() {
|
||||
it("must move focus to the previous token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(blue))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{leftarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('green');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('green');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -329,14 +337,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the previous token of the leftmost selected token", function() {
|
||||
it("must move focus to the previous token of the leftmost selected token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{leftarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -349,12 +357,12 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should keep the focus on the first token", function() {
|
||||
it("must keep the focus on the first token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(red))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{leftarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -367,10 +375,10 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should keep the focus on the input", function() {
|
||||
it("must keep the focus on the input", function() {
|
||||
this.$input.simulate("key-sequence", { sequence: "{leftarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -385,10 +393,10 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should keep the focus on the input", function() {
|
||||
it("must keep the focus on the input", function() {
|
||||
this.$input.simulate("key-sequence", { sequence: "{rightarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -401,12 +409,12 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the next token", function() {
|
||||
it("must move focus to the next token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(red))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{rightarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('green');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('green');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -419,14 +427,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the next token of the rightmost selected token", function() {
|
||||
it("must move focus to the next token of the rightmost selected token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(red))').addClass('active');
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(blue))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{rightarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('yellow');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('yellow');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -439,13 +447,13 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move the focus to the input", function() {
|
||||
it("must move the focus to the input", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(blue))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{rightarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('');
|
||||
this.$input.is(document.activeElement).should.be.true;
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('');
|
||||
this.$input.is(document.activeElement).must.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -458,9 +466,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the last token", function() {
|
||||
it("must move focus to the last token", function() {
|
||||
this.$input.simulate("key-sequence", { sequence: "{rightarrow}" });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('blue');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -475,9 +483,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move focus to the last token", function() {
|
||||
it("must move focus to the last token", function() {
|
||||
this.$input.focus().simulate("keydown", { keyCode: 37, charCode: 37, shiftKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('blue');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -490,14 +498,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should activate the previous token in addition to the already active token", function() {
|
||||
it("must activate the previous token in addition to the already active token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(blue))').addClass('active');
|
||||
|
||||
this.$copyHelper.focus()
|
||||
.simulate("keydown", { keyCode: 37, shiftKey: true })
|
||||
.simulate("keydown", { keyCode: 37, shiftKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red, green, blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red, green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -510,14 +518,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move select the previous token of the leftmost selected token in addition to the already selected tokens", function() {
|
||||
it("must move select the previous token of the leftmost selected token in addition to the already selected tokens", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
this.$copyHelper.focus().simulate("keydown", { keyCode: 37, shiftKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red, green, yellow');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red, green, yellow');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -532,14 +540,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should activate the next token in addition to the already active token", function() {
|
||||
it("must activate the next token in addition to the already active token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(red))').addClass('active');
|
||||
|
||||
this.$copyHelper.focus()
|
||||
.simulate("keydown", { keyCode: 39, shiftKey: true })
|
||||
.simulate("keydown", { keyCode: 39, shiftKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red, green, blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red, green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -552,14 +560,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should move select the next token of the rightmost selected token in addition to the already selected tokens", function() {
|
||||
it("must move select the next token of the rightmost selected token in addition to the already selected tokens", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
this.$copyHelper.focus().simulate("keydown", { keyCode: 39, shiftKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('green, yellow, purple');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('green, yellow, purple');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -574,14 +582,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should enter edit mode for the active token", function() {
|
||||
it("must enter edit mode for the active token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{enter}" });
|
||||
this.$input.data('edit').should.be.true;
|
||||
this.$input.prev(':contains(red)').should.have.length(1);
|
||||
this.$input.next(':contains(blue)').should.have.length(1);
|
||||
this.$input.data('edit').must.be.true();
|
||||
this.$input.prev(':contains(red)').must.have.length(1);
|
||||
this.$input.next(':contains(blue)').must.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -596,12 +604,12 @@ describe('Integration', function() {
|
||||
delete TFT.options;
|
||||
});
|
||||
|
||||
it("should not enter edit mode for the active token [no data('edit') property]", function() {
|
||||
it("must not enter edit mode for the active token [no data('edit') property]", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
this.$copyHelper.simulate("key-sequence", { sequence: "{enter}" });
|
||||
this.$input.data().should.not.have.property('edit');
|
||||
this.$input.data().must.not.have.property('edit');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -614,10 +622,10 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should create a new token from the input", function() {
|
||||
it("must create a new token from the input", function() {
|
||||
|
||||
this.$input.simulate("key-sequence", { sequence: "purple{enter}" });
|
||||
this.$field.tokenfield('getTokens').should.have.length(4);
|
||||
this.$field.tokenfield('getTokens').must.have.length(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -632,15 +640,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should create a new token from the input", function() {
|
||||
it("must create a new token from the input", function() {
|
||||
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "purple" });
|
||||
this.$input.simulate("keydown", { keyCode: 9 });
|
||||
this.$field.tokenfield('getTokens').should.have.length(4);
|
||||
this.$field.tokenfield('getTokens').must.have.length(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Mouse interaction", function() {
|
||||
@@ -654,12 +661,12 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should select the token and deactivate any other active tokens", function() {
|
||||
it("must select the token and deactivate any other active tokens", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
this.$wrapper.find('.token:contains(red)').click();
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -672,9 +679,9 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should remove the token", function() {
|
||||
it("must remove the token", function() {
|
||||
this.$wrapper.find('.token:contains(red)').find('.close').click();
|
||||
this.$field.tokenfield('getTokensList' ).should.equal('green, blue');
|
||||
this.$field.tokenfield('getTokensList' ).must.equal('green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -687,17 +694,17 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should enter the edit mode of the token", function() {
|
||||
it("must enter the edit mode of the token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
this.$wrapper.find('.token:contains(red)').dblclick();
|
||||
this.$input.data('edit').should.be.true;
|
||||
this.$input.next(':contains(green)').should.have.length(1);
|
||||
this.$input.data('edit').must.be.true();
|
||||
this.$input.next(':contains(green)').must.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("should not enter the edit mode of the token when allowEditing false", function() {
|
||||
describe("must not enter the edit mode of the token when allowEditing false", function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue" />';
|
||||
TFT.options = { allowEditing: false }
|
||||
@@ -708,9 +715,9 @@ describe('Integration', function() {
|
||||
delete TFT.options;
|
||||
});
|
||||
|
||||
it("should not enter the edit mode of the token", function() {
|
||||
it("must not enter the edit mode of the token", function() {
|
||||
this.$wrapper.find('.token:contains(red)').dblclick();
|
||||
this.$input.data().should.not.have.property('edit');
|
||||
this.$input.data().must.not.have.property('edit');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -723,12 +730,12 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should activate the token in addition to the already active token", function() {
|
||||
it("must activate the token in addition to the already active token", function() {
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
this.$wrapper.find('.token:contains(red)').simulate('click', { ctrlKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red, green');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red, green');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -741,14 +748,14 @@ describe('Integration', function() {
|
||||
delete TFT.template;
|
||||
});
|
||||
|
||||
it("should activate the token and all the tokens between the already active token", function() {
|
||||
it("must activate the token and all the tokens between the already active token", function() {
|
||||
this.$wrapper.find('.token:contains(blue)').simulate('click');
|
||||
this.$wrapper.find('.token:contains(red)').simulate('click', { shiftKey: true });
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).should.equal('red, green, blue');
|
||||
this.$field.tokenfield('getTokensList', null, null, true ).must.equal('red, green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pressing enter with when there is no input", function() {
|
||||
describe("Pressing enter when there is no input", function() {
|
||||
var submitted = false;
|
||||
|
||||
before(function() {
|
||||
@@ -762,17 +769,18 @@ describe('Integration', function() {
|
||||
beforeEach(function() {
|
||||
this.$sandbox.find('form').on('submit', function(e) {
|
||||
submitted = true;
|
||||
event.preventDefault();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "{enter}" });
|
||||
// key-sequence does not trigger submit event on the form when pressing enter
|
||||
// so we need to trigger it manually. Not really a solid test-case, but oh well
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "{enter}" }).trigger('submit');
|
||||
});
|
||||
|
||||
it("should submit the underlying form", function() {
|
||||
submitted.should.equal(true);
|
||||
it("must submit the underlying form", function() {
|
||||
submitted.must.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Duplicates", function() {
|
||||
@@ -799,21 +807,24 @@ describe('Integration', function() {
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "red{enter}" });
|
||||
});
|
||||
|
||||
it("should not create a duplicate token", function() {
|
||||
this.$field.tokenfield('getTokensList').should.equal('red, green, blue');
|
||||
it("must not create a duplicate token", function() {
|
||||
this.$field.tokenfield('getTokensList').must.equal('red, green, blue');
|
||||
});
|
||||
|
||||
it("should add duplicate class to the existing token", function() {
|
||||
this.$sandbox.find( '.token[data-value="red"]' ).hasClass('duplicate').should.equal(true);
|
||||
it("must add duplicate class to the existing token", function() {
|
||||
this.$sandbox.find( '.token[data-value="red"]' ).hasClass('duplicate').must.equal(true);
|
||||
});
|
||||
|
||||
it("should keep the duplicate value in the input", function() {
|
||||
this.$input.val().should.equal('red');
|
||||
it("must keep the duplicate value in the input", function() {
|
||||
// key-sequnce acts a bit weird on node by adding a newline
|
||||
// at the beginning when pressing enter. So we need to
|
||||
// remove it by hand before comparing values
|
||||
this.$input.val().replace(/\n/g, '').must.equal('red');
|
||||
});
|
||||
|
||||
it("should not submit the form when pressing enter", function(done) {
|
||||
it("must not submit the form when pressing enter", function(done) {
|
||||
setTimeout(function() {
|
||||
submitted.should.equal(false);
|
||||
submitted.must.equal(false);
|
||||
done();
|
||||
}, 1);
|
||||
});
|
||||
@@ -841,28 +852,27 @@ describe('Integration', function() {
|
||||
this.$input.focus().simulate("key-sequence", { sequence: "red{enter}" });
|
||||
});
|
||||
|
||||
it("should create a duplicate token", function() {
|
||||
this.$field.tokenfield('getTokensList').should.equal('red, green, blue, red');
|
||||
it("must create a duplicate token", function() {
|
||||
this.$field.tokenfield('getTokensList').must.equal('red, green, blue, red');
|
||||
});
|
||||
|
||||
it("should not keep the duplicate value in the input", function() {
|
||||
this.$input.val().should.equal('');
|
||||
it("must not keep the duplicate value in the input", function() {
|
||||
this.$input.val().must.equal('');
|
||||
});
|
||||
|
||||
it("should not submit the form when pressing enter", function(done) {
|
||||
it("must not submit the form when pressing enter", function(done) {
|
||||
setTimeout(function() {
|
||||
submitted.should.equal(false);
|
||||
submitted.must.equal(false);
|
||||
done();
|
||||
}, 1);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
|
||||
describe("tokenfield:preparetoken", function() {
|
||||
it("should allow changing token field and value", function() {
|
||||
it("must allow changing token field and value", function() {
|
||||
this.$field.on('tokenfield:preparetoken', function (e) {
|
||||
e.token.value = 'one';
|
||||
e.token.label = 'two';
|
||||
@@ -870,22 +880,21 @@ describe('Integration', function() {
|
||||
this.$field.tokenfield('createToken', 'zero');
|
||||
|
||||
var results = this.$field.tokenfield('getTokens');
|
||||
results[0].label.should.equal('two');
|
||||
results[0].value.should.equal('one');
|
||||
results[0].label.must.equal('two');
|
||||
results[0].value.must.equal('one');
|
||||
});
|
||||
|
||||
it("should allow setting token value to an empty string", function() {
|
||||
it("must allow setting token value to an empty string", function() {
|
||||
this.$field.on('tokenfield:preparetoken', function (e) {
|
||||
e.token.value = '';
|
||||
});
|
||||
this.$field.tokenfield('createToken', 'zero');
|
||||
|
||||
var results = this.$field.tokenfield('getTokens');
|
||||
results[0].label.should.equal('zero');
|
||||
results[0].value.should.equal('');
|
||||
results[0].label.must.equal('zero');
|
||||
results[0].value.must.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,395 +1,396 @@
|
||||
describe('initializing tokenfield', function() {
|
||||
describe('unit', function() {
|
||||
|
||||
describe('with an empty input', function() {
|
||||
describe('initializing tokenfield', function() {
|
||||
describe('with an empty input', function() {
|
||||
|
||||
it('must wrap the original input with the wrapper element', function() {
|
||||
this.$field.parents('.tokenfield').hasClass('form-control').must.be.true();
|
||||
});
|
||||
|
||||
it('must create a new input element for token input', function() {
|
||||
this.$field.parents('.tokenfield').find('.token-input').length.must.equal(1);
|
||||
});
|
||||
|
||||
it('must move the original input out of the way', function() {
|
||||
this.$field.css('position').must.equal('absolute');
|
||||
this.$field.css('left').must.equal('-10000px');
|
||||
});
|
||||
|
||||
it('must not create any tokens', function() {
|
||||
this.$wrapper.find('.token').length.must.equal(0);
|
||||
});
|
||||
|
||||
it('should wrap the original input with the wrapper element', function() {
|
||||
this.$field.parents('.tokenfield').hasClass('form-control').should.be.true;
|
||||
});
|
||||
|
||||
it('should create a new input element for token input', function() {
|
||||
this.$field.parents('.tokenfield').find('.token-input').length.should.equal(1);
|
||||
describe('with an input with comma-separated values', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must create tokens for each comma-separated value', function() {
|
||||
this.$wrapper.find('.token').length.must.equal(3);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should move the original input out of the way', function() {
|
||||
this.$field.css('position').should.equal('absolute');
|
||||
this.$field.css('left').should.equal('-10000px');
|
||||
this.$wrapper.offset().left.should.be.above(this.$field.offset().left);
|
||||
describe('with an input with data-tokens values', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" data-tokens="red, green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must create tokens for each comma-separated token', function() {
|
||||
this.$wrapper.find('.token').length.must.equal(3);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should not create any tokens', function() {
|
||||
this.$wrapper.find('.token').length.should.equal(0);
|
||||
});
|
||||
describe('with RTL', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" style="direction:rtl" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must set rtl class on tokenfield', function() {
|
||||
this.$wrapper.hasClass('rtl').must.equal(true);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('with an input with comma-separated values', function() {
|
||||
|
||||
describe('destroying tokenfield', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
TFT.template = '<div id="destroy-test-container"><label for="destroy-test"></label><input type="text" id="destroy-test" class="tokenize" value="red,green, blue" /></div>';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should create tokens for each comma-separated value', function() {
|
||||
this.$wrapper.find('.token').length.should.equal(3);
|
||||
it('must reset the original input to previous state', function() {
|
||||
var $field = this.$field.tokenfield('destroy');
|
||||
$field.must.be.an.object();
|
||||
$field.data().must.not.have.property('bs.tokenfield');
|
||||
$field.parent().prop('id').must.equal('destroy-test-container');
|
||||
$field.val().must.equal('red, green, blue');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with an input with data-tokens values', function() {
|
||||
describe('Tokenfield public methods', function() {
|
||||
|
||||
describe('.createToken()', function() {
|
||||
|
||||
describe('using an empty input', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('createToken', 'awesome');
|
||||
});
|
||||
|
||||
it('must create a new token', function() {
|
||||
this.$wrapper.find('.token').must.have.length(1);
|
||||
});
|
||||
|
||||
it('add the new token value to original input', function() {
|
||||
this.$field.val().must.equal('awesome');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('using a non-empty input', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('createToken', 'awesome');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must append a new token to the end of existing tokens', function() {
|
||||
this.$field.val().must.equal('red, green, blue, awesome');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('given an object', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('createToken', { value: 'purple', label: 'Violet' });
|
||||
});
|
||||
|
||||
it('must create a new token', function() {
|
||||
this.$wrapper.find('.token').must.have.length(1);
|
||||
});
|
||||
|
||||
it('add the new token value to original input', function() {
|
||||
this.$field.val().must.equal('purple');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('.getTokens()', function() {
|
||||
|
||||
describe('given no arguments', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must return an array of token key-value pairs', function() {
|
||||
var tokens = this.$field.tokenfield('getTokens');
|
||||
tokens.must.be.an.array();
|
||||
tokens.must.have.length(3);
|
||||
tokens[0].must.have.keys(['label', 'value']);
|
||||
tokens[0].label.must.equal('red');
|
||||
tokens[0].value.must.equal('red');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given arguments active = true', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must return an array of only active token key-value pairs', function() {
|
||||
// Mark green as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
var tokens = this.$field.tokenfield('getTokens', true);
|
||||
tokens.must.be.an.array();
|
||||
tokens.must.have.length(1);
|
||||
tokens[0].must.have.keys(['label', 'value']);
|
||||
tokens[0].label.must.equal('green');
|
||||
tokens[0].value.must.equal('green');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" data-tokens="red, green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
describe('getTokensList()', function() {
|
||||
|
||||
describe('given no arguments', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must return a string with comma-separated token values', function() {
|
||||
var tokens = this.$field.tokenfield('getTokensList');
|
||||
tokens.must.be.a.string();
|
||||
tokens.must.equal('red, green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given an alternative delimiter', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must return a string with semicolon-separated token values', function() {
|
||||
var tokens = this.$field.tokenfield('getTokensList', ';', false);
|
||||
tokens.must.be.a.string();
|
||||
tokens.must.equal('red;green;blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given an alternative delimiter and active = true', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue,yellow" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must return a string with semicolon-separated token values', function() {
|
||||
// Mark green and yellow as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
var tokens = this.$field.tokenfield('getTokensList', ';', false, true);
|
||||
tokens.must.be.a.string();
|
||||
tokens.must.equal('green;yellow');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should create tokens for each comma-separated token', function() {
|
||||
this.$wrapper.find('.token').length.should.equal(3);
|
||||
describe('setTokens()', function() {
|
||||
|
||||
describe('using comma-separated string', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function(){
|
||||
this.$field.tokenfield('setTokens', 'black,yellow,white');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must replace any existing tokens with new ones', function() {
|
||||
var tokens = this.$field.tokenfield('getTokens')
|
||||
, tokensList = this.$field.tokenfield('getTokensList');
|
||||
|
||||
tokens.must.have.length(3);
|
||||
tokens[0].must.have.keys(['label', 'value']);
|
||||
tokens[0].label.must.equal('black');
|
||||
tokens[0].value.must.equal('black');
|
||||
|
||||
tokensList.must.not.contain('red');
|
||||
|
||||
});
|
||||
|
||||
it('must set the original input value to comma-separated list of token values', function() {
|
||||
this.$field.val().must.equal('black, yellow, white');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('using an array of string values', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function(){
|
||||
this.$field.tokenfield('setTokens', 'black,yellow,white');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must replace any existing tokens with new ones', function() {
|
||||
var tokens = this.$field.tokenfield('getTokens')
|
||||
, tokensList = this.$field.tokenfield('getTokensList');
|
||||
|
||||
tokens.must.have.length(3);
|
||||
tokens[0].must.have.keys(['label', 'value']);
|
||||
tokens[0].label.must.equal('black');
|
||||
tokens[0].value.must.equal('black');
|
||||
|
||||
tokensList.must.not.contain('red');
|
||||
|
||||
});
|
||||
|
||||
it('must set the original input value to comma-separated list of token values', function() {
|
||||
this.$field.val().must.equal('black, yellow, white');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('using an array of token objects', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function(){
|
||||
this.$field.tokenfield('setTokens', [{ value: "black", label: "Schwarz" }, { value: "yellow", label: "Gelb" }]);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('must replace any existing tokens with new ones', function() {
|
||||
this.$field.tokenfield('setTokens', [{ value: "black", label: "Schwarz" }, { value: "yellow", label: "Gelb" }]);
|
||||
|
||||
var tokens = this.$field.tokenfield('getTokens')
|
||||
, tokensList = this.$field.tokenfield('getTokensList');
|
||||
|
||||
tokens.must.have.length(2);
|
||||
tokens[0].must.have.keys(['label', 'value']);
|
||||
tokens[0].label.must.equal('Schwarz');
|
||||
tokens[0].value.must.equal('black');
|
||||
|
||||
tokensList.must.not.contain('red');
|
||||
|
||||
});
|
||||
|
||||
it('must set the original input value to comma-separated list of token values', function() {
|
||||
this.$field.val().must.equal('black, yellow');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with RTL', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" style="direction:rtl" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should set rtl class on tokenfield', function() {
|
||||
this.$wrapper.hasClass('rtl').should.equal(true);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('destroying tokenfield', function() {
|
||||
before(function() {
|
||||
TFT.template = '<div id="destroy-test-container"><label for="destroy-test"></label><input type="text" id="destroy-test" class="tokenize" value="red,green, blue" /></div>';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should reset the original input to previous state', function() {
|
||||
var $field = this.$field.tokenfield('destroy');
|
||||
$field.should.be.an('object');
|
||||
$field.data().should.not.have.property('bs.tokenfield');
|
||||
$field.parent().prop('id').should.equal('destroy-test-container');
|
||||
$field.val().should.equal('red, green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tokenfield public methods', function() {
|
||||
|
||||
describe('.createToken()', function() {
|
||||
|
||||
describe('using an empty input', function() {
|
||||
describe('disable()', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('createToken', 'awesome');
|
||||
this.$field.tokenfield('disable');
|
||||
});
|
||||
|
||||
it('should create a new token', function() {
|
||||
this.$wrapper.find('.token').should.have.length(1);
|
||||
it('must disable both original and token input', function() {
|
||||
this.$field.prop('disabled').must.be.true();
|
||||
this.$input.prop('disabled').must.be.true();
|
||||
});
|
||||
|
||||
it('add the new token value to original input', function() {
|
||||
this.$field.val().should.equal('awesome');
|
||||
it('must add "disabled" class to tokenfield', function() {
|
||||
this.$wrapper.hasClass('disabled').must.be.true();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('using a non-empty input', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
describe('enable()', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('createToken', 'awesome');
|
||||
this.$field.tokenfield('disable');
|
||||
this.$field.tokenfield('enable');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should append a new token to the end of existing tokens', function() {
|
||||
this.$field.val().should.equal('red, green, blue, awesome');
|
||||
it('must enable both original and token input', function() {
|
||||
this.$field.prop('disabled').must.be.false();
|
||||
this.$input.prop('disabled').must.be.false();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('given an object', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('createToken', { value: 'purple', label: 'Violet' });
|
||||
it('must remove "disabled" class from tokenfield', function() {
|
||||
this.$wrapper.hasClass('disabled').must.be.false();
|
||||
});
|
||||
|
||||
it('should create a new token', function() {
|
||||
this.$wrapper.find('.token').should.have.length(1);
|
||||
});
|
||||
|
||||
it('add the new token value to original input', function() {
|
||||
this.$field.val().should.equal('purple');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('.getTokens()', function() {
|
||||
|
||||
describe('given no arguments', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should return an array of token key-value pairs', function() {
|
||||
var tokens = this.$field.tokenfield('getTokens');
|
||||
tokens.should.be.an('array');
|
||||
tokens.should.have.length(3);
|
||||
tokens[0].should.include.keys('label', 'value');
|
||||
tokens[0].label.should.equal('red');
|
||||
tokens[0].value.should.equal('red');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given arguments active = true', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should return an array of only active token key-value pairs', function() {
|
||||
// Mark green as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
|
||||
var tokens = this.$field.tokenfield('getTokens', true);
|
||||
tokens.should.be.an('array');
|
||||
tokens.should.have.length(1);
|
||||
tokens[0].should.include.keys('label', 'value');
|
||||
tokens[0].label.should.equal('green');
|
||||
tokens[0].value.should.equal('green');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTokensList()', function() {
|
||||
|
||||
describe('given no arguments', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should return a string with comma-separated token values', function() {
|
||||
var tokens = this.$field.tokenfield('getTokensList');
|
||||
tokens.should.be.a('string');
|
||||
tokens.should.equal('red, green, blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given an alternative delimiter', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should return a string with semicolon-separated token values', function() {
|
||||
var tokens = this.$field.tokenfield('getTokensList', ';', false);
|
||||
tokens.should.be.a('string');
|
||||
tokens.should.equal('red;green;blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given an alternative delimiter and active = true', function() {
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green,blue,yellow" />';
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should return a string with semicolon-separated token values', function() {
|
||||
// Mark green and yellow as active
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(green))').addClass('active');
|
||||
this.$wrapper.find('.token')
|
||||
.filter(':has(.token-label:contains(yellow))').addClass('active');
|
||||
|
||||
var tokens = this.$field.tokenfield('getTokensList', ';', false, true);
|
||||
tokens.should.be.a('string');
|
||||
tokens.should.equal('green;yellow');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('setTokens()', function() {
|
||||
|
||||
describe('using comma-separated string', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function(){
|
||||
this.$field.tokenfield('setTokens', 'black,yellow,white');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should replace any existing tokens with new ones', function() {
|
||||
var tokens = this.$field.tokenfield('getTokens')
|
||||
, tokensList = this.$field.tokenfield('getTokensList');
|
||||
|
||||
tokens.should.have.length(3);
|
||||
tokens[0].should.include.keys('label', 'value');
|
||||
tokens[0].label.should.equal('black');
|
||||
tokens[0].value.should.equal('black');
|
||||
|
||||
tokensList.should.not.contain('red');
|
||||
|
||||
});
|
||||
|
||||
it('should set the original input value to comma-separated list of token values', function() {
|
||||
this.$field.val().should.equal('black, yellow, white');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('using an array of string values', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function(){
|
||||
this.$field.tokenfield('setTokens', 'black,yellow,white');
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should replace any existing tokens with new ones', function() {
|
||||
var tokens = this.$field.tokenfield('getTokens')
|
||||
, tokensList = this.$field.tokenfield('getTokensList');
|
||||
|
||||
tokens.should.have.length(3);
|
||||
tokens[0].should.include.keys('label', 'value');
|
||||
tokens[0].label.should.equal('black');
|
||||
tokens[0].value.should.equal('black');
|
||||
|
||||
tokensList.should.not.contain('red');
|
||||
|
||||
});
|
||||
|
||||
it('should set the original input value to comma-separated list of token values', function() {
|
||||
this.$field.val().should.equal('black, yellow, white');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('using an array of token objects', function() {
|
||||
|
||||
before(function() {
|
||||
TFT.template = '<input type="text" class="tokenize" value="red,green, blue" />';
|
||||
});
|
||||
|
||||
beforeEach(function(){
|
||||
this.$field.tokenfield('setTokens', [{ value: "black", label: "Schwarz" }, { value: "yellow", label: "Gelb" }]);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
TFT.template = null;
|
||||
});
|
||||
|
||||
it('should replace any existing tokens with new ones', function() {
|
||||
this.$field.tokenfield('setTokens', [{ value: "black", label: "Schwarz" }, { value: "yellow", label: "Gelb" }]);
|
||||
|
||||
var tokens = this.$field.tokenfield('getTokens')
|
||||
, tokensList = this.$field.tokenfield('getTokensList');
|
||||
|
||||
tokens.should.have.length(2);
|
||||
tokens[0].should.include.keys('label', 'value');
|
||||
tokens[0].label.should.equal('Schwarz');
|
||||
tokens[0].value.should.equal('black');
|
||||
|
||||
tokensList.should.not.contain('red');
|
||||
|
||||
});
|
||||
|
||||
it('should set the original input value to comma-separated list of token values', function() {
|
||||
this.$field.val().should.equal('black, yellow');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('disable()', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('disable');
|
||||
});
|
||||
|
||||
it('should disable both original and token input', function() {
|
||||
this.$field.prop('disabled').should.be.true;
|
||||
this.$input.prop('disabled').should.be.true;
|
||||
});
|
||||
|
||||
it('should add "disabled" class to tokenfield', function() {
|
||||
this.$wrapper.hasClass('disabled').should.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('enable()', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.$field.tokenfield('disable');
|
||||
this.$field.tokenfield('enable');
|
||||
});
|
||||
|
||||
it('should enable both original and token input', function() {
|
||||
this.$field.prop('disabled').should.be.false;
|
||||
this.$input.prop('disabled').should.be.false;
|
||||
});
|
||||
|
||||
it('should remove "disabled" class from tokenfield', function() {
|
||||
this.$wrapper.hasClass('disabled').should.be.false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tokenfield Test Runner</title>
|
||||
|
||||
<link rel="stylesheet" href="../bower_components/mocha/mocha.css" />
|
||||
<link rel="stylesheet" href="../bower_components/bootstrap/dist/css/bootstrap.css" />
|
||||
|
||||
<link rel="stylesheet" href="../dist/css/bootstrap-tokenfield.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
|
||||
<!-- dependencides -->
|
||||
<script src="../bower_components/jquery/jquery.js"></script>
|
||||
<script src="../bower_components/bootstrap/dist/js/bootstrap.js"></script>
|
||||
<script src="../dist/bootstrap-tokenfield.js"></script>
|
||||
<script src="../bower_components/mocha/mocha.js"></script>
|
||||
<script src="../bower_components/chai/chai.js"></script>
|
||||
<script src="vendor/bililiteRange.js"></script>
|
||||
<script src="vendor/jquery.simulate.js"></script>
|
||||
<script src="vendor/jquery.simulate.ext.js"></script>
|
||||
<script src="vendor/jquery.simulate.key-combo.js"></script>
|
||||
<script src="vendor/jquery.simulate.key-sequence.js"></script>
|
||||
<script>mocha.setup('bdd')</script>
|
||||
|
||||
<!-- tests -->
|
||||
<script src="setup.js"></script>
|
||||
<script src="test.tokenfield.unit.js"></script>
|
||||
<script src="test.tokenfield.integration.js"></script>
|
||||
|
||||
<!-- setup & run tests -->
|
||||
<script>
|
||||
should = chai.should();
|
||||
mocha.run();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
347
test/vendor/bililiteRange.js
vendored
347
test/vendor/bililiteRange.js
vendored
@@ -1,347 +0,0 @@
|
||||
// Cross-broswer implementation of text ranges and selections
|
||||
// documentation: http://bililite.com/blog/2011/01/17/cross-browser-text-ranges-and-selections/
|
||||
// Version: 1.1
|
||||
// Copyright (c) 2010 Daniel Wachsstock
|
||||
// MIT license:
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(function(){
|
||||
|
||||
bililiteRange = function(el, debug){
|
||||
var ret;
|
||||
if (debug){
|
||||
ret = new NothingRange(); // Easier to force it to use the no-selection type than to try to find an old browser
|
||||
}else if (document.selection){
|
||||
// Internet Explorer
|
||||
ret = new IERange();
|
||||
}else if (window.getSelection && el.setSelectionRange){
|
||||
// Standards. Element is an input or textarea
|
||||
ret = new InputRange();
|
||||
}else if (window.getSelection){
|
||||
// Standards, with any other kind of element
|
||||
ret = new W3CRange();
|
||||
}else{
|
||||
// doesn't support selection
|
||||
ret = new NothingRange();
|
||||
}
|
||||
ret._el = el;
|
||||
ret._textProp = textProp(el);
|
||||
ret._bounds = [0, ret.length()];
|
||||
return ret;
|
||||
};
|
||||
|
||||
function textProp(el){
|
||||
// returns the property that contains the text of the element
|
||||
if (typeof el.value != 'undefined') return 'value';
|
||||
if (typeof el.text != 'undefined') return 'text';
|
||||
if (typeof el.textContent != 'undefined') return 'textContent';
|
||||
return 'innerText';
|
||||
}
|
||||
|
||||
// base class
|
||||
function Range(){}
|
||||
Range.prototype = {
|
||||
length: function() {
|
||||
return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness
|
||||
},
|
||||
bounds: function(s){
|
||||
if (s === 'all'){
|
||||
this._bounds = [0, this.length()];
|
||||
}else if (s === 'start'){
|
||||
this._bounds = [0, 0];
|
||||
}else if (s === 'end'){
|
||||
this._bounds = [this.length(), this.length()];
|
||||
}else if (s === 'selection'){
|
||||
this.bounds ('all'); // first select the whole thing for constraining
|
||||
this._bounds = this._nativeSelection();
|
||||
}else if (s){
|
||||
this._bounds = s; // don't error check now; the element may change at any moment, so constrain it when we need it.
|
||||
}else{
|
||||
var b = [
|
||||
Math.max(0, Math.min (this.length(), this._bounds[0])),
|
||||
Math.max(0, Math.min (this.length(), this._bounds[1]))
|
||||
];
|
||||
return b; // need to constrain it to fit
|
||||
}
|
||||
return this; // allow for chaining
|
||||
},
|
||||
select: function(){
|
||||
this._nativeSelect(this._nativeRange(this.bounds()));
|
||||
return this; // allow for chaining
|
||||
},
|
||||
text: function(text, select){
|
||||
if (arguments.length){
|
||||
this._nativeSetText(text, this._nativeRange(this.bounds()));
|
||||
if (select == 'start'){
|
||||
this.bounds ([this._bounds[0], this._bounds[0]]);
|
||||
this.select();
|
||||
}else if (select == 'end'){
|
||||
this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]);
|
||||
this.select();
|
||||
}else if (select == 'all'){
|
||||
this.bounds ([this._bounds[0], this._bounds[0]+text.length]);
|
||||
this.select();
|
||||
}
|
||||
return this; // allow for chaining
|
||||
}else{
|
||||
return this._nativeGetText(this._nativeRange(this.bounds()));
|
||||
}
|
||||
},
|
||||
insertEOL: function (){
|
||||
this._nativeEOL();
|
||||
this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function IERange(){}
|
||||
IERange.prototype = new Range();
|
||||
IERange.prototype._nativeRange = function (bounds){
|
||||
var rng;
|
||||
if (this._el.tagName == 'INPUT'){
|
||||
// IE 8 is very inconsistent; textareas have createTextRange but it doesn't work
|
||||
rng = this._el.createTextRange();
|
||||
}else{
|
||||
rng = document.body.createTextRange ();
|
||||
rng.moveToElementText(this._el);
|
||||
}
|
||||
if (bounds){
|
||||
if (bounds[1] < 0) bounds[1] = 0; // IE tends to run elements out of bounds
|
||||
if (bounds[0] > this.length()) bounds[0] = this.length();
|
||||
if (bounds[1] < rng.text.replace(/\r/g, '').length){ // correct for IE's CrLf wierdness
|
||||
// block-display elements have an invisible, uncounted end of element marker, so we move an extra one and use the current length of the range
|
||||
rng.moveEnd ('character', -1);
|
||||
rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length);
|
||||
}
|
||||
if (bounds[0] > 0) rng.moveStart('character', bounds[0]);
|
||||
}
|
||||
return rng;
|
||||
};
|
||||
IERange.prototype._nativeSelect = function (rng){
|
||||
rng.select();
|
||||
};
|
||||
IERange.prototype._nativeSelection = function (){
|
||||
// returns [start, end] for the selection constrained to be in element
|
||||
var rng = this._nativeRange(); // range of the element to constrain to
|
||||
var len = this.length();
|
||||
if (document.selection.type != 'Text') return [len, len]; // append to the end
|
||||
var sel = document.selection.createRange();
|
||||
try{
|
||||
return [
|
||||
iestart(sel, rng),
|
||||
ieend (sel, rng)
|
||||
];
|
||||
}catch (e){
|
||||
// IE gets upset sometimes about comparing text to input elements, but the selections cannot overlap, so make a best guess
|
||||
return (sel.parentElement().sourceIndex < this._el.sourceIndex) ? [0,0] : [len, len];
|
||||
}
|
||||
};
|
||||
IERange.prototype._nativeGetText = function (rng){
|
||||
return rng.text.replace(/\r/g, ''); // correct for IE's CrLf weirdness
|
||||
};
|
||||
IERange.prototype._nativeSetText = function (text, rng){
|
||||
rng.text = text;
|
||||
};
|
||||
IERange.prototype._nativeEOL = function(){
|
||||
if (typeof this._el.value != 'undefined'){
|
||||
this.text('\n'); // for input and textarea, insert it straight
|
||||
}else{
|
||||
this._nativeRange(this.bounds()).pasteHTML('<br/>');
|
||||
}
|
||||
};
|
||||
// IE internals
|
||||
function iestart(rng, constraint){
|
||||
// returns the position (in character) of the start of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
|
||||
var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
|
||||
if (rng.compareEndPoints ('StartToStart', constraint) <= 0) return 0; // at or before the beginning
|
||||
if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len;
|
||||
for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1));
|
||||
return i;
|
||||
}
|
||||
function ieend (rng, constraint){
|
||||
// returns the position (in character) of the end of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
|
||||
var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
|
||||
if (rng.compareEndPoints ('EndToEnd', constraint) >= 0) return len; // at or after the end
|
||||
if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0;
|
||||
for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1));
|
||||
return i;
|
||||
}
|
||||
|
||||
// an input element in a standards document. "Native Range" is just the bounds array
|
||||
function InputRange(){}
|
||||
InputRange.prototype = new Range();
|
||||
InputRange.prototype._nativeRange = function(bounds) {
|
||||
return bounds || [0, this.length()];
|
||||
};
|
||||
InputRange.prototype._nativeSelect = function (rng){
|
||||
this._el.setSelectionRange(rng[0], rng[1]);
|
||||
};
|
||||
InputRange.prototype._nativeSelection = function(){
|
||||
return [this._el.selectionStart, this._el.selectionEnd];
|
||||
};
|
||||
InputRange.prototype._nativeGetText = function(rng){
|
||||
return this._el.value.substring(rng[0], rng[1]);
|
||||
};
|
||||
InputRange.prototype._nativeSetText = function(text, rng){
|
||||
var val = this._el.value;
|
||||
this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]);
|
||||
};
|
||||
InputRange.prototype._nativeEOL = function(){
|
||||
this.text('\n');
|
||||
};
|
||||
|
||||
function W3CRange(){}
|
||||
W3CRange.prototype = new Range();
|
||||
W3CRange.prototype._nativeRange = function (bounds){
|
||||
var rng = document.createRange();
|
||||
rng.selectNodeContents(this._el);
|
||||
if (bounds){
|
||||
w3cmoveBoundary (rng, bounds[0], true, this._el);
|
||||
rng.collapse (true);
|
||||
w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el);
|
||||
}
|
||||
return rng;
|
||||
};
|
||||
W3CRange.prototype._nativeSelect = function (rng){
|
||||
window.getSelection().removeAllRanges();
|
||||
window.getSelection().addRange (rng);
|
||||
};
|
||||
W3CRange.prototype._nativeSelection = function (){
|
||||
// returns [start, end] for the selection constrained to be in element
|
||||
var rng = this._nativeRange(); // range of the element to constrain to
|
||||
if (window.getSelection().rangeCount == 0) return [this.length(), this.length()]; // append to the end
|
||||
var sel = window.getSelection().getRangeAt(0);
|
||||
return [
|
||||
w3cstart(sel, rng),
|
||||
w3cend (sel, rng)
|
||||
];
|
||||
};
|
||||
W3CRange.prototype._nativeGetText = function (rng){
|
||||
return rng.toString();
|
||||
};
|
||||
W3CRange.prototype._nativeSetText = function (text, rng){
|
||||
rng.deleteContents();
|
||||
rng.insertNode (document.createTextNode(text));
|
||||
this._el.normalize(); // merge the text with the surrounding text
|
||||
};
|
||||
W3CRange.prototype._nativeEOL = function(){
|
||||
var rng = this._nativeRange(this.bounds());
|
||||
rng.deleteContents();
|
||||
var br = document.createElement('br');
|
||||
br.setAttribute ('_moz_dirty', ''); // for Firefox
|
||||
rng.insertNode (br);
|
||||
rng.insertNode (document.createTextNode('\n'));
|
||||
rng.collapse (false);
|
||||
};
|
||||
// W3C internals
|
||||
function nextnode (node, root){
|
||||
// in-order traversal
|
||||
// we've already visited node, so get kids then siblings
|
||||
if (node.firstChild) return node.firstChild;
|
||||
if (node.nextSibling) return node.nextSibling;
|
||||
if (node===root) return null;
|
||||
while (node.parentNode){
|
||||
// get uncles
|
||||
node = node.parentNode;
|
||||
if (node == root) return null;
|
||||
if (node.nextSibling) return node.nextSibling;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function w3cmoveBoundary (rng, n, bStart, el){
|
||||
// move the boundary (bStart == true ? start : end) n characters forward, up to the end of element el. Forward only!
|
||||
// if the start is moved after the end, then an exception is raised
|
||||
if (n <= 0) return;
|
||||
var node = rng[bStart ? 'startContainer' : 'endContainer'];
|
||||
if (node.nodeType == 3){
|
||||
// we may be starting somewhere into the text
|
||||
n += rng[bStart ? 'startOffset' : 'endOffset'];
|
||||
}
|
||||
while (node){
|
||||
if (node.nodeType == 3){
|
||||
if (n <= node.nodeValue.length){
|
||||
rng[bStart ? 'setStart' : 'setEnd'](node, n);
|
||||
// special case: if we end next to a <br>, include that node.
|
||||
if (n == node.nodeValue.length){
|
||||
// skip past zero-length text nodes
|
||||
for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){
|
||||
rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
|
||||
}
|
||||
if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
|
||||
}
|
||||
return;
|
||||
}else{
|
||||
rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one
|
||||
n -= node.nodeValue.length; // and eat these characters
|
||||
}
|
||||
}
|
||||
node = nextnode (node, el);
|
||||
}
|
||||
}
|
||||
var START_TO_START = 0; // from the w3c definitions
|
||||
var START_TO_END = 1;
|
||||
var END_TO_END = 2;
|
||||
var END_TO_START = 3;
|
||||
// from the Mozilla documentation, for range.compareBoundaryPoints(how, sourceRange)
|
||||
// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange.
|
||||
// * Range.END_TO_END compares the end boundary-point of sourceRange to the end boundary-point of range.
|
||||
// * Range.END_TO_START compares the end boundary-point of sourceRange to the start boundary-point of range.
|
||||
// * Range.START_TO_END compares the start boundary-point of sourceRange to the end boundary-point of range.
|
||||
// * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
|
||||
function w3cstart(rng, constraint){
|
||||
if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning
|
||||
if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;
|
||||
rng = rng.cloneRange(); // don't change the original
|
||||
rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place
|
||||
return constraint.toString().length - rng.toString().length;
|
||||
}
|
||||
function w3cend (rng, constraint){
|
||||
if (rng.compareBoundaryPoints (END_TO_END, constraint) >= 0) return constraint.toString().length; // at or after the end
|
||||
if (rng.compareBoundaryPoints (START_TO_END, constraint) <= 0) return 0;
|
||||
rng = rng.cloneRange(); // don't change the original
|
||||
rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place
|
||||
return rng.toString().length;
|
||||
}
|
||||
|
||||
function NothingRange(){}
|
||||
NothingRange.prototype = new Range();
|
||||
NothingRange.prototype._nativeRange = function(bounds) {
|
||||
return bounds || [0,this.length()];
|
||||
};
|
||||
NothingRange.prototype._nativeSelect = function (rng){ // do nothing
|
||||
};
|
||||
NothingRange.prototype._nativeSelection = function(){
|
||||
return [0,0];
|
||||
};
|
||||
NothingRange.prototype._nativeGetText = function (rng){
|
||||
return this._el[this._textProp].substring(rng[0], rng[1]);
|
||||
};
|
||||
NothingRange.prototype._nativeSetText = function (text, rng){
|
||||
var val = this._el[this._textProp];
|
||||
this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]);
|
||||
};
|
||||
NothingRange.prototype._nativeEOL = function(){
|
||||
this.text('\n');
|
||||
};
|
||||
|
||||
})();
|
||||
583
test/vendor/jquery.simulate.drag-n-drop.js
vendored
583
test/vendor/jquery.simulate.drag-n-drop.js
vendored
@@ -1,583 +0,0 @@
|
||||
/*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
|
||||
/*jslint white: true vars: true browser: true todo: true */
|
||||
/*global jQuery:true $:true */
|
||||
|
||||
/* jQuery Simulate Drag-n-Drop Plugin 1.2.0
|
||||
* http://github.com/j-ulrich/jquery-simulate-ext
|
||||
*
|
||||
* Copyright (c) 2013 Jochen Ulrich
|
||||
* Licensed under the MIT license (MIT-LICENSE.txt).
|
||||
*/
|
||||
|
||||
;(function($, undefined) {
|
||||
"use strict";
|
||||
|
||||
/* Overwrite the $.fn.simulate function to reduce the jQuery set to the first element for the
|
||||
* drag-n-drop interactions.
|
||||
*/
|
||||
$.fn.simulate = function( type, options ) {
|
||||
switch (type) {
|
||||
case "drag":
|
||||
case "drop":
|
||||
case "drag-n-drop":
|
||||
var ele = this.first();
|
||||
new $.simulate( ele[0], type, options);
|
||||
return ele;
|
||||
default:
|
||||
return this.each(function() {
|
||||
new $.simulate( this, type, options );
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var now = Date.now || function() { return new Date().getTime(); };
|
||||
|
||||
var rdocument = /\[object (?:HTML)?Document\]/;
|
||||
/**
|
||||
* Tests whether an object is an (HTML) document object.
|
||||
* @param {DOM Element} elem - the object/element to be tested
|
||||
* @returns {Boolean} <code>true</code> if <i>elem</i> is an (HTML) document object.
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.1
|
||||
*/
|
||||
function isDocument( elem ) {
|
||||
return rdocument.test(Object.prototype.toString.call($(elem)[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the first match from an array.
|
||||
* @param {Array} array - Array of objects to be be tested
|
||||
* @param {Function} check - Callback function that accepts one argument (which will be one element
|
||||
* from the <i>array</i>) and returns a boolean.
|
||||
* @returns {Boolean|null} the first element in <i>array</i> for which <i>check</i> returns <code>true</code>.
|
||||
* If none of the elements in <i>array</i> passes <i>check</i>, <code>null</code> is returned.
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.1
|
||||
*/
|
||||
function selectFirstMatch(array, check) {
|
||||
var i;
|
||||
if ($.isFunction(check)) {
|
||||
for (i=0; i < array.length; i+=1) {
|
||||
if (check(array[i])) {
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
for (i=0; i < array.length; i+=1) {
|
||||
if (array[i]) {
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Based on the findCenter function from jquery.simulate.js
|
||||
/**
|
||||
* Calculates the position of the center of an DOM element.
|
||||
* @param {DOM Element} elem - the element whose center should be calculated.
|
||||
* @returns {Object} an object with the properties <code>x</code> and <code>y</code>
|
||||
* representing the position of the center of <i>elem</i> in page relative coordinates
|
||||
* (i.e. independent of any scrolling).
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function findCenter( elem ) {
|
||||
var offset;
|
||||
elem = $( elem );
|
||||
if ( isDocument(elem[0]) ) {
|
||||
offset = {left: 0, top: 0};
|
||||
}
|
||||
else {
|
||||
offset = elem.offset();
|
||||
}
|
||||
|
||||
return {
|
||||
x: offset.left + elem.outerWidth() / 2,
|
||||
y: offset.top + elem.outerHeight() / 2
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts page relative coordinates into client relative coordinates.
|
||||
* @param {Numeric|Object} x - Either the x coordinate of the page relative coordinates or
|
||||
* an object with the properties <code>pageX</code> and <code>pageY</code> representing page
|
||||
* relative coordinates.
|
||||
* @param {Numeric} [y] - If <i>x</i> is numeric (i.e. the x coordinate of page relative coordinates),
|
||||
* then this is the y coordinate. If <i>x</i> is an object, this parameter is skipped.
|
||||
* @param {DOM Document} [docRel] - Optional DOM document object used to calculate the client relative
|
||||
* coordinates. The page relative coordinates are interpreted as being relative to that document and
|
||||
* the scroll position of that document is used to calculate the client relative coordinates.
|
||||
* By default, <code>document</code> is used.
|
||||
* @returns {Object} an object representing the client relative coordinates corresponding to the
|
||||
* given page relative coordinates. The object either provides the properties <code>x</code> and
|
||||
* <code>y</code> when <i>x</i> and <i>y</i> were given as arguments, or <code>clientX</code>
|
||||
* and <code>clientY</code> when the parameter <i>x</i> was given as an object (see above).
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function pageToClientPos(x, y, docRel) {
|
||||
var jDocument;
|
||||
if ( isDocument(y) ) {
|
||||
jDocument = $(y);
|
||||
} else {
|
||||
jDocument = $(docRel || document);
|
||||
}
|
||||
|
||||
if (typeof x === "number" && typeof y === "number") {
|
||||
return {
|
||||
x: x - jDocument.scrollLeft(),
|
||||
y: y - jDocument.scrollTop()
|
||||
};
|
||||
}
|
||||
else if (typeof x === "object" && x.pageX && x.pageY) {
|
||||
return {
|
||||
clientX: x.pageX - jDocument.scrollLeft(),
|
||||
clientY: x.pageY - jDocument.scrollTop()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser-independent implementation of <code>document.elementFromPoint()</code>.
|
||||
*
|
||||
* When run for the first time on a scrolled page, this function performs a check on how
|
||||
* <code>document.elementFromPoint()</code> is implemented in the current browser. It stores
|
||||
* the results in two static variables so that the check can be skipped for successive calls.
|
||||
*
|
||||
* @param {Numeric|Object} x - Either the x coordinate of client relative coordinates or an object
|
||||
* with the properties <code>x</code> and <code>y</code> representing client relative coordinates.
|
||||
* @param {Numeric} [y] - If <i>x</i> is numeric (i.e. the x coordinate of client relative coordinates),
|
||||
* this is the y coordinate. If <i>x</i> is an object, this parameter is skipped.
|
||||
* @param {DOM Document} [docRel] - Optional DOM document object
|
||||
* @returns {DOM Element|Null}
|
||||
* @private
|
||||
* @author Nicolas Zeh (Basic idea), julrich
|
||||
* @see http://www.zehnet.de/2010/11/19/document-elementfrompoint-a-jquery-solution/
|
||||
* @since 1.0
|
||||
*/
|
||||
function elementAtPosition(x, y, docRel) {
|
||||
var doc;
|
||||
if ( isDocument(y) ) {
|
||||
doc = y;
|
||||
} else {
|
||||
doc = docRel || document;
|
||||
}
|
||||
|
||||
if(!doc.elementFromPoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var clientX = x, clientY = y;
|
||||
if (typeof x === "object" && (x.clientX || x.clientY)) {
|
||||
clientX = x.clientX || 0 ;
|
||||
clientY = x.clientY || 0;
|
||||
}
|
||||
|
||||
if(elementAtPosition.prototype.check)
|
||||
{
|
||||
var sl, ele;
|
||||
if ((sl = $(doc).scrollTop()) >0)
|
||||
{
|
||||
ele = doc.elementFromPoint(0, sl + $(window).height() -1);
|
||||
if ( ele !== null && ele.tagName.toUpperCase() === 'HTML' ) { ele = null; }
|
||||
elementAtPosition.prototype.nativeUsesRelative = ( ele === null );
|
||||
}
|
||||
else if((sl = $(doc).scrollLeft()) >0)
|
||||
{
|
||||
ele = doc.elementFromPoint(sl + $(window).width() -1, 0);
|
||||
if ( ele !== null && ele.tagName.toUpperCase() === 'HTML' ) { ele = null; }
|
||||
elementAtPosition.prototype.nativeUsesRelative = ( ele === null );
|
||||
}
|
||||
elementAtPosition.prototype.check = (sl<=0); // Check was not meaningful because we were at scroll position 0
|
||||
}
|
||||
|
||||
if(!elementAtPosition.prototype.nativeUsesRelative)
|
||||
{
|
||||
clientX += $(doc).scrollLeft();
|
||||
clientY += $(doc).scrollTop();
|
||||
}
|
||||
|
||||
return doc.elementFromPoint(clientX,clientY);
|
||||
}
|
||||
// Default values for the check variables
|
||||
elementAtPosition.prototype.check = true;
|
||||
elementAtPosition.prototype.nativeUsesRelative = true;
|
||||
|
||||
/**
|
||||
* Informs the rest of the world that the drag is finished.
|
||||
* @param {DOM Element} ele - The element which was dragged.
|
||||
* @param {Object} [options] - The drag options.
|
||||
* @fires simulate-drag
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function dragFinished(ele, options) {
|
||||
var opts = options || {};
|
||||
$(ele).trigger({type: "simulate-drag"});
|
||||
if ($.isFunction(opts.callback)) {
|
||||
opts.callback.apply(ele);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a series of <code>mousemove</code> events for a drag.
|
||||
* @param {Object} self - The simulate object.
|
||||
* @param {DOM Element} ele - The element which is dragged.
|
||||
* @param {Object} start - The start coordinates of the drag, represented using the properties
|
||||
* <code>x</code> and <code>y</code>.
|
||||
* @param {Object} drag - The distance to be dragged, represented using the properties <code>dx</code>
|
||||
* and <code>dy</code>.
|
||||
* @param {Object} options - The drag options. Must have the property <code>interpolation</code>
|
||||
* containing the interpolation options (<code>stepWidth</code>, <code>stepCount</code>, etc.).
|
||||
* @requires eventTarget
|
||||
* @requires now()
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function interpolatedEvents(self, ele, start, drag, options) {
|
||||
var targetDoc = selectFirstMatch([ele, ele.ownerDocument], isDocument) || document,
|
||||
interpolOptions = options.interpolation,
|
||||
dragDistance = Math.sqrt(Math.pow(drag.dx,2) + Math.pow(drag.dy,2)), // sqrt(a^2 + b^2)
|
||||
stepWidth, stepCount, stepVector;
|
||||
|
||||
if (interpolOptions.stepWidth) {
|
||||
stepWidth = parseInt(interpolOptions.stepWidth, 10);
|
||||
stepCount = Math.floor(dragDistance / stepWidth)-1;
|
||||
var stepScale = stepWidth / dragDistance;
|
||||
stepVector = {x: drag.dx*stepScale, y: drag.dy*stepScale };
|
||||
}
|
||||
else {
|
||||
stepCount = parseInt(interpolOptions.stepCount, 10);
|
||||
stepWidth = dragDistance / (stepCount+1);
|
||||
stepVector = {x: drag.dx/(stepCount+1), y: drag.dy/(stepCount+1)};
|
||||
}
|
||||
|
||||
|
||||
var coords = $.extend({},start);
|
||||
|
||||
/**
|
||||
* Calculates the effective coordinates for one <code>mousemove</code> event and fires the event.
|
||||
* @requires eventTarget
|
||||
* @requires targetDoc
|
||||
* @requires coords
|
||||
* @requires stepVector
|
||||
* @requires interpolOptions
|
||||
* @fires mousemove
|
||||
* @inner
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function interpolationStep() {
|
||||
coords.x += stepVector.x;
|
||||
coords.y += stepVector.y;
|
||||
var effectiveCoords = {pageX: coords.x, pageY: coords.y};
|
||||
if (interpolOptions.shaky && (interpolOptions.shaky === true || !isNaN(parseInt(interpolOptions.shaky,10)) )) {
|
||||
var amplitude = (interpolOptions.shaky === true)? 1 : parseInt(interpolOptions.shaky,10);
|
||||
effectiveCoords.pageX += Math.floor(Math.random()*(2*amplitude+1)-amplitude);
|
||||
effectiveCoords.pageY += Math.floor(Math.random()*(2*amplitude+1)-amplitude);
|
||||
}
|
||||
var clientCoord = pageToClientPos(effectiveCoords, targetDoc),
|
||||
eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
|
||||
self.simulateEvent( eventTarget, "mousemove", {pageX: Math.round(effectiveCoords.pageX), pageY: Math.round(effectiveCoords.pageY)});
|
||||
}
|
||||
|
||||
|
||||
var lastTime;
|
||||
|
||||
/**
|
||||
* Performs one interpolation step (i.e. cares about firing the event) and then sleeps for
|
||||
* <code>stepDelay</code> milliseconds.
|
||||
* @requires lastTime
|
||||
* @requires stepDelay
|
||||
* @requires step
|
||||
* @requires ele
|
||||
* @requires eventTarget
|
||||
* @reuiqre targetDoc
|
||||
* @requires start
|
||||
* @requires drag
|
||||
* @requires now()
|
||||
* @inner
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function stepAndSleep() {
|
||||
var timeElapsed = now() - lastTime; // Work-around for Firefox & IE "bug": setTimeout can fire before the timeout
|
||||
if (timeElapsed >= stepDelay) {
|
||||
if (step < stepCount) {
|
||||
interpolationStep();
|
||||
step += 1;
|
||||
lastTime = now();
|
||||
setTimeout(stepAndSleep, stepDelay);
|
||||
}
|
||||
else {
|
||||
var pageCoord = {pageX: Math.round(start.x+drag.dx), pageY: Math.round(start.y+drag.dy)},
|
||||
clientCoord = pageToClientPos(pageCoord, targetDoc),
|
||||
eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
|
||||
self.simulateEvent( eventTarget, "mousemove", pageCoord);
|
||||
dragFinished(ele, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setTimeout(stepAndSleep, stepDelay - timeElapsed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( (!interpolOptions.stepDelay && !interpolOptions.duration) || ((interpolOptions.stepDelay <= 0) && (interpolOptions.duration <= 0)) ) {
|
||||
// Trigger as fast as possible
|
||||
for (var i=0; i < stepCount; i+=1) {
|
||||
interpolationStep();
|
||||
}
|
||||
var pageCoord = {pageX: Math.round(start.x+drag.dx), pageY: Math.round(start.y+drag.dy)},
|
||||
clientCoord = pageToClientPos(pageCoord, targetDoc),
|
||||
eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
|
||||
self.simulateEvent( eventTarget, "mousemove", pageCoord);
|
||||
dragFinished(ele, options);
|
||||
}
|
||||
else {
|
||||
var stepDelay = parseInt(interpolOptions.stepDelay,10) || Math.ceil(parseInt(interpolOptions.duration,10) / (stepCount+1));
|
||||
var step = 0;
|
||||
|
||||
lastTime = now();
|
||||
setTimeout(stepAndSleep, stepDelay);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Object|undefined} an object containing information about the currently active drag
|
||||
* or <code>undefined</code> when there is no active drag.
|
||||
* The returned object contains the following properties:
|
||||
* <ul>
|
||||
* <li><code>dragElement</code>: the dragged element</li>
|
||||
* <li><code>dragStart</code>: object with the properties <code>x</code> and <code>y</code>
|
||||
* representing the page relative start coordinates of the drag</li>
|
||||
* <li><code>dragDistance</code>: object with the properties <code>x</code> and <code>y</code>
|
||||
* representing the distance of the drag in x and y direction</li>
|
||||
* </ul>
|
||||
* @public
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
$.simulate.activeDrag = function() {
|
||||
if (!$.simulate._activeDrag) {
|
||||
return undefined;
|
||||
}
|
||||
return $.extend(true,{},$.simulate._activeDrag);
|
||||
};
|
||||
|
||||
$.extend( $.simulate.prototype,
|
||||
|
||||
/**
|
||||
* @lends $.simulate.prototype
|
||||
*/
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a drag.
|
||||
*
|
||||
* @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/drag-n-drop.md
|
||||
* @public
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
simulateDrag: function() {
|
||||
var self = this,
|
||||
ele = self.target,
|
||||
options = $.extend({
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
dragTarget: undefined,
|
||||
clickToDrag: false,
|
||||
interpolation: {
|
||||
stepWidth: 0,
|
||||
stepCount: 0,
|
||||
stepDelay: 0,
|
||||
duration: 0,
|
||||
shaky: false
|
||||
},
|
||||
callback: undefined
|
||||
}, this.options);
|
||||
|
||||
var start,
|
||||
continueDrag = ($.simulate._activeDrag && $.simulate._activeDrag.dragElement === ele);
|
||||
|
||||
if (continueDrag) {
|
||||
start = $.simulate._activeDrag.dragStart;
|
||||
}
|
||||
else {
|
||||
start = findCenter( ele );
|
||||
}
|
||||
|
||||
var x = Math.round( start.x ),
|
||||
y = Math.round( start.y ),
|
||||
coord = { pageX: x, pageY: y },
|
||||
dx,
|
||||
dy;
|
||||
|
||||
if (options.dragTarget) {
|
||||
var end = findCenter(options.dragTarget);
|
||||
dx = Math.round(end.x - start.x);
|
||||
dy = Math.round(end.y - start.y);
|
||||
}
|
||||
else {
|
||||
dx = options.dx || 0;
|
||||
dy = options.dy || 0;
|
||||
}
|
||||
|
||||
if (continueDrag) {
|
||||
// We just continue to move the dragged element
|
||||
$.simulate._activeDrag.dragDistance.x += dx;
|
||||
$.simulate._activeDrag.dragDistance.y += dy;
|
||||
coord = { pageX: Math.round(x + $.simulate._activeDrag.dragDistance.x) , pageY: Math.round(y + $.simulate._activeDrag.dragDistance.y) };
|
||||
}
|
||||
else {
|
||||
if ($.simulate._activeDrag) {
|
||||
// Drop before starting a new drag
|
||||
$($.simulate._activeDrag.dragElement).simulate( "drop" );
|
||||
}
|
||||
|
||||
// We start a new drag
|
||||
self.simulateEvent( ele, "mousedown", coord );
|
||||
if (options.clickToDrag === true) {
|
||||
self.simulateEvent( ele, "mouseup", coord );
|
||||
self.simulateEvent( ele, "click", coord );
|
||||
}
|
||||
$(ele).add(ele.ownerDocument).one('mouseup', function() {
|
||||
$.simulate._activeDrag = undefined;
|
||||
});
|
||||
|
||||
$.extend($.simulate, {
|
||||
_activeDrag: {
|
||||
dragElement: ele,
|
||||
dragStart: { x: x, y: y },
|
||||
dragDistance: { x: dx, y: dy }
|
||||
}
|
||||
});
|
||||
coord = { pageX: Math.round(x + dx), pageY: Math.round(y + dy) };
|
||||
}
|
||||
|
||||
if (dx !== 0 || dy !== 0) {
|
||||
|
||||
if ( options.interpolation && (options.interpolation.stepCount || options.interpolation.stepWidth) ) {
|
||||
interpolatedEvents(self, ele, {x: x, y: y}, {dx: dx, dy: dy}, options);
|
||||
}
|
||||
else {
|
||||
var targetDoc = selectFirstMatch([ele, ele.ownerDocument], isDocument) || document,
|
||||
clientCoord = pageToClientPos(coord, targetDoc),
|
||||
eventTarget = elementAtPosition(clientCoord, targetDoc) || ele;
|
||||
|
||||
self.simulateEvent( eventTarget, "mousemove", coord );
|
||||
dragFinished(ele, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dragFinished(ele, options);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates a drop.
|
||||
*
|
||||
* @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/drag-n-drop.md
|
||||
* @public
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
simulateDrop: function() {
|
||||
var self = this,
|
||||
ele = this.target,
|
||||
activeDrag = $.simulate._activeDrag,
|
||||
options = $.extend({
|
||||
clickToDrop: false,
|
||||
callback: undefined
|
||||
}, self.options),
|
||||
moveBeforeDrop = true,
|
||||
center = findCenter( ele ),
|
||||
x = Math.round( center.x ),
|
||||
y = Math.round( center.y ),
|
||||
coord = { pageX: x, pageY: y },
|
||||
targetDoc = ( (activeDrag)? selectFirstMatch([activeDrag.dragElement, activeDrag.dragElement.ownerDocument], isDocument) : selectFirstMatch([ele, ele.ownerDocument], isDocument) ) || document,
|
||||
clientCoord = pageToClientPos(coord, targetDoc),
|
||||
eventTarget = elementAtPosition(clientCoord, targetDoc);
|
||||
|
||||
if (activeDrag && (activeDrag.dragElement === ele || isDocument(ele))) {
|
||||
// We already moved the mouse during the drag so we just simulate the drop on the end position
|
||||
x = Math.round(activeDrag.dragStart.x + activeDrag.dragDistance.x);
|
||||
y = Math.round(activeDrag.dragStart.y + activeDrag.dragDistance.y);
|
||||
coord = { pageX: x, pageY: y };
|
||||
clientCoord = pageToClientPos(coord, targetDoc);
|
||||
eventTarget = elementAtPosition(clientCoord, targetDoc);
|
||||
moveBeforeDrop = false;
|
||||
}
|
||||
|
||||
if (!eventTarget) {
|
||||
eventTarget = (activeDrag)? activeDrag.dragElement : ele;
|
||||
}
|
||||
|
||||
if (moveBeforeDrop === true) {
|
||||
// Else we assume the drop should happen on target, so we move there
|
||||
self.simulateEvent( eventTarget, "mousemove", coord );
|
||||
}
|
||||
|
||||
if (options.clickToDrop) {
|
||||
self.simulateEvent( eventTarget, "mousedown", coord );
|
||||
}
|
||||
this.simulateEvent( eventTarget, "mouseup", coord );
|
||||
if (options.clickToDrop) {
|
||||
self.simulateEvent( eventTarget, "click", coord );
|
||||
}
|
||||
|
||||
$.simulate._activeDrag = undefined;
|
||||
$(eventTarget).trigger({type: "simulate-drop"});
|
||||
if ($.isFunction(options.callback)) {
|
||||
options.callback.apply(eventTarget);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates a drag followed by drop.
|
||||
*
|
||||
* @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/drag-n-drop.md
|
||||
* @public
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
simulateDragNDrop: function() {
|
||||
var self = this,
|
||||
ele = this.target,
|
||||
options = $.extend({
|
||||
dragTarget: undefined,
|
||||
dropTarget: undefined
|
||||
}, self.options),
|
||||
// If there is a dragTarget or dx/dy, then we drag there and simulate an independent drop on dropTarget or ele
|
||||
dropEle = ((options.dragTarget || options.dx || options.dy)? options.dropTarget : ele) || ele;
|
||||
/*
|
||||
dx = (options.dropTarget)? 0 : (options.dx || 0),
|
||||
dy = (options.dropTarget)? 0 : (options.dy || 0),
|
||||
dragDistance = { dx: dx, dy: dy };
|
||||
|
||||
$.extend(options, dragDistance);
|
||||
*/
|
||||
$(ele).simulate( "drag", $.extend({},options,{
|
||||
// If there is no dragTarget, no dx and no dy, we drag onto the dropTarget directly
|
||||
dragTarget: options.dragTarget || ((options.dx || options.dy)?undefined:options.dropTarget),
|
||||
callback: function() {
|
||||
$(dropEle).simulate( "drop", options );
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
31
test/vendor/jquery.simulate.ext.js
vendored
31
test/vendor/jquery.simulate.ext.js
vendored
@@ -1,31 +0,0 @@
|
||||
/*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
|
||||
/*jslint white: true vars: true browser: true todo: true */
|
||||
/*global jQuery:true $:true */
|
||||
|
||||
/* jQuery Simulate Extended Plugin 1.2.0
|
||||
* http://github.com/j-ulrich/jquery-simulate-ext
|
||||
*
|
||||
* Copyright (c) 2013 Jochen Ulrich
|
||||
* Licensed under the MIT license (MIT-LICENSE.txt).
|
||||
*/
|
||||
|
||||
;(function( $ ) {
|
||||
"use strict";
|
||||
|
||||
/* Overwrite the $.simulate.prototype.mouseEvent function
|
||||
* to convert pageX/Y to clientX/Y
|
||||
*/
|
||||
var originalMouseEvent = $.simulate.prototype.mouseEvent,
|
||||
rdocument = /\[object (?:HTML)?Document\]/;
|
||||
|
||||
$.simulate.prototype.mouseEvent = function(type, options) {
|
||||
if (options.pageX || options.pageY) {
|
||||
var doc = rdocument.test(Object.prototype.toString.call(this.target))? this.target : (this.target.ownerDocument || document);
|
||||
options.clientX = (options.pageX || 0) - $(doc).scrollLeft();
|
||||
options.clientY = (options.pageY || 0) - $(doc).scrollTop();
|
||||
}
|
||||
return originalMouseEvent.apply(this, [type, options]);
|
||||
};
|
||||
|
||||
|
||||
})( jQuery );
|
||||
367
test/vendor/jquery.simulate.js
vendored
367
test/vendor/jquery.simulate.js
vendored
@@ -1,367 +0,0 @@
|
||||
/*!
|
||||
* jQuery Simulate v0.0.1 - simulate browser mouse and keyboard events
|
||||
* https://github.com/jquery/jquery-simulate
|
||||
*
|
||||
* Copyright 2012 jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Date: Sun Dec 9 12:15:33 2012 -0500
|
||||
*/
|
||||
|
||||
;(function( $, undefined ) {
|
||||
"use strict";
|
||||
|
||||
var rkeyEvent = /^key/,
|
||||
rmouseEvent = /^(?:mouse|contextmenu)|click/,
|
||||
rdocument = /\[object (?:HTML)?Document\]/;
|
||||
|
||||
function isDocument(ele) {
|
||||
return rdocument.test(Object.prototype.toString.call(ele));
|
||||
}
|
||||
|
||||
function windowOfDocument(doc) {
|
||||
for (var i=0; i < window.frames.length; i+=1) {
|
||||
if (window.frames[i].document === doc) {
|
||||
return window.frames[i];
|
||||
}
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
$.fn.simulate = function( type, options ) {
|
||||
return this.each(function() {
|
||||
new $.simulate( this, type, options );
|
||||
});
|
||||
};
|
||||
|
||||
$.simulate = function( elem, type, options ) {
|
||||
var method = $.camelCase( "simulate-" + type );
|
||||
|
||||
this.target = elem;
|
||||
this.options = options || {};
|
||||
|
||||
if ( this[ method ] ) {
|
||||
this[ method ]();
|
||||
} else {
|
||||
this.simulateEvent( elem, type, this.options );
|
||||
}
|
||||
};
|
||||
|
||||
$.extend( $.simulate, {
|
||||
|
||||
keyCode: {
|
||||
BACKSPACE: 8,
|
||||
COMMA: 188,
|
||||
DELETE: 46,
|
||||
DOWN: 40,
|
||||
END: 35,
|
||||
ENTER: 13,
|
||||
ESCAPE: 27,
|
||||
HOME: 36,
|
||||
LEFT: 37,
|
||||
NUMPAD_ADD: 107,
|
||||
NUMPAD_DECIMAL: 110,
|
||||
NUMPAD_DIVIDE: 111,
|
||||
NUMPAD_ENTER: 108,
|
||||
NUMPAD_MULTIPLY: 106,
|
||||
NUMPAD_SUBTRACT: 109,
|
||||
PAGE_DOWN: 34,
|
||||
PAGE_UP: 33,
|
||||
PERIOD: 190,
|
||||
RIGHT: 39,
|
||||
SPACE: 32,
|
||||
TAB: 9,
|
||||
UP: 38
|
||||
},
|
||||
|
||||
buttonCode: {
|
||||
LEFT: 0,
|
||||
MIDDLE: 1,
|
||||
RIGHT: 2
|
||||
}
|
||||
});
|
||||
|
||||
$.extend( $.simulate.prototype, {
|
||||
|
||||
simulateEvent: function( elem, type, options ) {
|
||||
var event = this.createEvent( type, options );
|
||||
this.dispatchEvent( elem, type, event, options );
|
||||
},
|
||||
|
||||
createEvent: function( type, options ) {
|
||||
if ( rkeyEvent.test( type ) ) {
|
||||
return this.keyEvent( type, options );
|
||||
}
|
||||
|
||||
if ( rmouseEvent.test( type ) ) {
|
||||
return this.mouseEvent( type, options );
|
||||
}
|
||||
},
|
||||
|
||||
mouseEvent: function( type, options ) {
|
||||
var event,
|
||||
eventDoc,
|
||||
doc = isDocument(this.target)? this.target : (this.target.ownerDocument || document),
|
||||
docEle,
|
||||
body;
|
||||
|
||||
|
||||
options = $.extend({
|
||||
bubbles: true,
|
||||
cancelable: (type !== "mousemove"),
|
||||
view: windowOfDocument(doc),
|
||||
detail: 0,
|
||||
screenX: 0,
|
||||
screenY: 0,
|
||||
clientX: 1,
|
||||
clientY: 1,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
metaKey: false,
|
||||
button: 0,
|
||||
relatedTarget: undefined
|
||||
}, options );
|
||||
|
||||
|
||||
|
||||
if ( doc.createEvent ) {
|
||||
event = doc.createEvent( "MouseEvents" );
|
||||
event.initMouseEvent( type, options.bubbles, options.cancelable,
|
||||
options.view, options.detail,
|
||||
options.screenX, options.screenY, options.clientX, options.clientY,
|
||||
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
|
||||
options.button, options.relatedTarget || doc.body.parentNode );
|
||||
|
||||
// IE 9+ creates events with pageX and pageY set to 0.
|
||||
// Trying to modify the properties throws an error,
|
||||
// so we define getters to return the correct values.
|
||||
if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
|
||||
eventDoc = isDocument(event.relatedTarget)? event.relatedTarget : (event.relatedTarget.ownerDocument || document);
|
||||
docEle = eventDoc.documentElement;
|
||||
body = eventDoc.body;
|
||||
|
||||
Object.defineProperty( event, "pageX", {
|
||||
get: function() {
|
||||
return options.clientX +
|
||||
( docEle && docEle.scrollLeft || body && body.scrollLeft || 0 ) -
|
||||
( docEle && docEle.clientLeft || body && body.clientLeft || 0 );
|
||||
}
|
||||
});
|
||||
Object.defineProperty( event, "pageY", {
|
||||
get: function() {
|
||||
return options.clientY +
|
||||
( docEle && docEle.scrollTop || body && body.scrollTop || 0 ) -
|
||||
( docEle && docEle.clientTop || body && body.clientTop || 0 );
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if ( doc.createEventObject ) {
|
||||
event = doc.createEventObject();
|
||||
$.extend( event, options );
|
||||
// standards event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ff974877(v=vs.85).aspx
|
||||
// old IE event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ms533544(v=vs.85).aspx
|
||||
// so we actually need to map the standard back to oldIE
|
||||
event.button = {
|
||||
0: 1,
|
||||
1: 4,
|
||||
2: 2
|
||||
}[ event.button ] || event.button;
|
||||
}
|
||||
|
||||
return event;
|
||||
},
|
||||
|
||||
keyEvent: function( type, options ) {
|
||||
var event, doc;
|
||||
options = $.extend({
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
view: windowOfDocument(doc),
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
metaKey: false,
|
||||
keyCode: 0,
|
||||
charCode: undefined
|
||||
}, options );
|
||||
|
||||
doc = isDocument(this.target)? this.target : (this.target.ownerDocument || document);
|
||||
if ( doc.createEvent ) {
|
||||
try {
|
||||
event = doc.createEvent( "KeyEvents" );
|
||||
event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
|
||||
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
|
||||
options.keyCode, options.charCode );
|
||||
// initKeyEvent throws an exception in WebKit
|
||||
// see: http://stackoverflow.com/questions/6406784/initkeyevent-keypress-only-works-in-firefox-need-a-cross-browser-solution
|
||||
// and also https://bugs.webkit.org/show_bug.cgi?id=13368
|
||||
// fall back to a generic event until we decide to implement initKeyboardEvent
|
||||
} catch( err ) {
|
||||
event = doc.createEvent( "Events" );
|
||||
event.initEvent( type, options.bubbles, options.cancelable );
|
||||
$.extend( event, {
|
||||
view: options.view,
|
||||
ctrlKey: options.ctrlKey,
|
||||
altKey: options.altKey,
|
||||
shiftKey: options.shiftKey,
|
||||
metaKey: options.metaKey,
|
||||
keyCode: options.keyCode,
|
||||
charCode: options.charCode
|
||||
});
|
||||
}
|
||||
} else if ( doc.createEventObject ) {
|
||||
event = doc.createEventObject();
|
||||
$.extend( event, options );
|
||||
}
|
||||
|
||||
if ( !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) || (({}).toString.call( window.opera ) === "[object Opera]") ) {
|
||||
event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
|
||||
event.charCode = undefined;
|
||||
}
|
||||
|
||||
return event;
|
||||
},
|
||||
|
||||
dispatchEvent: function( elem, type, event ) {
|
||||
if ( elem.dispatchEvent ) {
|
||||
elem.dispatchEvent( event );
|
||||
} else if ( elem.fireEvent ) {
|
||||
elem.fireEvent( "on" + type, event );
|
||||
}
|
||||
},
|
||||
|
||||
simulateFocus: function() {
|
||||
var focusinEvent,
|
||||
triggered = false,
|
||||
element = $( this.target );
|
||||
|
||||
function trigger() {
|
||||
triggered = true;
|
||||
}
|
||||
|
||||
element.bind( "focus", trigger );
|
||||
element[ 0 ].focus();
|
||||
|
||||
if ( !triggered ) {
|
||||
focusinEvent = $.Event( "focusin" );
|
||||
focusinEvent.preventDefault();
|
||||
element.trigger( focusinEvent );
|
||||
element.triggerHandler( "focus" );
|
||||
}
|
||||
element.unbind( "focus", trigger );
|
||||
},
|
||||
|
||||
simulateBlur: function() {
|
||||
var focusoutEvent,
|
||||
triggered = false,
|
||||
element = $( this.target );
|
||||
|
||||
function trigger() {
|
||||
triggered = true;
|
||||
}
|
||||
|
||||
element.bind( "blur", trigger );
|
||||
element[ 0 ].blur();
|
||||
|
||||
// blur events are async in IE
|
||||
setTimeout(function() {
|
||||
// IE won't let the blur occur if the window is inactive
|
||||
if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
|
||||
element[ 0 ].ownerDocument.body.focus();
|
||||
}
|
||||
|
||||
// Firefox won't trigger events if the window is inactive
|
||||
// IE doesn't trigger events if we had to manually focus the body
|
||||
if ( !triggered ) {
|
||||
focusoutEvent = $.Event( "focusout" );
|
||||
focusoutEvent.preventDefault();
|
||||
element.trigger( focusoutEvent );
|
||||
element.triggerHandler( "blur" );
|
||||
}
|
||||
element.unbind( "blur", trigger );
|
||||
}, 1 );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
/** complex events **/
|
||||
|
||||
function findCenter( elem ) {
|
||||
var offset,
|
||||
$document;
|
||||
|
||||
elem = $( elem );
|
||||
if ( isDocument(elem[0]) ) {
|
||||
$document = elem;
|
||||
offset = { left: 0, top: 0 };
|
||||
}
|
||||
else {
|
||||
$document = $( elem[0].ownerDocument || document );
|
||||
offset = elem.offset();
|
||||
}
|
||||
|
||||
return {
|
||||
x: offset.left + elem.outerWidth() / 2 - $document.scrollLeft(),
|
||||
y: offset.top + elem.outerHeight() / 2 - $document.scrollTop()
|
||||
};
|
||||
}
|
||||
|
||||
function findCorner( elem ) {
|
||||
var offset,
|
||||
$document;
|
||||
elem = $( elem );
|
||||
if ( isDocument(elem[0]) ) {
|
||||
$document = elem;
|
||||
offset = { left: 0, top: 0 };
|
||||
}
|
||||
else {
|
||||
$document = $( elem[0].ownerDocument || document );
|
||||
offset = elem.offset();
|
||||
}
|
||||
|
||||
return {
|
||||
x: offset.left - document.scrollLeft(),
|
||||
y: offset.top - document.scrollTop()
|
||||
};
|
||||
}
|
||||
|
||||
$.extend( $.simulate.prototype, {
|
||||
simulateDrag: function() {
|
||||
var i = 0,
|
||||
target = this.target,
|
||||
options = this.options,
|
||||
center = options.handle === "corner" ? findCorner( target ) : findCenter( target ),
|
||||
x = Math.floor( center.x ),
|
||||
y = Math.floor( center.y ),
|
||||
coord = { clientX: x, clientY: y },
|
||||
dx = options.dx || ( options.x !== undefined ? options.x - x : 0 ),
|
||||
dy = options.dy || ( options.y !== undefined ? options.y - y : 0 ),
|
||||
moves = options.moves || 3;
|
||||
|
||||
this.simulateEvent( target, "mousedown", coord );
|
||||
|
||||
for ( ; i < moves ; i++ ) {
|
||||
x += dx / moves;
|
||||
y += dy / moves;
|
||||
|
||||
coord = {
|
||||
clientX: Math.round( x ),
|
||||
clientY: Math.round( y )
|
||||
};
|
||||
|
||||
this.simulateEvent( target.ownerDocument, "mousemove", coord );
|
||||
}
|
||||
|
||||
if ( $.contains( document, target ) ) {
|
||||
this.simulateEvent( target, "mouseup", coord );
|
||||
this.simulateEvent( target, "click", coord );
|
||||
} else {
|
||||
this.simulateEvent( document, "mouseup", coord );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})( jQuery );
|
||||
158
test/vendor/jquery.simulate.key-combo.js
vendored
158
test/vendor/jquery.simulate.key-combo.js
vendored
@@ -1,158 +0,0 @@
|
||||
/*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
|
||||
/*jslint white: true vars: true browser: true todo: true */
|
||||
/*global jQuery:true $:true */
|
||||
|
||||
/* jQuery Simulate Key-Combo Plugin 1.2.0
|
||||
* http://github.com/j-ulrich/jquery-simulate-ext
|
||||
*
|
||||
* Copyright (c) 2013 Jochen Ulrich
|
||||
* Licensed under the MIT license (MIT-LICENSE.txt).
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* For details about key events, key codes, char codes etc. see http://unixpapa.com/js/key.html
|
||||
*/
|
||||
|
||||
;(function($,undefined) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Key codes of the modifier keys.
|
||||
* @private
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
var ModifierKeyCodes = {
|
||||
SHIFT: 16,
|
||||
CONTROL: 17,
|
||||
ALT: 18,
|
||||
COMMAND: 91
|
||||
};
|
||||
|
||||
$.extend( $.simulate.prototype,
|
||||
|
||||
/**
|
||||
* @lends $.simulate.prototype
|
||||
*/
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Simulates simultaneous key presses
|
||||
*
|
||||
* @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/key-combo.md
|
||||
* @public
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
simulateKeyCombo: function() {
|
||||
var target = $(this.target),
|
||||
options = $.extend({
|
||||
combo: "",
|
||||
eventsOnly: false
|
||||
}, this.options),
|
||||
eventOptions = {},
|
||||
combo = options.combo,
|
||||
comboSplit = combo.split(/(\+)/),
|
||||
plusExpected = false,
|
||||
holdKeys = [],
|
||||
i;
|
||||
|
||||
if (combo.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Remove empty parts
|
||||
comboSplit = $.grep(comboSplit, function(part) {
|
||||
return (part !== "");
|
||||
});
|
||||
|
||||
for (i=0; i < comboSplit.length; i+=1) {
|
||||
var key = comboSplit[i],
|
||||
keyLowered = key.toLowerCase();
|
||||
|
||||
if (plusExpected) {
|
||||
if (key !== "+") {
|
||||
throw 'Syntax error: expected "+"';
|
||||
}
|
||||
}
|
||||
else {
|
||||
var keyCode;
|
||||
switch (keyLowered) {
|
||||
case "ctrl":
|
||||
case "alt":
|
||||
case "shift":
|
||||
case "meta":
|
||||
switch (keyLowered) {
|
||||
case "ctrl": keyCode = ModifierKeyCodes.CONTROL; break;
|
||||
case "alt": keyCode = ModifierKeyCodes.ALT; break;
|
||||
case "shift": keyCode = ModifierKeyCodes.SHIFT; break;
|
||||
case "meta": keyCode = ModifierKeyCodes.COMMAND; break;
|
||||
}
|
||||
eventOptions[keyLowered+"Key"] = true;
|
||||
holdKeys.unshift(keyCode);
|
||||
eventOptions.keyCode = keyCode;
|
||||
target.simulate("keydown", eventOptions);
|
||||
break;
|
||||
default:
|
||||
if (key.length > 1) {
|
||||
throw 'Syntax error: expecting "+" between each key';
|
||||
}
|
||||
else {
|
||||
keyCode = $.simulate.prototype.simulateKeySequence.prototype.charToKeyCode(key);
|
||||
holdKeys.unshift(keyCode);
|
||||
eventOptions.keyCode = keyCode;
|
||||
eventOptions.which = keyCode;
|
||||
eventOptions.charCode = undefined;
|
||||
target.simulate("keydown", eventOptions);
|
||||
if (eventOptions.shiftKey) {
|
||||
key = key.toUpperCase();
|
||||
}
|
||||
eventOptions.keyCode = key.charCodeAt(0);
|
||||
eventOptions.charCode = eventOptions.keyCode;
|
||||
eventOptions.which = eventOptions.keyCode;
|
||||
target.simulate("keypress", eventOptions);
|
||||
if (options.eventsOnly !== true && !eventOptions.ctrlKey && !eventOptions.altKey && !eventOptions.metaKey) {
|
||||
target.simulate('key-sequence', {sequence: key, triggerKeyEvents: false});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
plusExpected = !plusExpected;
|
||||
}
|
||||
|
||||
if (!plusExpected) {
|
||||
throw 'Syntax error: expected key (trailing "+"?)';
|
||||
}
|
||||
|
||||
// Release keys
|
||||
eventOptions.charCode = undefined;
|
||||
for (i=0; i < holdKeys.length; i+=1) {
|
||||
eventOptions.keyCode = holdKeys[i];
|
||||
eventOptions.which = holdKeys[i];
|
||||
switch (eventOptions.keyCode) {
|
||||
case ModifierKeyCodes.ALT:
|
||||
eventOptions.altKey = false;
|
||||
break;
|
||||
case ModifierKeyCodes.SHIFT:
|
||||
eventOptions.shiftKey = false;
|
||||
break;
|
||||
case ModifierKeyCodes.CONTROL:
|
||||
eventOptions.ctrlKey = false;
|
||||
break;
|
||||
case ModifierKeyCodes.COMMAND:
|
||||
eventOptions.metaKey = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
target.simulate("keyup", eventOptions);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}(jQuery));
|
||||
457
test/vendor/jquery.simulate.key-sequence.js
vendored
457
test/vendor/jquery.simulate.key-sequence.js
vendored
@@ -1,457 +0,0 @@
|
||||
/*jshint camelcase:true, plusplus:true, forin:true, noarg:true, noempty:true, eqeqeq:true, bitwise:true, strict:true, undef:true, unused:true, curly:true, browser:true, devel:true, maxerr:100, white:false, onevar:false */
|
||||
/*jslint white: true vars: true browser: true todo: true */
|
||||
/*global jQuery:true $:true bililiteRange:true */
|
||||
|
||||
/* jQuery Simulate Key-Sequence Plugin 1.2.0
|
||||
* http://github.com/j-ulrich/jquery-simulate-ext
|
||||
*
|
||||
* Copyright (c) 2013 Jochen Ulrich
|
||||
* Licensed under the MIT license (MIT-LICENSE.txt).
|
||||
*
|
||||
* The plugin is an extension and modification of the jQuery sendkeys plugin by Daniel Wachsstock.
|
||||
* Therefore, the original copyright notice and license follow below.
|
||||
*/
|
||||
|
||||
// insert characters in a textarea or text input field
|
||||
// special characters are enclosed in {}; use {{} for the { character itself
|
||||
// documentation: http://bililite.com/blog/2008/08/20/the-fnsendkeys-plugin/
|
||||
// Version: 2.0
|
||||
// Copyright (c) 2010 Daniel Wachsstock
|
||||
// MIT license:
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
;(function($, undefined){
|
||||
"use strict";
|
||||
|
||||
$.simulate.prototype.quirks = $.simulate.prototype.quirks || {};
|
||||
|
||||
$.extend($.simulate.prototype.quirks,
|
||||
|
||||
/**
|
||||
* @lends $.simulate.prototype.quirks
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* When simulating with delay in non-input elements,
|
||||
* all spaces are simulated at the end of the sequence instead
|
||||
* of the correct position.
|
||||
* @see {@link https://github.com/j-ulrich/jquery-simulate-ext/issues/6|issues #6}
|
||||
*/
|
||||
delayedSpacesInNonInputGlitchToEnd: undefined
|
||||
|
||||
});
|
||||
|
||||
$.extend($.simulate.prototype,
|
||||
|
||||
/**
|
||||
* @lends $.simulate.prototype
|
||||
*/
|
||||
{
|
||||
|
||||
/**
|
||||
* Simulates sequencial key strokes.
|
||||
*
|
||||
* @see https://github.com/j-ulrich/jquery-simulate-ext/blob/master/doc/key-sequence.md
|
||||
* @public
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
simulateKeySequence: function() {
|
||||
var target = this.target,
|
||||
$target = $(target),
|
||||
opts = $.extend({
|
||||
sequence: "",
|
||||
triggerKeyEvents: true,
|
||||
triggerFormEvents: true,
|
||||
delay: 0,
|
||||
callback: undefined
|
||||
}, this.options),
|
||||
sequence = opts.sequence;
|
||||
|
||||
opts.delay = parseInt(opts.delay,10);
|
||||
|
||||
var localkeys = {};
|
||||
|
||||
// Fix for #6 (https://github.com/j-ulrich/jquery-simulate-ext/issues/6)
|
||||
if ($.simulate.prototype.quirks.delayedSpacesInNonInputGlitchToEnd && !$target.is('input,textarea')) {
|
||||
$.extend(localkeys, {
|
||||
' ': function(rng, s, opts) {
|
||||
var internalOpts = $.extend({}, opts, {
|
||||
triggerKeyEvents: false,
|
||||
delay: 0,
|
||||
callback: undefined
|
||||
});
|
||||
$.simulate.prototype.simulateKeySequence.defaults.simplechar(rng, '\xA0', internalOpts);
|
||||
$.simulate.prototype.simulateKeySequence.defaults['{leftarrow}'](rng, s, internalOpts);
|
||||
$.simulate.prototype.simulateKeySequence.defaults.simplechar(rng, s, opts);
|
||||
$.simulate.prototype.simulateKeySequence.defaults['{del}'](rng, s, internalOpts);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$.extend(localkeys, opts, $target.data('simulate-keySequence')); // allow for element-specific key functions
|
||||
|
||||
// most elements to not keep track of their selection when they lose focus, so we have to do it for them
|
||||
var rng = $.data (target, 'simulate-keySequence.selection');
|
||||
if (!rng){
|
||||
rng = bililiteRange(target).bounds('selection');
|
||||
$.data(target, 'simulate-keySequence.selection', rng);
|
||||
$target.bind('mouseup.simulate-keySequence', function(){
|
||||
// we have to update the saved range. The routines here update the bounds with each press, but actual keypresses and mouseclicks do not
|
||||
$.data(target, 'simulate-keySequence.selection').bounds('selection');
|
||||
}).bind('keyup.simulate-keySequence', function(evt){
|
||||
// restore the selection if we got here with a tab (a click should select what was clicked on)
|
||||
if (evt.which === 9){
|
||||
// there's a flash of selection when we restore the focus, but I don't know how to avoid that.
|
||||
$.data(target, 'simulate-keySequence.selection').select();
|
||||
}else{
|
||||
$.data(target, 'simulate-keySequence.selection').bounds('selection');
|
||||
}
|
||||
});
|
||||
}
|
||||
target.focus();
|
||||
if (typeof sequence === 'undefined') { // no string, so we just set up the event handlers
|
||||
return;
|
||||
}
|
||||
sequence = sequence.replace(/\n/g, '{enter}'); // turn line feeds into explicit break insertions
|
||||
|
||||
/**
|
||||
* Informs the rest of the world that the sequences is finished.
|
||||
* @fires simulate-keySequence
|
||||
* @requires target
|
||||
* @requires sequence
|
||||
* @requires opts
|
||||
* @inner
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function sequenceFinished() {
|
||||
$target.trigger({type: 'simulate-keySequence', sequence: sequence});
|
||||
if ($.isFunction(opts.callback)) {
|
||||
opts.callback.apply(target, [{
|
||||
sequence: sequence
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates the key stroke for one character (or special sequence) and sleeps for
|
||||
* <code>opts.delay</code> milliseconds.
|
||||
* @requires lastTime
|
||||
* @requires now()
|
||||
* @requires tokenRegExp
|
||||
* @requires opts
|
||||
* @requires rng
|
||||
* @inner
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
function processNextToken() {
|
||||
var timeElapsed = now() - lastTime; // Work-around for Firefox "bug": setTimeout can fire before the timeout
|
||||
if (timeElapsed >= opts.delay) {
|
||||
var match = tokenRegExp.exec(sequence);
|
||||
if ( match !== null ) {
|
||||
var s = match[0];
|
||||
(localkeys[s] || $.simulate.prototype.simulateKeySequence.defaults[s] || $.simulate.prototype.simulateKeySequence.defaults.simplechar)(rng, s, opts);
|
||||
setTimeout(processNextToken, opts.delay);
|
||||
}
|
||||
else {
|
||||
sequenceFinished();
|
||||
}
|
||||
lastTime = now();
|
||||
}
|
||||
else {
|
||||
setTimeout(processNextToken, opts.delay - timeElapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts.delay || opts.delay <= 0) {
|
||||
// Run as fast as possible
|
||||
sequence.replace(/\{[^}]*\}|[^{]+/g, function(s){
|
||||
(localkeys[s] || $.simulate.prototype.simulateKeySequence.defaults[s] || $.simulate.prototype.simulateKeySequence.defaults.simplechar)(rng, s, opts);
|
||||
});
|
||||
sequenceFinished();
|
||||
}
|
||||
else {
|
||||
var tokenRegExp = /\{[^}]*\}|[^{]/g; // This matches curly bracket expressions or single characters
|
||||
var now = Date.now || function() { return new Date().getTime(); },
|
||||
lastTime = now();
|
||||
|
||||
processNextToken();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.simulate.prototype.simulateKeySequence.prototype,
|
||||
|
||||
/**
|
||||
* @lends $.simulate.prototype.simulateKeySequence.prototype
|
||||
*/
|
||||
{
|
||||
|
||||
/**
|
||||
* Maps special character char codes to IE key codes (covers IE and Webkit)
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
IEKeyCodeTable: {
|
||||
33: 49, // ! -> 1
|
||||
64: 50, // @ -> 2
|
||||
35: 51, // # -> 3
|
||||
36: 52, // $ -> 4
|
||||
37: 53, // % -> 5
|
||||
94: 54, // ^ -> 6
|
||||
38: 55, // & -> 7
|
||||
42: 56, // * -> 8
|
||||
40: 57, // ( -> 9
|
||||
41: 48, // ) -> 0
|
||||
|
||||
59: 186, // ; -> 186
|
||||
58: 186, // : -> 186
|
||||
61: 187, // = -> 187
|
||||
43: 187, // + -> 187
|
||||
44: 188, // , -> 188
|
||||
60: 188, // < -> 188
|
||||
45: 189, // - -> 189
|
||||
95: 189, // _ -> 189
|
||||
46: 190, // . -> 190
|
||||
62: 190, // > -> 190
|
||||
47: 191, // / -> 191
|
||||
63: 191, // ? -> 191
|
||||
96: 192, // ` -> 192
|
||||
126: 192, // ~ -> 192
|
||||
91: 219, // [ -> 219
|
||||
123: 219, // { -> 219
|
||||
92: 220, // \ -> 220
|
||||
124: 220, // | -> 220
|
||||
93: 221, // ] -> 221
|
||||
125: 221, // } -> 221
|
||||
39: 222, // ' -> 222
|
||||
34: 222 // " -> 222
|
||||
},
|
||||
|
||||
/**
|
||||
* Tries to convert character codes to key codes.
|
||||
* @param {Numeric} character - A character code
|
||||
* @returns {Numeric} The key code corresponding to the given character code,
|
||||
* based on the key code table of InternetExplorer. If no corresponding key code
|
||||
* could be found (which will be the case for all special characters except the common
|
||||
* ones), the character code itself is returned. However, <code>keyCode === charCode</code>
|
||||
* does not imply that no key code was found because some key codes are identical to the
|
||||
* character codes (e.g. for uppercase characters).
|
||||
* @requires $.simulate.prototype.simulateKeySequence.prototype.IEKeyCodeTable
|
||||
* @see $.simulate.prototype.simulateKeySequence.prototype.IEKeyCodeTable
|
||||
* @author julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
charToKeyCode: function(character) {
|
||||
var specialKeyCodeTable = $.simulate.prototype.simulateKeySequence.prototype.IEKeyCodeTable;
|
||||
var charCode = character.charCodeAt(0);
|
||||
|
||||
if (charCode >= 64 && charCode <= 90 || charCode >= 48 && charCode <= 57) {
|
||||
// A-Z and 0-9
|
||||
return charCode;
|
||||
}
|
||||
else if (charCode >= 97 && charCode <= 122) {
|
||||
// a-z -> A-Z
|
||||
return character.toUpperCase().charCodeAt(0);
|
||||
}
|
||||
else if (specialKeyCodeTable[charCode] !== undefined) {
|
||||
return specialKeyCodeTable[charCode];
|
||||
}
|
||||
else {
|
||||
return charCode;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// add the functions publicly so they can be overridden
|
||||
$.simulate.prototype.simulateKeySequence.defaults = {
|
||||
|
||||
/**
|
||||
* Simulates key strokes of "normal" characters (i.e. non-special sequences).
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - String of (simple) characters to be simulated.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
simplechar: function (rng, s, opts){
|
||||
rng.text(s, 'end');
|
||||
if (opts.triggerKeyEvents) {
|
||||
for (var i =0; i < s.length; i += 1){
|
||||
var charCode = s.charCodeAt(i);
|
||||
var keyCode = $.simulate.prototype.simulateKeySequence.prototype.charToKeyCode(s.charAt(i));
|
||||
// a bit of cheating: rng._el is the element associated with rng.
|
||||
$(rng._el).simulate('keydown', {keyCode: keyCode});
|
||||
$(rng._el).simulate('keypress', {keyCode: charCode, which: charCode, charCode: charCode});
|
||||
$(rng._el).simulate('keyup', {keyCode: keyCode});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates key strokes of a curly opening bracket.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - Ignored.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{{}': function (rng, s, opts){
|
||||
$.simulate.prototype.simulateKeySequence.defaults.simplechar(rng, '{', opts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates hitting the enter button.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - Ignored.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{enter}': function (rng, s, opts){
|
||||
rng.insertEOL();
|
||||
rng.select();
|
||||
|
||||
if (opts.triggerFormEvents === true) {
|
||||
var $_form = $(rng._el).parents('form');
|
||||
if ($_form.length && $_form.find('[type=submit]').length) {
|
||||
$(rng._el).on('keydown', function (event) {
|
||||
if (event.keyCode === 13 && !event.isDefaultPrevented()) {
|
||||
$_form.trigger('submit');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.triggerKeyEvents === true) {
|
||||
$(rng._el).simulate('keydown', {keyCode: 13});
|
||||
$(rng._el).simulate('keypress', {keyCode: 13, which: 13, charCode: 13});
|
||||
$(rng._el).simulate('keyup', {keyCode: 13});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates hitting the backspace button.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - Ignored.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{backspace}': function (rng, s, opts){
|
||||
var b = rng.bounds();
|
||||
if (b[0] === b[1]) { rng.bounds([b[0]-1, b[0]]); } // no characters selected; it's just an insertion point. Remove the previous character
|
||||
rng.text('', 'end'); // delete the characters and update the selection
|
||||
if (opts.triggerKeyEvents === true) {
|
||||
$(rng._el).simulate('keydown', {keyCode: 8});
|
||||
$(rng._el).simulate('keyup', {keyCode: 8});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates hitting the delete button.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - Ignored.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{del}': function (rng, s, opts){
|
||||
var b = rng.bounds();
|
||||
if (b[0] === b[1]) { rng.bounds([b[0], b[0]+1]); } // no characters selected; it's just an insertion point. Remove the next character
|
||||
rng.text('', 'end'); // delete the characters and update the selection
|
||||
if (opts.triggerKeyEvents === true) {
|
||||
$(rng._el).simulate('keydown', {keyCode: 46});
|
||||
$(rng._el).simulate('keyup', {keyCode: 46});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates hitting the right arrow button.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - Ignored.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{rightarrow}': function (rng, s, opts){
|
||||
var b = rng.bounds();
|
||||
if (b[0] === b[1]) { b[1] += 1; } // no characters selected; it's just an insertion point. Move to the right
|
||||
rng.bounds([b[1], b[1]]).select();
|
||||
if (opts.triggerKeyEvents === true) {
|
||||
$(rng._el).simulate('keydown', {keyCode: 39});
|
||||
$(rng._el).simulate('keyup', {keyCode: 39});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates hitting the left arrow button.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @param {String} s - Ignored.
|
||||
* @param {Object} opts - The key-sequence options.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{leftarrow}': function (rng, s, opts){
|
||||
var b = rng.bounds();
|
||||
if (b[0] === b[1]) { b[0] -= 1; } // no characters selected; it's just an insertion point. Move to the left
|
||||
rng.bounds([b[0], b[0]]).select();
|
||||
if (opts.triggerKeyEvents === true) {
|
||||
$(rng._el).simulate('keydown', {keyCode: 37});
|
||||
$(rng._el).simulate('keyup', {keyCode: 37});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects all characters in the target element.
|
||||
* @param {Object} rng - bililiteRange object of the simulation target element.
|
||||
* @author Daniel Wachsstock, julrich
|
||||
* @since 1.0
|
||||
*/
|
||||
'{selectall}' : function (rng){
|
||||
rng.bounds('all').select();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//####### Quirk detection #######
|
||||
if ($.simulate.ext_disableQuirkDetection !== true) { // Fixes issue #9 (https://github.com/j-ulrich/jquery-simulate-ext/issues/9)
|
||||
$(document).ready(function() {
|
||||
// delayedSpacesInNonInputGlitchToEnd
|
||||
// See issues #6 (https://github.com/j-ulrich/jquery-simulate-ext/issues/6)
|
||||
/* Append a div to the document (bililiteRange needs the element to be in the document), simulate
|
||||
* a delayed sequence containing a space in the middle and check if the space moves to the end.
|
||||
*/
|
||||
var testDiv = $('<div/>').css({height: 1, width: 1, position: 'absolute', left: -1000, top: -1000}).appendTo('body');
|
||||
testDiv.simulate('key-sequence', {sequence: '\xA0 \xA0', delay:1, callback: function() {
|
||||
$.simulate.prototype.quirks.delayedSpacesInNonInputGlitchToEnd = (testDiv.text().indexOf(' ') > 1);
|
||||
testDiv.remove();
|
||||
}});
|
||||
});
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user