- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
444 lines
16 KiB
JavaScript
444 lines
16 KiB
JavaScript
/*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 */
|
|
/*global jQuery:true $:true bililiteRange:true */
|
|
|
|
/* jQuery Simulate Key-Sequence Plugin 1.3.0
|
|
* http://github.com/j-ulrich/jquery-simulate-ext
|
|
*
|
|
* Copyright (c) 2014 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,
|
|
eventProps: {},
|
|
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', $.extend({}, opts.eventProps, {keyCode: keyCode}));
|
|
$(rng._el).simulate('keypress', $.extend({}, opts.eventProps,{keyCode: charCode, which: charCode, charCode: charCode}));
|
|
$(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {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.triggerKeyEvents === true) {
|
|
$(rng._el).simulate('keydown', $.extend({}, opts.eventProps, {keyCode: 13}));
|
|
$(rng._el).simulate('keypress', $.extend({}, opts.eventProps, {keyCode: 13, which: 13, charCode: 13}));
|
|
$(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {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', $.extend({}, opts.eventProps, {keyCode: 8}));
|
|
$(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {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', $.extend({}, opts.eventProps, {keyCode: 46}));
|
|
$(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {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', $.extend({}, opts.eventProps, {keyCode: 39}));
|
|
$(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {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', $.extend({}, opts.eventProps, {keyCode: 37}));
|
|
$(rng._el).simulate('keyup', $.extend({}, opts.eventProps, {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() === '\xA0\xA0 ');
|
|
$testDiv.remove();
|
|
}});
|
|
});
|
|
}
|
|
|
|
})(jQuery); |