24761 lines
768 KiB
JavaScript
24761 lines
768 KiB
JavaScript
/******/ (function(modules) { // webpackBootstrap
|
|
/******/ // The module cache
|
|
/******/ var installedModules = {};
|
|
|
|
/******/ // The require function
|
|
/******/ function __webpack_require__(moduleId) {
|
|
|
|
/******/ // Check if module is in cache
|
|
/******/ if(installedModules[moduleId])
|
|
/******/ return installedModules[moduleId].exports;
|
|
|
|
/******/ // Create a new module (and put it into the cache)
|
|
/******/ var module = installedModules[moduleId] = {
|
|
/******/ exports: {},
|
|
/******/ id: moduleId,
|
|
/******/ loaded: false
|
|
/******/ };
|
|
|
|
/******/ // Execute the module function
|
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
|
|
/******/ // Flag the module as loaded
|
|
/******/ module.loaded = true;
|
|
|
|
/******/ // Return the exports of the module
|
|
/******/ return module.exports;
|
|
/******/ }
|
|
|
|
|
|
/******/ // expose the modules object (__webpack_modules__)
|
|
/******/ __webpack_require__.m = modules;
|
|
|
|
/******/ // expose the module cache
|
|
/******/ __webpack_require__.c = installedModules;
|
|
|
|
/******/ // __webpack_public_path__
|
|
/******/ __webpack_require__.p = "";
|
|
|
|
/******/ // Load entry module and return exports
|
|
/******/ return __webpack_require__(0);
|
|
/******/ })
|
|
/************************************************************************/
|
|
/******/ ([
|
|
/* 0 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
var _array_visualization = __webpack_require__(1);
|
|
|
|
var _array_visualization2 = _interopRequireDefault(_array_visualization);
|
|
|
|
var _zoom_control = __webpack_require__(6);
|
|
|
|
var _zoom_control2 = _interopRequireDefault(_zoom_control);
|
|
|
|
var _overlay_control = __webpack_require__(7);
|
|
|
|
var _overlay_control2 = _interopRequireDefault(_overlay_control);
|
|
|
|
var _seismic_control = __webpack_require__(8);
|
|
|
|
var _seismic_control2 = _interopRequireDefault(_seismic_control);
|
|
|
|
var _subarray_display = __webpack_require__(9);
|
|
|
|
var _subarray_display2 = _interopRequireDefault(_subarray_display);
|
|
|
|
var _auto_upload = __webpack_require__(10);
|
|
|
|
var _auto_upload2 = _interopRequireDefault(_auto_upload);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
$(document).ready(function () {
|
|
(0, _auto_upload2.default)();
|
|
var subarrayDisplay = new _subarray_display2.default();
|
|
subarrayDisplay.init($('#current_anchors'), $('#needed_anchors'), $('#subarray_weight'), panel_data);
|
|
var arrayVisualization = new _array_visualization2.default(panel_data, is_dual_tilt, subarrayDisplay, buildings_coordinates);
|
|
arrayVisualization.init();
|
|
new _zoom_control2.default(arrayVisualization).init($('#zoom_control'));
|
|
new _overlay_control2.default(arrayVisualization).init($('#overlay_control'), $('#legend_container'));
|
|
new _seismic_control2.default(arrayVisualization, subarrayDisplay).init($('.seismic_anchor_control'), $("#seismic_save"));
|
|
window.arrayVisualization = arrayVisualization;
|
|
});
|
|
|
|
/***/ }),
|
|
/* 1 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
__webpack_require__(2);
|
|
|
|
var _jquery = __webpack_require__(3);
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
var _panel = __webpack_require__(4);
|
|
|
|
var _colors = __webpack_require__(5);
|
|
|
|
var _colors2 = _interopRequireDefault(_colors);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var ArrayVisualization = function () {
|
|
function ArrayVisualization(panelData, isDualTilt, subarrayDisplay, buildings) {
|
|
_classCallCheck(this, ArrayVisualization);
|
|
|
|
this.panelData = panelData;
|
|
this.isDualTilt = isDualTilt;
|
|
this.subarrayDisplay = subarrayDisplay;
|
|
this.buildings = buildings;
|
|
this.scale = 10;
|
|
}
|
|
|
|
_createClass(ArrayVisualization, [{
|
|
key: 'init',
|
|
value: function init() {
|
|
this.stage = new createjs.Stage("arrayCanvas");
|
|
this.container = new createjs.Container();
|
|
|
|
var background = new createjs.Shape();
|
|
background.graphics.beginFill(_colors2.default.canvas_background).drawRect(0, 0, 850, 850);
|
|
this.stage.addChild(background);
|
|
this.stage.mouseMoveOutside = true;
|
|
this.adjustScale(this.buildings);
|
|
|
|
this.drawArray(this.container, this.panelData, this.scale);
|
|
this.container.x = 0;
|
|
this.container.y = 0;
|
|
this.stage.addChild(this.container);
|
|
|
|
this.drawBuildings(this.buildings, this.scale);
|
|
|
|
this.stage.update();
|
|
|
|
var lastMove = undefined;
|
|
|
|
var self = this;
|
|
|
|
// Panning
|
|
this.stage.on("pressmove", function (event) {
|
|
var x = event.stageX;
|
|
var y = event.stageY;
|
|
if (lastMove != undefined) {
|
|
var deltaX = x - lastMove.x;
|
|
var deltaY = y - lastMove.y;
|
|
|
|
self.container.x += deltaX;
|
|
self.container.y += deltaY;
|
|
|
|
self.stage.update();
|
|
}
|
|
lastMove = { x: x, y: y };
|
|
});
|
|
|
|
this.stage.on("pressup", function () {
|
|
lastMove = undefined;
|
|
});
|
|
}
|
|
}, {
|
|
key: 'adjustScale',
|
|
value: function adjustScale(buildings) {
|
|
var _ref,
|
|
_this = this,
|
|
_ref2;
|
|
|
|
if (!buildings || buildings.length === 0) return; // leave scale as default
|
|
// we cannot determine canvas size
|
|
// we are in a test or in a very impossible state
|
|
// so we better not touch anything
|
|
if (!this.stage.canvas) return;
|
|
|
|
var all_x = (_ref = []).concat.apply(_ref, _toConsumableArray(buildings)).map(function (building) {
|
|
return building.x * _this.scale;
|
|
});
|
|
var all_y = (_ref2 = []).concat.apply(_ref2, _toConsumableArray(buildings)).map(function (building) {
|
|
return building.y * _this.scale;
|
|
});
|
|
var max_x = Math.max.apply(Math, _toConsumableArray(all_x));
|
|
var max_y = Math.max.apply(Math, _toConsumableArray(all_y));
|
|
|
|
var ratio_y = this.stage.canvas.height * 1.0 / max_y;
|
|
var ratio_x = this.stage.canvas.width * 1.0 / max_x;
|
|
|
|
var MARGIN = 0.05;
|
|
this.scale = this.scale * Math.min(ratio_y, ratio_x) * (1 - MARGIN);
|
|
}
|
|
}, {
|
|
key: 'drawBuildings',
|
|
value: function drawBuildings(buildings, scale) {
|
|
if (!buildings) {
|
|
console.log("No Buildings!");
|
|
return;
|
|
}
|
|
|
|
if (!scale) {
|
|
console.log("No Scale - don't know how big the buildings should be!");
|
|
return;
|
|
}
|
|
|
|
var line = new createjs.Shape();
|
|
var color = createjs.Graphics.getRGB(0x010101, 1);
|
|
|
|
line.graphics.setStrokeStyle(3);
|
|
|
|
for (var i = 0; i < buildings.length; i++) {
|
|
var building = buildings[i];
|
|
var firstPoint = building[0];
|
|
var nextPoint = null;
|
|
|
|
line.graphics.beginStroke(color);
|
|
|
|
line.graphics.moveTo(firstPoint.x * scale, firstPoint.y * scale);
|
|
|
|
for (var j = 1; j < building.length; j++) {
|
|
nextPoint = building[j];
|
|
line.graphics.lineTo(nextPoint.x * scale, nextPoint.y * scale);
|
|
line.graphics.moveTo(nextPoint.x * scale, nextPoint.y * scale);
|
|
}
|
|
line.graphics.lineTo(firstPoint.x * scale, firstPoint.y * scale);
|
|
line.graphics.endStroke();
|
|
}
|
|
|
|
this.container.addChild(line);
|
|
}
|
|
}, {
|
|
key: 'refreshPanels',
|
|
value: function refreshPanels(panelData) {
|
|
this.panelData = panelData;
|
|
|
|
for (var i = 0; i < this.panels.length; i++) {
|
|
this.container.removeChild(this.panels[i]);
|
|
}
|
|
|
|
this.drawArray(this.container, this.panelData, this.scale);
|
|
|
|
var selectedPanel = this.selectedPanel;
|
|
this.selectedPanel = undefined;
|
|
this.selectPanel(selectedPanel);
|
|
this.setOverlay(this.overlay);
|
|
|
|
this.subarrayDisplay.didModifyPanels(panelData);
|
|
|
|
this.stage.update();
|
|
}
|
|
}, {
|
|
key: 'drawArray',
|
|
value: function drawArray(container, panels) {
|
|
var _this2 = this;
|
|
|
|
this.panels = [];
|
|
var treatCoordinatesAsCenterpoints = this.buildings && this.buildings.length > 0;
|
|
|
|
var _loop = function _loop(i) {
|
|
var panel = panels[i];
|
|
|
|
var box = new _panel.Panel(panel, _this2.isDualTilt, _this2.scale, treatCoordinatesAsCenterpoints);
|
|
container.addChild(box);
|
|
_this2.panels.push(box);
|
|
|
|
var self = _this2;
|
|
box.on("click", function () {
|
|
self.selectPanel(i);
|
|
});
|
|
};
|
|
|
|
for (var i = 0; i < panels.length; i++) {
|
|
_loop(i);
|
|
}
|
|
}
|
|
}, {
|
|
key: 'selectPanel',
|
|
value: function selectPanel(panelIndex) {
|
|
var panel = this.panels[panelIndex];
|
|
if (this.selectedPanel !== undefined) {
|
|
var previousPanel = this.panels[this.selectedPanel];
|
|
previousPanel.deselect();
|
|
}
|
|
if (this.selectedPanel === panelIndex) {
|
|
this.selectedPanel = undefined;
|
|
} else {
|
|
this.selectedPanel = panelIndex;
|
|
panel.select();
|
|
}
|
|
this.stage.update();
|
|
}
|
|
}, {
|
|
key: 'setZoom',
|
|
value: function setZoom(zoomLevel) {
|
|
this.container.scaleX = zoomLevel;
|
|
this.container.scaleY = zoomLevel;
|
|
|
|
this.stage.update();
|
|
}
|
|
}, {
|
|
key: 'setOverlay',
|
|
value: function setOverlay(overlay) {
|
|
this.overlay = overlay;
|
|
this.panels.forEach(function (panel) {
|
|
panel.setOverlay(overlay);
|
|
});
|
|
|
|
this.stage.update();
|
|
}
|
|
}, {
|
|
key: 'addSeismicAnchor',
|
|
value: function addSeismicAnchor() {
|
|
if (this.selectedPanel !== undefined) {
|
|
this.panelData[this.selectedPanel].data.seismic_anchors++;
|
|
this.panels[this.selectedPanel].redrawOverlays();
|
|
this.stage.update();
|
|
this.subarrayDisplay.didModifyPanels(this.panelData);
|
|
}
|
|
}
|
|
}, {
|
|
key: 'removeSeismicAnchor',
|
|
value: function removeSeismicAnchor() {
|
|
if (this.selectedPanel !== undefined) {
|
|
var seismicAnchors = this.panelData[this.selectedPanel].data.seismic_anchors;
|
|
if (seismicAnchors > 0) {
|
|
this.panelData[this.selectedPanel].data.seismic_anchors--;
|
|
this.panels[this.selectedPanel].redrawOverlays();
|
|
this.stage.update();
|
|
this.subarrayDisplay.didModifyPanels(this.panelData);
|
|
}
|
|
}
|
|
}
|
|
}]);
|
|
|
|
return ArrayVisualization;
|
|
}();
|
|
|
|
exports.default = ArrayVisualization;
|
|
|
|
/***/ }),
|
|
/* 2 */
|
|
/***/ (function(module, exports) {
|
|
|
|
"use strict";
|
|
|
|
/*** IMPORTS FROM imports-loader ***/
|
|
(function () {
|
|
|
|
/*!
|
|
* EaselJS
|
|
* Visit http://createjs.com/ for documentation, updates and examples.
|
|
*
|
|
* Copyright (c) 2010 gskinner.com, inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
//##############################################################################
|
|
// extend.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
/**
|
|
* @class Utility Methods
|
|
*/
|
|
|
|
/**
|
|
* Sets up the prototype chain and constructor property for a new class.
|
|
*
|
|
* This should be called right after creating the class constructor.
|
|
*
|
|
* function MySubClass() {}
|
|
* createjs.extend(MySubClass, MySuperClass);
|
|
* MySubClass.prototype.doSomething = function() { }
|
|
*
|
|
* var foo = new MySubClass();
|
|
* console.log(foo instanceof MySuperClass); // true
|
|
* console.log(foo.prototype.constructor === MySubClass); // true
|
|
*
|
|
* @method extend
|
|
* @param {Function} subclass The subclass.
|
|
* @param {Function} superclass The superclass to extend.
|
|
* @return {Function} Returns the subclass's new prototype.
|
|
*/
|
|
createjs.extend = function (subclass, superclass) {
|
|
"use strict";
|
|
|
|
function o() {
|
|
this.constructor = subclass;
|
|
}
|
|
o.prototype = superclass.prototype;
|
|
return subclass.prototype = new o();
|
|
};
|
|
|
|
//##############################################################################
|
|
// promote.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
/**
|
|
* @class Utility Methods
|
|
*/
|
|
|
|
/**
|
|
* Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
|
|
* It is recommended to use the super class's name as the prefix.
|
|
* An alias to the super class's constructor is always added in the format `prefix_constructor`.
|
|
* This allows the subclass to call super class methods without using `function.call`, providing better performance.
|
|
*
|
|
* For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
|
|
* would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
|
|
* prototype of `MySubClass` as `MySuperClass_draw`.
|
|
*
|
|
* This should be called after the class's prototype is fully defined.
|
|
*
|
|
* function ClassA(name) {
|
|
* this.name = name;
|
|
* }
|
|
* ClassA.prototype.greet = function() {
|
|
* return "Hello "+this.name;
|
|
* }
|
|
*
|
|
* function ClassB(name, punctuation) {
|
|
* this.ClassA_constructor(name);
|
|
* this.punctuation = punctuation;
|
|
* }
|
|
* createjs.extend(ClassB, ClassA);
|
|
* ClassB.prototype.greet = function() {
|
|
* return this.ClassA_greet()+this.punctuation;
|
|
* }
|
|
* createjs.promote(ClassB, "ClassA");
|
|
*
|
|
* var foo = new ClassB("World", "!?!");
|
|
* console.log(foo.greet()); // Hello World!?!
|
|
*
|
|
* @method promote
|
|
* @param {Function} subclass The class to promote super class methods on.
|
|
* @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
|
|
* @return {Function} Returns the subclass.
|
|
*/
|
|
createjs.promote = function (subclass, prefix) {
|
|
"use strict";
|
|
|
|
var subP = subclass.prototype,
|
|
supP = Object.getPrototypeOf && Object.getPrototypeOf(subP) || subP.__proto__;
|
|
if (supP) {
|
|
subP[(prefix += "_") + "constructor"] = supP.constructor; // constructor is not always innumerable
|
|
for (var n in supP) {
|
|
if (subP.hasOwnProperty(n) && typeof supP[n] == "function") {
|
|
subP[prefix + n] = supP[n];
|
|
}
|
|
}
|
|
}
|
|
return subclass;
|
|
};
|
|
|
|
//##############################################################################
|
|
// indexOf.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
/**
|
|
* @class Utility Methods
|
|
*/
|
|
|
|
/**
|
|
* Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
|
|
* that value. Returns -1 if value is not found.
|
|
*
|
|
* var i = createjs.indexOf(myArray, myElementToFind);
|
|
*
|
|
* @method indexOf
|
|
* @param {Array} array Array to search for searchElement
|
|
* @param searchElement Element to find in array.
|
|
* @return {Number} The first index of searchElement in array.
|
|
*/
|
|
createjs.indexOf = function (array, searchElement) {
|
|
"use strict";
|
|
|
|
for (var i = 0, l = array.length; i < l; i++) {
|
|
if (searchElement === array[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
//##############################################################################
|
|
// Event.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Contains properties and methods shared by all events for use with
|
|
* {{#crossLink "EventDispatcher"}}{{/crossLink}}.
|
|
*
|
|
* Note that Event objects are often reused, so you should never
|
|
* rely on an event object's state outside of the call stack it was received in.
|
|
* @class Event
|
|
* @param {String} type The event type.
|
|
* @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
|
|
* @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
|
|
* @constructor
|
|
**/
|
|
|
|
function Event(type, bubbles, cancelable) {
|
|
|
|
// public properties:
|
|
/**
|
|
* The type of event.
|
|
* @property type
|
|
* @type String
|
|
**/
|
|
this.type = type;
|
|
|
|
/**
|
|
* The object that generated an event.
|
|
* @property target
|
|
* @type Object
|
|
* @default null
|
|
* @readonly
|
|
*/
|
|
this.target = null;
|
|
|
|
/**
|
|
* The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
|
|
* always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
|
|
* is generated from childObj, then a listener on parentObj would receive the event with
|
|
* target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
|
|
* @property currentTarget
|
|
* @type Object
|
|
* @default null
|
|
* @readonly
|
|
*/
|
|
this.currentTarget = null;
|
|
|
|
/**
|
|
* For bubbling events, this indicates the current event phase:<OL>
|
|
* <LI> capture phase: starting from the top parent to the target</LI>
|
|
* <LI> at target phase: currently being dispatched from the target</LI>
|
|
* <LI> bubbling phase: from the target to the top parent</LI>
|
|
* </OL>
|
|
* @property eventPhase
|
|
* @type Number
|
|
* @default 0
|
|
* @readonly
|
|
*/
|
|
this.eventPhase = 0;
|
|
|
|
/**
|
|
* Indicates whether the event will bubble through the display list.
|
|
* @property bubbles
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.bubbles = !!bubbles;
|
|
|
|
/**
|
|
* Indicates whether the default behaviour of this event can be cancelled via
|
|
* {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
|
|
* @property cancelable
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.cancelable = !!cancelable;
|
|
|
|
/**
|
|
* The epoch time at which this event was created.
|
|
* @property timeStamp
|
|
* @type Number
|
|
* @default 0
|
|
* @readonly
|
|
*/
|
|
this.timeStamp = new Date().getTime();
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
|
|
* on this event.
|
|
* @property defaultPrevented
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.defaultPrevented = false;
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
|
|
* {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
|
|
* @property propagationStopped
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.propagationStopped = false;
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
|
|
* on this event.
|
|
* @property immediatePropagationStopped
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.immediatePropagationStopped = false;
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
|
|
* @property removed
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.removed = false;
|
|
}
|
|
var p = Event.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true if the event is cancelable.
|
|
* Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will
|
|
* cancel the default behaviour associated with the event.
|
|
* @method preventDefault
|
|
**/
|
|
p.preventDefault = function () {
|
|
this.defaultPrevented = this.cancelable && true;
|
|
};
|
|
|
|
/**
|
|
* Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
|
|
* Mirrors the DOM event standard.
|
|
* @method stopPropagation
|
|
**/
|
|
p.stopPropagation = function () {
|
|
this.propagationStopped = true;
|
|
};
|
|
|
|
/**
|
|
* Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
|
|
* {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
|
|
* Mirrors the DOM event standard.
|
|
* @method stopImmediatePropagation
|
|
**/
|
|
p.stopImmediatePropagation = function () {
|
|
this.immediatePropagationStopped = this.propagationStopped = true;
|
|
};
|
|
|
|
/**
|
|
* Causes the active listener to be removed via removeEventListener();
|
|
*
|
|
* myBtn.addEventListener("click", function(evt) {
|
|
* // do stuff...
|
|
* evt.remove(); // removes this listener.
|
|
* });
|
|
*
|
|
* @method remove
|
|
**/
|
|
p.remove = function () {
|
|
this.removed = true;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Event instance.
|
|
* @method clone
|
|
* @return {Event} a clone of the Event instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new Event(this.type, this.bubbles, this.cancelable);
|
|
};
|
|
|
|
/**
|
|
* Provides a chainable shortcut method for setting a number of properties on the instance.
|
|
*
|
|
* @method set
|
|
* @param {Object} props A generic object containing properties to copy to the instance.
|
|
* @return {Event} Returns the instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
*/
|
|
p.set = function (props) {
|
|
for (var n in props) {
|
|
this[n] = props[n];
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Event (type=" + this.type + ")]";
|
|
};
|
|
|
|
createjs.Event = Event;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// EventDispatcher.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* EventDispatcher provides methods for managing queues of event listeners and dispatching events.
|
|
*
|
|
* You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the
|
|
* EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method.
|
|
*
|
|
* Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the
|
|
* DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports
|
|
* bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent.
|
|
*
|
|
* EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier
|
|
* to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The
|
|
* {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to
|
|
* {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}.
|
|
*
|
|
* Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}}
|
|
* method, which can be used to listeners for all events, or listeners for a specific event. The Event object also
|
|
* includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener.
|
|
*
|
|
* <h4>Example</h4>
|
|
* Add EventDispatcher capabilities to the "MyClass" class.
|
|
*
|
|
* EventDispatcher.initialize(MyClass.prototype);
|
|
*
|
|
* Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
|
|
*
|
|
* instance.addEventListener("eventName", handlerMethod);
|
|
* function handlerMethod(event) {
|
|
* console.log(event.target + " Was Clicked");
|
|
* }
|
|
*
|
|
* <b>Maintaining proper scope</b><br />
|
|
* Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
|
|
* method to subscribe to events simplifies this.
|
|
*
|
|
* instance.addEventListener("click", function(event) {
|
|
* console.log(instance == this); // false, scope is ambiguous.
|
|
* });
|
|
*
|
|
* instance.on("click", function(event) {
|
|
* console.log(instance == this); // true, "on" uses dispatcher scope by default.
|
|
* });
|
|
*
|
|
* If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
|
|
* scope.
|
|
*
|
|
* <b>Browser support</b>
|
|
* The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
|
|
* requires modern browsers (IE9+).
|
|
*
|
|
*
|
|
* @class EventDispatcher
|
|
* @constructor
|
|
**/
|
|
|
|
function EventDispatcher() {
|
|
|
|
// private properties:
|
|
/**
|
|
* @protected
|
|
* @property _listeners
|
|
* @type Object
|
|
**/
|
|
this._listeners = null;
|
|
|
|
/**
|
|
* @protected
|
|
* @property _captureListeners
|
|
* @type Object
|
|
**/
|
|
this._captureListeners = null;
|
|
}
|
|
var p = EventDispatcher.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// static public methods:
|
|
/**
|
|
* Static initializer to mix EventDispatcher methods into a target object or prototype.
|
|
*
|
|
* EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
|
|
* EventDispatcher.initialize(myObject); // add to a specific instance
|
|
*
|
|
* @method initialize
|
|
* @static
|
|
* @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
|
|
* prototype.
|
|
**/
|
|
EventDispatcher.initialize = function (target) {
|
|
target.addEventListener = p.addEventListener;
|
|
target.on = p.on;
|
|
target.removeEventListener = target.off = p.removeEventListener;
|
|
target.removeAllEventListeners = p.removeAllEventListeners;
|
|
target.hasEventListener = p.hasEventListener;
|
|
target.dispatchEvent = p.dispatchEvent;
|
|
target._dispatchEvent = p._dispatchEvent;
|
|
target.willTrigger = p.willTrigger;
|
|
};
|
|
|
|
// public methods:
|
|
/**
|
|
* Adds the specified event listener. Note that adding multiple listeners to the same function will result in
|
|
* multiple callbacks getting fired.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.addEventListener("click", handleClick);
|
|
* function handleClick(event) {
|
|
* // Click happened.
|
|
* }
|
|
*
|
|
* @method addEventListener
|
|
* @param {String} type The string type of the event.
|
|
* @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
|
|
* the event is dispatched.
|
|
* @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
|
|
* @return {Function | Object} Returns the listener for chaining or assignment.
|
|
**/
|
|
p.addEventListener = function (type, listener, useCapture) {
|
|
var listeners;
|
|
if (useCapture) {
|
|
listeners = this._captureListeners = this._captureListeners || {};
|
|
} else {
|
|
listeners = this._listeners = this._listeners || {};
|
|
}
|
|
var arr = listeners[type];
|
|
if (arr) {
|
|
this.removeEventListener(type, listener, useCapture);
|
|
}
|
|
arr = listeners[type]; // remove may have deleted the array
|
|
if (!arr) {
|
|
listeners[type] = [listener];
|
|
} else {
|
|
arr.push(listener);
|
|
}
|
|
return listener;
|
|
};
|
|
|
|
/**
|
|
* A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
|
|
* only run once, associate arbitrary data with the listener, and remove the listener.
|
|
*
|
|
* This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
|
|
* The wrapper function is returned for use with `removeEventListener` (or `off`).
|
|
*
|
|
* <b>IMPORTANT:</b> To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
|
|
* {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
|
|
* to `on` with the same params will create multiple listeners.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var listener = myBtn.on("click", handleClick, null, false, {count:3});
|
|
* function handleClick(evt, data) {
|
|
* data.count -= 1;
|
|
* console.log(this == myBtn); // true - scope defaults to the dispatcher
|
|
* if (data.count == 0) {
|
|
* alert("clicked 3 times!");
|
|
* myBtn.off("click", listener);
|
|
* // alternately: evt.remove();
|
|
* }
|
|
* }
|
|
*
|
|
* @method on
|
|
* @param {String} type The string type of the event.
|
|
* @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
|
|
* the event is dispatched.
|
|
* @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
|
|
* @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
|
|
* @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
|
|
* @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
|
|
* @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
|
|
**/
|
|
p.on = function (type, listener, scope, once, data, useCapture) {
|
|
if (listener.handleEvent) {
|
|
scope = scope || listener;
|
|
listener = listener.handleEvent;
|
|
}
|
|
scope = scope || this;
|
|
return this.addEventListener(type, function (evt) {
|
|
listener.call(scope, evt, data);
|
|
once && evt.remove();
|
|
}, useCapture);
|
|
};
|
|
|
|
/**
|
|
* Removes the specified event listener.
|
|
*
|
|
* <b>Important Note:</b> that you must pass the exact function reference used when the event was added. If a proxy
|
|
* function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
|
|
* closure will not work.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.removeEventListener("click", handleClick);
|
|
*
|
|
* @method removeEventListener
|
|
* @param {String} type The string type of the event.
|
|
* @param {Function | Object} listener The listener function or object.
|
|
* @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
|
|
**/
|
|
p.removeEventListener = function (type, listener, useCapture) {
|
|
var listeners = useCapture ? this._captureListeners : this._listeners;
|
|
if (!listeners) {
|
|
return;
|
|
}
|
|
var arr = listeners[type];
|
|
if (!arr) {
|
|
return;
|
|
}
|
|
for (var i = 0, l = arr.length; i < l; i++) {
|
|
if (arr[i] == listener) {
|
|
if (l == 1) {
|
|
delete listeners[type];
|
|
} // allows for faster checks.
|
|
else {
|
|
arr.splice(i, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A shortcut to the removeEventListener method, with the same parameters and return value. This is a companion to the
|
|
* .on method.
|
|
*
|
|
* <b>IMPORTANT:</b> To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
|
|
* {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
|
|
*
|
|
* @method off
|
|
* @param {String} type The string type of the event.
|
|
* @param {Function | Object} listener The listener function or object.
|
|
* @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
|
|
**/
|
|
p.off = p.removeEventListener;
|
|
|
|
/**
|
|
* Removes all listeners for the specified type, or all listeners of all types.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* // Remove all listeners
|
|
* displayObject.removeAllEventListeners();
|
|
*
|
|
* // Remove all click listeners
|
|
* displayObject.removeAllEventListeners("click");
|
|
*
|
|
* @method removeAllEventListeners
|
|
* @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
|
|
**/
|
|
p.removeAllEventListeners = function (type) {
|
|
if (!type) {
|
|
this._listeners = this._captureListeners = null;
|
|
} else {
|
|
if (this._listeners) {
|
|
delete this._listeners[type];
|
|
}
|
|
if (this._captureListeners) {
|
|
delete this._captureListeners[type];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Dispatches the specified event to all listeners.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* // Use a string event
|
|
* this.dispatchEvent("complete");
|
|
*
|
|
* // Use an Event instance
|
|
* var event = new createjs.Event("progress");
|
|
* this.dispatchEvent(event);
|
|
*
|
|
* @method dispatchEvent
|
|
* @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
|
|
* While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
|
|
* dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
|
|
* be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
|
|
* @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
|
|
* @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
|
|
* @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
|
|
**/
|
|
p.dispatchEvent = function (eventObj, bubbles, cancelable) {
|
|
if (typeof eventObj == "string") {
|
|
// skip everything if there's no listeners and it doesn't bubble:
|
|
var listeners = this._listeners;
|
|
if (!bubbles && (!listeners || !listeners[eventObj])) {
|
|
return true;
|
|
}
|
|
eventObj = new createjs.Event(eventObj, bubbles, cancelable);
|
|
} else if (eventObj.target && eventObj.clone) {
|
|
// redispatching an active event object, so clone it:
|
|
eventObj = eventObj.clone();
|
|
}
|
|
|
|
// TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
|
|
try {
|
|
eventObj.target = this;
|
|
} catch (e) {} // try/catch allows redispatching of native events
|
|
|
|
if (!eventObj.bubbles || !this.parent) {
|
|
this._dispatchEvent(eventObj, 2);
|
|
} else {
|
|
var top = this,
|
|
list = [top];
|
|
while (top.parent) {
|
|
list.push(top = top.parent);
|
|
}
|
|
var i,
|
|
l = list.length;
|
|
|
|
// capture & atTarget
|
|
for (i = l - 1; i >= 0 && !eventObj.propagationStopped; i--) {
|
|
list[i]._dispatchEvent(eventObj, 1 + (i == 0));
|
|
}
|
|
// bubbling
|
|
for (i = 1; i < l && !eventObj.propagationStopped; i++) {
|
|
list[i]._dispatchEvent(eventObj, 3);
|
|
}
|
|
}
|
|
return !eventObj.defaultPrevented;
|
|
};
|
|
|
|
/**
|
|
* Indicates whether there is at least one listener for the specified event type.
|
|
* @method hasEventListener
|
|
* @param {String} type The string type of the event.
|
|
* @return {Boolean} Returns true if there is at least one listener for the specified event.
|
|
**/
|
|
p.hasEventListener = function (type) {
|
|
var listeners = this._listeners,
|
|
captureListeners = this._captureListeners;
|
|
return !!(listeners && listeners[type] || captureListeners && captureListeners[type]);
|
|
};
|
|
|
|
/**
|
|
* Indicates whether there is at least one listener for the specified event type on this object or any of its
|
|
* ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the
|
|
* specified type is dispatched from this object, it will trigger at least one listener.
|
|
*
|
|
* This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire
|
|
* event flow for a listener, not just this object.
|
|
* @method willTrigger
|
|
* @param {String} type The string type of the event.
|
|
* @return {Boolean} Returns `true` if there is at least one listener for the specified event.
|
|
**/
|
|
p.willTrigger = function (type) {
|
|
var o = this;
|
|
while (o) {
|
|
if (o.hasEventListener(type)) {
|
|
return true;
|
|
}
|
|
o = o.parent;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[EventDispatcher]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _dispatchEvent
|
|
* @param {Object | String | Event} eventObj
|
|
* @param {Object} eventPhase
|
|
* @protected
|
|
**/
|
|
p._dispatchEvent = function (eventObj, eventPhase) {
|
|
var l,
|
|
listeners = eventPhase == 1 ? this._captureListeners : this._listeners;
|
|
if (eventObj && listeners) {
|
|
var arr = listeners[eventObj.type];
|
|
if (!arr || !(l = arr.length)) {
|
|
return;
|
|
}
|
|
try {
|
|
eventObj.currentTarget = this;
|
|
} catch (e) {}
|
|
try {
|
|
eventObj.eventPhase = eventPhase;
|
|
} catch (e) {}
|
|
eventObj.removed = false;
|
|
|
|
arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
|
|
for (var i = 0; i < l && !eventObj.immediatePropagationStopped; i++) {
|
|
var o = arr[i];
|
|
if (o.handleEvent) {
|
|
o.handleEvent(eventObj);
|
|
} else {
|
|
o(eventObj);
|
|
}
|
|
if (eventObj.removed) {
|
|
this.off(eventObj.type, o, eventPhase == 1);
|
|
eventObj.removed = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
createjs.EventDispatcher = EventDispatcher;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Ticker.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* The Ticker provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe to the tick
|
|
* event to be notified when a set time interval has elapsed.
|
|
*
|
|
* Note that the interval that the tick event is called is a target interval, and may be broadcast at a slower interval
|
|
* when under high CPU load. The Ticker class uses a static interface (ex. `Ticker.framerate = 30;`) and
|
|
* can not be instantiated.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* createjs.Ticker.addEventListener("tick", handleTick);
|
|
* function handleTick(event) {
|
|
* // Actions carried out each tick (aka frame)
|
|
* if (!event.paused) {
|
|
* // Actions carried out when the Ticker is not paused.
|
|
* }
|
|
* }
|
|
*
|
|
* @class Ticker
|
|
* @uses EventDispatcher
|
|
* @static
|
|
**/
|
|
|
|
function Ticker() {
|
|
throw "Ticker cannot be instantiated.";
|
|
}
|
|
|
|
// constants:
|
|
/**
|
|
* In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It
|
|
* uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and
|
|
* dispatches the tick when the time is within a certain threshold.
|
|
*
|
|
* This mode has a higher variance for time between frames than {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}},
|
|
* but does not require that content be time based as with {{#crossLink "Ticker/RAF:property"}}{{/crossLink}} while
|
|
* gaining the benefits of that API (screen synch, background throttling).
|
|
*
|
|
* Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so
|
|
* framerates of 10, 12, 15, 20, and 30 work well.
|
|
*
|
|
* Falls back to {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
|
|
* supported.
|
|
* @property RAF_SYNCHED
|
|
* @static
|
|
* @type {String}
|
|
* @default "synched"
|
|
* @readonly
|
|
**/
|
|
Ticker.RAF_SYNCHED = "synched";
|
|
|
|
/**
|
|
* In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely.
|
|
* Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based.
|
|
* You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
|
|
* event object's "delta" properties to make this easier.
|
|
*
|
|
* Falls back on {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
|
|
* supported.
|
|
* @property RAF
|
|
* @static
|
|
* @type {String}
|
|
* @default "raf"
|
|
* @readonly
|
|
**/
|
|
Ticker.RAF = "raf";
|
|
|
|
/**
|
|
* In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not
|
|
* provide the benefits of requestAnimationFrame (screen synch, background throttling).
|
|
* @property TIMEOUT
|
|
* @static
|
|
* @type {String}
|
|
* @default "timeout"
|
|
* @readonly
|
|
**/
|
|
Ticker.TIMEOUT = "timeout";
|
|
|
|
// static events:
|
|
/**
|
|
* Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using
|
|
* {{#crossLink "Ticker/setPaused"}}{{/crossLink}}.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* createjs.Ticker.addEventListener("tick", handleTick);
|
|
* function handleTick(event) {
|
|
* console.log("Paused:", event.paused, event.delta);
|
|
* }
|
|
*
|
|
* @event tick
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {Boolean} paused Indicates whether the ticker is currently paused.
|
|
* @param {Number} delta The time elapsed in ms since the last tick.
|
|
* @param {Number} time The total time in ms since Ticker was initialized.
|
|
* @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example,
|
|
* you could determine the amount of time that the Ticker has been paused since initialization with `time-runTime`.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
// public static properties:
|
|
/**
|
|
* Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}, and will be removed in a future version. If true, timingMode will
|
|
* use {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} by default.
|
|
* @deprecated Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}.
|
|
* @property useRAF
|
|
* @static
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
Ticker.useRAF = false;
|
|
|
|
/**
|
|
* Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See
|
|
* {{#crossLink "Ticker/TIMEOUT"}}{{/crossLink}}, {{#crossLink "Ticker/RAF"}}{{/crossLink}}, and
|
|
* {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} for mode details.
|
|
* @property timingMode
|
|
* @static
|
|
* @type {String}
|
|
* @default Ticker.TIMEOUT
|
|
**/
|
|
Ticker.timingMode = null;
|
|
|
|
/**
|
|
* Specifies a maximum value for the delta property in the tick event object. This is useful when building time
|
|
* based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep,
|
|
* alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value
|
|
* (ex. maxDelta=50 when running at 40fps).
|
|
*
|
|
* This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta
|
|
* when using both delta and other values.
|
|
*
|
|
* If 0, there is no maximum.
|
|
* @property maxDelta
|
|
* @static
|
|
* @type {number}
|
|
* @default 0
|
|
*/
|
|
Ticker.maxDelta = 0;
|
|
|
|
/**
|
|
* When the ticker is paused, all listeners will still receive a tick event, but the <code>paused</code> property
|
|
* of the event will be `true`. Also, while paused the `runTime` will not increase. See {{#crossLink "Ticker/tick:event"}}{{/crossLink}},
|
|
* {{#crossLink "Ticker/getTime"}}{{/crossLink}}, and {{#crossLink "Ticker/getEventTime"}}{{/crossLink}} for more
|
|
* info.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* createjs.Ticker.addEventListener("tick", handleTick);
|
|
* createjs.Ticker.paused = true;
|
|
* function handleTick(event) {
|
|
* console.log(event.paused,
|
|
* createjs.Ticker.getTime(false),
|
|
* createjs.Ticker.getTime(true));
|
|
* }
|
|
*
|
|
* @property paused
|
|
* @static
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
Ticker.paused = false;
|
|
|
|
// mix-ins:
|
|
// EventDispatcher methods:
|
|
Ticker.removeEventListener = null;
|
|
Ticker.removeAllEventListeners = null;
|
|
Ticker.dispatchEvent = null;
|
|
Ticker.hasEventListener = null;
|
|
Ticker._listeners = null;
|
|
createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods.
|
|
Ticker._addEventListener = Ticker.addEventListener;
|
|
Ticker.addEventListener = function () {
|
|
!Ticker._inited && Ticker.init();
|
|
return Ticker._addEventListener.apply(Ticker, arguments);
|
|
};
|
|
|
|
// private static properties:
|
|
/**
|
|
* @property _inited
|
|
* @static
|
|
* @type {Boolean}
|
|
* @protected
|
|
**/
|
|
Ticker._inited = false;
|
|
|
|
/**
|
|
* @property _startTime
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._startTime = 0;
|
|
|
|
/**
|
|
* @property _pausedTime
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._pausedTime = 0;
|
|
|
|
/**
|
|
* The number of ticks that have passed
|
|
* @property _ticks
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._ticks = 0;
|
|
|
|
/**
|
|
* The number of ticks that have passed while Ticker has been paused
|
|
* @property _pausedTicks
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._pausedTicks = 0;
|
|
|
|
/**
|
|
* @property _interval
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._interval = 50;
|
|
|
|
/**
|
|
* @property _lastTime
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._lastTime = 0;
|
|
|
|
/**
|
|
* @property _times
|
|
* @static
|
|
* @type {Array}
|
|
* @protected
|
|
**/
|
|
Ticker._times = null;
|
|
|
|
/**
|
|
* @property _tickTimes
|
|
* @static
|
|
* @type {Array}
|
|
* @protected
|
|
**/
|
|
Ticker._tickTimes = null;
|
|
|
|
/**
|
|
* Stores the timeout or requestAnimationFrame id.
|
|
* @property _timerId
|
|
* @static
|
|
* @type {Number}
|
|
* @protected
|
|
**/
|
|
Ticker._timerId = null;
|
|
|
|
/**
|
|
* True if currently using requestAnimationFrame, false if using setTimeout. This may be different than timingMode
|
|
* if that property changed and a tick hasn't fired.
|
|
* @property _raf
|
|
* @static
|
|
* @type {Boolean}
|
|
* @protected
|
|
**/
|
|
Ticker._raf = true;
|
|
|
|
// static getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
|
|
* @method setInterval
|
|
* @static
|
|
* @param {Number} interval
|
|
* @deprecated
|
|
**/
|
|
Ticker.setInterval = function (interval) {
|
|
Ticker._interval = interval;
|
|
if (!Ticker._inited) {
|
|
return;
|
|
}
|
|
Ticker._setupTick();
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
|
|
* @method getInterval
|
|
* @static
|
|
* @return {Number}
|
|
* @deprecated
|
|
**/
|
|
Ticker.getInterval = function () {
|
|
return Ticker._interval;
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
|
|
* @method setFPS
|
|
* @static
|
|
* @param {Number} value
|
|
* @deprecated
|
|
**/
|
|
Ticker.setFPS = function (value) {
|
|
Ticker.setInterval(1000 / value);
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
|
|
* @method getFPS
|
|
* @static
|
|
* @return {Number}
|
|
* @deprecated
|
|
**/
|
|
Ticker.getFPS = function () {
|
|
return 1000 / Ticker._interval;
|
|
};
|
|
|
|
/**
|
|
* Indicates the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
|
|
* Note that actual time between ticks may be more than specified depending on CPU load.
|
|
* This property is ignored if the ticker is using the `RAF` timing mode.
|
|
* @property interval
|
|
* @static
|
|
* @type {Number}
|
|
**/
|
|
|
|
/**
|
|
* Indicates the target frame rate in frames per second (FPS). Effectively just a shortcut to `interval`, where
|
|
* `framerate == 1000/interval`.
|
|
* @property framerate
|
|
* @static
|
|
* @type {Number}
|
|
**/
|
|
try {
|
|
Object.defineProperties(Ticker, {
|
|
interval: { get: Ticker.getInterval, set: Ticker.setInterval },
|
|
framerate: { get: Ticker.getFPS, set: Ticker.setFPS }
|
|
});
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
|
|
// public static methods:
|
|
/**
|
|
* Starts the tick. This is called automatically when the first listener is added.
|
|
* @method init
|
|
* @static
|
|
**/
|
|
Ticker.init = function () {
|
|
if (Ticker._inited) {
|
|
return;
|
|
}
|
|
Ticker._inited = true;
|
|
Ticker._times = [];
|
|
Ticker._tickTimes = [];
|
|
Ticker._startTime = Ticker._getTime();
|
|
Ticker._times.push(Ticker._lastTime = 0);
|
|
Ticker.interval = Ticker._interval;
|
|
};
|
|
|
|
/**
|
|
* Stops the Ticker and removes all listeners. Use init() to restart the Ticker.
|
|
* @method reset
|
|
* @static
|
|
**/
|
|
Ticker.reset = function () {
|
|
if (Ticker._raf) {
|
|
var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame;
|
|
f && f(Ticker._timerId);
|
|
} else {
|
|
clearTimeout(Ticker._timerId);
|
|
}
|
|
Ticker.removeAllEventListeners("tick");
|
|
Ticker._timerId = Ticker._times = Ticker._tickTimes = null;
|
|
Ticker._startTime = Ticker._lastTime = Ticker._ticks = 0;
|
|
Ticker._inited = false;
|
|
};
|
|
|
|
/**
|
|
* Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS
|
|
* because it only measures the time spent within the tick execution stack.
|
|
*
|
|
* Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between
|
|
* the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that
|
|
* there may be up to 35ms of "idle" time between the end of one tick and the start of the next.
|
|
*
|
|
* Example 2: With a target FPS of 30, getFPS() returns 10fps, which indicates an average of 100ms between the end of
|
|
* one tick and the end of the next. However, getMeasuredTickTime() returns 20ms. This would indicate that something
|
|
* other than the tick is using ~80ms (another script, DOM rendering, etc).
|
|
* @method getMeasuredTickTime
|
|
* @static
|
|
* @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick.
|
|
* Defaults to the number of ticks per second. To get only the last tick's time, pass in 1.
|
|
* @return {Number} The average time spent in a tick in milliseconds.
|
|
**/
|
|
Ticker.getMeasuredTickTime = function (ticks) {
|
|
var ttl = 0,
|
|
times = Ticker._tickTimes;
|
|
if (!times || times.length < 1) {
|
|
return -1;
|
|
}
|
|
|
|
// by default, calculate average for the past ~1 second:
|
|
ticks = Math.min(times.length, ticks || Ticker.getFPS() | 0);
|
|
for (var i = 0; i < ticks; i++) {
|
|
ttl += times[i];
|
|
}
|
|
return ttl / ticks;
|
|
};
|
|
|
|
/**
|
|
* Returns the actual frames / ticks per second.
|
|
* @method getMeasuredFPS
|
|
* @static
|
|
* @param {Number} [ticks] The number of previous ticks over which to measure the actual frames / ticks per second.
|
|
* Defaults to the number of ticks per second.
|
|
* @return {Number} The actual frames / ticks per second. Depending on performance, this may differ
|
|
* from the target frames per second.
|
|
**/
|
|
Ticker.getMeasuredFPS = function (ticks) {
|
|
var times = Ticker._times;
|
|
if (!times || times.length < 2) {
|
|
return -1;
|
|
}
|
|
|
|
// by default, calculate fps for the past ~1 second:
|
|
ticks = Math.min(times.length - 1, ticks || Ticker.getFPS() | 0);
|
|
return 1000 / ((times[0] - times[ticks]) / ticks);
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "Ticker/paused:property"}}{{/crossLink}} property instead.
|
|
* @method setPaused
|
|
* @static
|
|
* @param {Boolean} value
|
|
* @deprecated
|
|
**/
|
|
Ticker.setPaused = function (value) {
|
|
// TODO: deprecated.
|
|
Ticker.paused = value;
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "Ticker/paused:property"}}{{/crossLink}} property instead.
|
|
* @method getPaused
|
|
* @static
|
|
* @return {Boolean}
|
|
* @deprecated
|
|
**/
|
|
Ticker.getPaused = function () {
|
|
// TODO: deprecated.
|
|
return Ticker.paused;
|
|
};
|
|
|
|
/**
|
|
* Returns the number of milliseconds that have elapsed since Ticker was initialized via {{#crossLink "Ticker/init"}}.
|
|
* Returns -1 if Ticker has not been initialized. For example, you could use
|
|
* this in a time synchronized animation to determine the exact amount of time that has elapsed.
|
|
* @method getTime
|
|
* @static
|
|
* @param {Boolean} [runTime=false] If true only time elapsed while Ticker was not paused will be returned.
|
|
* If false, the value returned will be total time elapsed since the first tick event listener was added.
|
|
* @return {Number} Number of milliseconds that have elapsed since Ticker was initialized or -1.
|
|
**/
|
|
Ticker.getTime = function (runTime) {
|
|
return Ticker._startTime ? Ticker._getTime() - (runTime ? Ticker._pausedTime : 0) : -1;
|
|
};
|
|
|
|
/**
|
|
* Similar to the {{#crossLink "Ticker/getTime"}}{{/crossLink}} method, but returns the time on the most recent {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
|
|
* event object.
|
|
* @method getEventTime
|
|
* @static
|
|
* @param runTime {Boolean} [runTime=false] If true, the runTime property will be returned instead of time.
|
|
* @returns {number} The time or runTime property from the most recent tick event or -1.
|
|
*/
|
|
Ticker.getEventTime = function (runTime) {
|
|
return Ticker._startTime ? (Ticker._lastTime || Ticker._startTime) - (runTime ? Ticker._pausedTime : 0) : -1;
|
|
};
|
|
|
|
/**
|
|
* Returns the number of ticks that have been broadcast by Ticker.
|
|
* @method getTicks
|
|
* @static
|
|
* @param {Boolean} pauseable Indicates whether to include ticks that would have been broadcast
|
|
* while Ticker was paused. If true only tick events broadcast while Ticker is not paused will be returned.
|
|
* If false, tick events that would have been broadcast while Ticker was paused will be included in the return
|
|
* value. The default value is false.
|
|
* @return {Number} of ticks that have been broadcast.
|
|
**/
|
|
Ticker.getTicks = function (pauseable) {
|
|
return Ticker._ticks - (pauseable ? Ticker._pausedTicks : 0);
|
|
};
|
|
|
|
// private static methods:
|
|
/**
|
|
* @method _handleSynch
|
|
* @static
|
|
* @protected
|
|
**/
|
|
Ticker._handleSynch = function () {
|
|
Ticker._timerId = null;
|
|
Ticker._setupTick();
|
|
|
|
// run if enough time has elapsed, with a little bit of flexibility to be early:
|
|
if (Ticker._getTime() - Ticker._lastTime >= (Ticker._interval - 1) * 0.97) {
|
|
Ticker._tick();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleRAF
|
|
* @static
|
|
* @protected
|
|
**/
|
|
Ticker._handleRAF = function () {
|
|
Ticker._timerId = null;
|
|
Ticker._setupTick();
|
|
Ticker._tick();
|
|
};
|
|
|
|
/**
|
|
* @method _handleTimeout
|
|
* @static
|
|
* @protected
|
|
**/
|
|
Ticker._handleTimeout = function () {
|
|
Ticker._timerId = null;
|
|
Ticker._setupTick();
|
|
Ticker._tick();
|
|
};
|
|
|
|
/**
|
|
* @method _setupTick
|
|
* @static
|
|
* @protected
|
|
**/
|
|
Ticker._setupTick = function () {
|
|
if (Ticker._timerId != null) {
|
|
return;
|
|
} // avoid duplicates
|
|
|
|
var mode = Ticker.timingMode || Ticker.useRAF && Ticker.RAF_SYNCHED;
|
|
if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) {
|
|
var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
|
|
if (f) {
|
|
Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch);
|
|
Ticker._raf = true;
|
|
return;
|
|
}
|
|
}
|
|
Ticker._raf = false;
|
|
Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval);
|
|
};
|
|
|
|
/**
|
|
* @method _tick
|
|
* @static
|
|
* @protected
|
|
**/
|
|
Ticker._tick = function () {
|
|
var paused = Ticker.paused;
|
|
var time = Ticker._getTime();
|
|
var elapsedTime = time - Ticker._lastTime;
|
|
Ticker._lastTime = time;
|
|
Ticker._ticks++;
|
|
|
|
if (paused) {
|
|
Ticker._pausedTicks++;
|
|
Ticker._pausedTime += elapsedTime;
|
|
}
|
|
|
|
if (Ticker.hasEventListener("tick")) {
|
|
var event = new createjs.Event("tick");
|
|
var maxDelta = Ticker.maxDelta;
|
|
event.delta = maxDelta && elapsedTime > maxDelta ? maxDelta : elapsedTime;
|
|
event.paused = paused;
|
|
event.time = time;
|
|
event.runTime = time - Ticker._pausedTime;
|
|
Ticker.dispatchEvent(event);
|
|
}
|
|
|
|
Ticker._tickTimes.unshift(Ticker._getTime() - time);
|
|
while (Ticker._tickTimes.length > 100) {
|
|
Ticker._tickTimes.pop();
|
|
}
|
|
|
|
Ticker._times.unshift(time);
|
|
while (Ticker._times.length > 100) {
|
|
Ticker._times.pop();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _getTime
|
|
* @static
|
|
* @protected
|
|
**/
|
|
var now = window.performance && (performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow);
|
|
Ticker._getTime = function () {
|
|
return (now && now.call(performance) || new Date().getTime()) - Ticker._startTime;
|
|
};
|
|
|
|
createjs.Ticker = Ticker;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// UID.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. <code>UID.get()</code>)
|
|
* and should not be instantiated.
|
|
* @class UID
|
|
* @static
|
|
**/
|
|
|
|
function UID() {
|
|
throw "UID cannot be instantiated";
|
|
}
|
|
|
|
// private static properties:
|
|
/**
|
|
* @property _nextID
|
|
* @type Number
|
|
* @protected
|
|
**/
|
|
UID._nextID = 0;
|
|
|
|
// public static methods:
|
|
/**
|
|
* Returns the next unique id.
|
|
* @method get
|
|
* @return {Number} The next unique id
|
|
* @static
|
|
**/
|
|
UID.get = function () {
|
|
return UID._nextID++;
|
|
};
|
|
|
|
createjs.UID = UID;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// MouseEvent.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties,
|
|
* see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings.
|
|
* @class MouseEvent
|
|
* @param {String} type The event type.
|
|
* @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
|
|
* @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
|
|
* @param {Number} stageX The normalized x position relative to the stage.
|
|
* @param {Number} stageY The normalized y position relative to the stage.
|
|
* @param {MouseEvent} nativeEvent The native DOM event related to this mouse event.
|
|
* @param {Number} pointerID The unique id for the pointer.
|
|
* @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment.
|
|
* @param {Number} rawX The raw x position relative to the stage.
|
|
* @param {Number} rawY The raw y position relative to the stage.
|
|
* @param {DisplayObject} relatedTarget The secondary target for the event.
|
|
* @extends Event
|
|
* @constructor
|
|
**/
|
|
|
|
function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY, relatedTarget) {
|
|
this.Event_constructor(type, bubbles, cancelable);
|
|
|
|
// public properties:
|
|
/**
|
|
* The normalized x position on the stage. This will always be within the range 0 to stage width.
|
|
* @property stageX
|
|
* @type Number
|
|
*/
|
|
this.stageX = stageX;
|
|
|
|
/**
|
|
* The normalized y position on the stage. This will always be within the range 0 to stage height.
|
|
* @property stageY
|
|
* @type Number
|
|
**/
|
|
this.stageY = stageY;
|
|
|
|
/**
|
|
* The raw x position relative to the stage. Normally this will be the same as the stageX value, unless
|
|
* stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
|
|
* @property rawX
|
|
* @type Number
|
|
*/
|
|
this.rawX = rawX == null ? stageX : rawX;
|
|
|
|
/**
|
|
* The raw y position relative to the stage. Normally this will be the same as the stageY value, unless
|
|
* stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
|
|
* @property rawY
|
|
* @type Number
|
|
*/
|
|
this.rawY = rawY == null ? stageY : rawY;
|
|
|
|
/**
|
|
* The native MouseEvent generated by the browser. The properties and API for this
|
|
* event may differ between browsers. This property will be null if the
|
|
* EaselJS property was not directly generated from a native MouseEvent.
|
|
* @property nativeEvent
|
|
* @type HtmlMouseEvent
|
|
* @default null
|
|
**/
|
|
this.nativeEvent = nativeEvent;
|
|
|
|
/**
|
|
* The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system
|
|
* supplied id value.
|
|
* @property pointerID
|
|
* @type {Number}
|
|
*/
|
|
this.pointerID = pointerID;
|
|
|
|
/**
|
|
* Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse.
|
|
* For touch pointers, the first pointer in the current stack will be considered the primary pointer.
|
|
* @property primary
|
|
* @type {Boolean}
|
|
*/
|
|
this.primary = !!primary;
|
|
|
|
/**
|
|
* The secondary target for the event, if applicable. This is used for mouseout/rollout
|
|
* events to indicate the object that the mouse entered from, mouseover/rollover for the object the mouse exited,
|
|
* and stagemousedown/stagemouseup events for the object that was the under the cursor, if any.
|
|
*
|
|
* Only valid interaction targets will be returned (ie. objects with mouse listeners or a cursor set).
|
|
* @property relatedTarget
|
|
* @type {DisplayObject}
|
|
*/
|
|
this.relatedTarget = relatedTarget;
|
|
}
|
|
var p = createjs.extend(MouseEvent, createjs.Event);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
|
|
* @property localX
|
|
* @type {Number}
|
|
* @readonly
|
|
*/
|
|
p._get_localX = function () {
|
|
return this.currentTarget.globalToLocal(this.rawX, this.rawY).x;
|
|
};
|
|
|
|
/**
|
|
* Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
|
|
* @property localY
|
|
* @type {Number}
|
|
* @readonly
|
|
*/
|
|
p._get_localY = function () {
|
|
return this.currentTarget.globalToLocal(this.rawX, this.rawY).y;
|
|
};
|
|
|
|
/**
|
|
* Indicates whether the event was generated by a touch input (versus a mouse input).
|
|
* @property isTouch
|
|
* @type {Boolean}
|
|
* @readonly
|
|
*/
|
|
p._get_isTouch = function () {
|
|
return this.pointerID !== -1;
|
|
};
|
|
|
|
try {
|
|
Object.defineProperties(p, {
|
|
localX: { get: p._get_localX },
|
|
localY: { get: p._get_localY },
|
|
isTouch: { get: p._get_isTouch }
|
|
});
|
|
} catch (e) {} // TODO: use Log
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a clone of the MouseEvent instance.
|
|
* @method clone
|
|
* @return {MouseEvent} a clone of the MouseEvent instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[MouseEvent (type=" + this.type + " stageX=" + this.stageX + " stageY=" + this.stageY + ")]";
|
|
};
|
|
|
|
createjs.MouseEvent = createjs.promote(MouseEvent, "Event");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Matrix2D.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Represents an affine transformation matrix, and provides tools for constructing and concatenating matrices.
|
|
*
|
|
* This matrix can be visualized as:
|
|
*
|
|
* [ a c tx
|
|
* b d ty
|
|
* 0 0 1 ]
|
|
*
|
|
* Note the locations of b and c.
|
|
*
|
|
* @class Matrix2D
|
|
* @param {Number} [a=1] Specifies the a property for the new matrix.
|
|
* @param {Number} [b=0] Specifies the b property for the new matrix.
|
|
* @param {Number} [c=0] Specifies the c property for the new matrix.
|
|
* @param {Number} [d=1] Specifies the d property for the new matrix.
|
|
* @param {Number} [tx=0] Specifies the tx property for the new matrix.
|
|
* @param {Number} [ty=0] Specifies the ty property for the new matrix.
|
|
* @constructor
|
|
**/
|
|
|
|
function Matrix2D(a, b, c, d, tx, ty) {
|
|
this.setValues(a, b, c, d, tx, ty);
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* Position (0, 0) in a 3x3 affine transformation matrix.
|
|
* @property a
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (0, 1) in a 3x3 affine transformation matrix.
|
|
* @property b
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (1, 0) in a 3x3 affine transformation matrix.
|
|
* @property c
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (1, 1) in a 3x3 affine transformation matrix.
|
|
* @property d
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (2, 0) in a 3x3 affine transformation matrix.
|
|
* @property tx
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (2, 1) in a 3x3 affine transformation matrix.
|
|
* @property ty
|
|
* @type Number
|
|
**/
|
|
}
|
|
var p = Matrix2D.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Multiplier for converting degrees to radians. Used internally by Matrix2D.
|
|
* @property DEG_TO_RAD
|
|
* @static
|
|
* @final
|
|
* @type Number
|
|
* @readonly
|
|
**/
|
|
Matrix2D.DEG_TO_RAD = Math.PI / 180;
|
|
|
|
// static public properties:
|
|
/**
|
|
* An identity matrix, representing a null transformation.
|
|
* @property identity
|
|
* @static
|
|
* @type Matrix2D
|
|
* @readonly
|
|
**/
|
|
Matrix2D.identity = null; // set at bottom of class definition.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets the specified values on this instance.
|
|
* @method setValues
|
|
* @param {Number} [a=1] Specifies the a property for the new matrix.
|
|
* @param {Number} [b=0] Specifies the b property for the new matrix.
|
|
* @param {Number} [c=0] Specifies the c property for the new matrix.
|
|
* @param {Number} [d=1] Specifies the d property for the new matrix.
|
|
* @param {Number} [tx=0] Specifies the tx property for the new matrix.
|
|
* @param {Number} [ty=0] Specifies the ty property for the new matrix.
|
|
* @return {Matrix2D} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.setValues = function (a, b, c, d, tx, ty) {
|
|
// don't forget to update docs in the constructor if these change:
|
|
this.a = a == null ? 1 : a;
|
|
this.b = b || 0;
|
|
this.c = c || 0;
|
|
this.d = d == null ? 1 : d;
|
|
this.tx = tx || 0;
|
|
this.ty = ty || 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Appends the specified matrix properties to this matrix. All parameters are required.
|
|
* This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
|
|
* @method append
|
|
* @param {Number} a
|
|
* @param {Number} b
|
|
* @param {Number} c
|
|
* @param {Number} d
|
|
* @param {Number} tx
|
|
* @param {Number} ty
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.append = function (a, b, c, d, tx, ty) {
|
|
var a1 = this.a;
|
|
var b1 = this.b;
|
|
var c1 = this.c;
|
|
var d1 = this.d;
|
|
if (a != 1 || b != 0 || c != 0 || d != 1) {
|
|
this.a = a1 * a + c1 * b;
|
|
this.b = b1 * a + d1 * b;
|
|
this.c = a1 * c + c1 * d;
|
|
this.d = b1 * c + d1 * d;
|
|
}
|
|
this.tx = a1 * tx + c1 * ty + this.tx;
|
|
this.ty = b1 * tx + d1 * ty + this.ty;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Prepends the specified matrix properties to this matrix.
|
|
* This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
|
|
* All parameters are required.
|
|
* @method prepend
|
|
* @param {Number} a
|
|
* @param {Number} b
|
|
* @param {Number} c
|
|
* @param {Number} d
|
|
* @param {Number} tx
|
|
* @param {Number} ty
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.prepend = function (a, b, c, d, tx, ty) {
|
|
var a1 = this.a;
|
|
var c1 = this.c;
|
|
var tx1 = this.tx;
|
|
|
|
this.a = a * a1 + c * this.b;
|
|
this.b = b * a1 + d * this.b;
|
|
this.c = a * c1 + c * this.d;
|
|
this.d = b * c1 + d * this.d;
|
|
this.tx = a * tx1 + c * this.ty + tx;
|
|
this.ty = b * tx1 + d * this.ty + ty;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Appends the specified matrix to this matrix.
|
|
* This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
|
|
* @method appendMatrix
|
|
* @param {Matrix2D} matrix
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.appendMatrix = function (matrix) {
|
|
return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
|
|
};
|
|
|
|
/**
|
|
* Prepends the specified matrix to this matrix.
|
|
* This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
|
|
* For example, you could calculate the combined transformation for a child object using:
|
|
*
|
|
* var o = myDisplayObject;
|
|
* var mtx = o.getMatrix();
|
|
* while (o = o.parent) {
|
|
* // prepend each parent's transformation in turn:
|
|
* o.prependMatrix(o.getMatrix());
|
|
* }
|
|
* @method prependMatrix
|
|
* @param {Matrix2D} matrix
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.prependMatrix = function (matrix) {
|
|
return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
|
|
};
|
|
|
|
/**
|
|
* Generates matrix properties from the specified display object transform properties, and appends them to this matrix.
|
|
* For example, you can use this to generate a matrix representing the transformations of a display object:
|
|
*
|
|
* var mtx = new createjs.Matrix2D();
|
|
* mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation);
|
|
* @method appendTransform
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} scaleX
|
|
* @param {Number} scaleY
|
|
* @param {Number} rotation
|
|
* @param {Number} skewX
|
|
* @param {Number} skewY
|
|
* @param {Number} regX Optional.
|
|
* @param {Number} regY Optional.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.appendTransform = function (x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
|
|
if (rotation % 360) {
|
|
var r = rotation * Matrix2D.DEG_TO_RAD;
|
|
var cos = Math.cos(r);
|
|
var sin = Math.sin(r);
|
|
} else {
|
|
cos = 1;
|
|
sin = 0;
|
|
}
|
|
|
|
if (skewX || skewY) {
|
|
// TODO: can this be combined into a single append operation?
|
|
skewX *= Matrix2D.DEG_TO_RAD;
|
|
skewY *= Matrix2D.DEG_TO_RAD;
|
|
this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
|
|
this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, 0, 0);
|
|
} else {
|
|
this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y);
|
|
}
|
|
|
|
if (regX || regY) {
|
|
// append the registration offset:
|
|
this.tx -= regX * this.a + regY * this.c;
|
|
this.ty -= regX * this.b + regY * this.d;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Generates matrix properties from the specified display object transform properties, and prepends them to this matrix.
|
|
* For example, you could calculate the combined transformation for a child object using:
|
|
*
|
|
* var o = myDisplayObject;
|
|
* var mtx = new createjs.Matrix2D();
|
|
* do {
|
|
* // prepend each parent's transformation in turn:
|
|
* mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
|
|
* } while (o = o.parent);
|
|
*
|
|
* Note that the above example would not account for {{#crossLink "DisplayObject/transformMatrix:property"}}{{/crossLink}}
|
|
* values. See {{#crossLink "Matrix2D/prependMatrix"}}{{/crossLink}} for an example that does.
|
|
* @method prependTransform
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} scaleX
|
|
* @param {Number} scaleY
|
|
* @param {Number} rotation
|
|
* @param {Number} skewX
|
|
* @param {Number} skewY
|
|
* @param {Number} regX Optional.
|
|
* @param {Number} regY Optional.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.prependTransform = function (x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
|
|
if (rotation % 360) {
|
|
var r = rotation * Matrix2D.DEG_TO_RAD;
|
|
var cos = Math.cos(r);
|
|
var sin = Math.sin(r);
|
|
} else {
|
|
cos = 1;
|
|
sin = 0;
|
|
}
|
|
|
|
if (regX || regY) {
|
|
// prepend the registration offset:
|
|
this.tx -= regX;this.ty -= regY;
|
|
}
|
|
if (skewX || skewY) {
|
|
// TODO: can this be combined into a single prepend operation?
|
|
skewX *= Matrix2D.DEG_TO_RAD;
|
|
skewY *= Matrix2D.DEG_TO_RAD;
|
|
this.prepend(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, 0, 0);
|
|
this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
|
|
} else {
|
|
this.prepend(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Applies a clockwise rotation transformation to the matrix.
|
|
* @method rotate
|
|
* @param {Number} angle The angle to rotate by, in degrees. To use a value in radians, multiply it by `180/Math.PI`.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.rotate = function (angle) {
|
|
angle = angle * Matrix2D.DEG_TO_RAD;
|
|
var cos = Math.cos(angle);
|
|
var sin = Math.sin(angle);
|
|
|
|
var a1 = this.a;
|
|
var b1 = this.b;
|
|
|
|
this.a = a1 * cos + this.c * sin;
|
|
this.b = b1 * cos + this.d * sin;
|
|
this.c = -a1 * sin + this.c * cos;
|
|
this.d = -b1 * sin + this.d * cos;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Applies a skew transformation to the matrix.
|
|
* @method skew
|
|
* @param {Number} skewX The amount to skew horizontally in degrees. To use a value in radians, multiply it by `180/Math.PI`.
|
|
* @param {Number} skewY The amount to skew vertically in degrees.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
*/
|
|
p.skew = function (skewX, skewY) {
|
|
skewX = skewX * Matrix2D.DEG_TO_RAD;
|
|
skewY = skewY * Matrix2D.DEG_TO_RAD;
|
|
this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Applies a scale transformation to the matrix.
|
|
* @method scale
|
|
* @param {Number} x The amount to scale horizontally. E.G. a value of 2 will double the size in the X direction, and 0.5 will halve it.
|
|
* @param {Number} y The amount to scale vertically.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.scale = function (x, y) {
|
|
this.a *= x;
|
|
this.b *= x;
|
|
this.c *= y;
|
|
this.d *= y;
|
|
//this.tx *= x;
|
|
//this.ty *= y;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Translates the matrix on the x and y axes.
|
|
* @method translate
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.translate = function (x, y) {
|
|
this.tx += this.a * x + this.c * y;
|
|
this.ty += this.b * x + this.d * y;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation).
|
|
* @method identity
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.identity = function () {
|
|
this.a = this.d = 1;
|
|
this.b = this.c = this.tx = this.ty = 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Inverts the matrix, causing it to perform the opposite transformation.
|
|
* @method invert
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.invert = function () {
|
|
var a1 = this.a;
|
|
var b1 = this.b;
|
|
var c1 = this.c;
|
|
var d1 = this.d;
|
|
var tx1 = this.tx;
|
|
var n = a1 * d1 - b1 * c1;
|
|
|
|
this.a = d1 / n;
|
|
this.b = -b1 / n;
|
|
this.c = -c1 / n;
|
|
this.d = a1 / n;
|
|
this.tx = (c1 * this.ty - d1 * tx1) / n;
|
|
this.ty = -(a1 * this.ty - b1 * tx1) / n;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns true if the matrix is an identity matrix.
|
|
* @method isIdentity
|
|
* @return {Boolean}
|
|
**/
|
|
p.isIdentity = function () {
|
|
return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
|
|
};
|
|
|
|
/**
|
|
* Returns true if this matrix is equal to the specified matrix (all property values are equal).
|
|
* @method equals
|
|
* @param {Matrix2D} matrix The matrix to compare.
|
|
* @return {Boolean}
|
|
**/
|
|
p.equals = function (matrix) {
|
|
return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d;
|
|
};
|
|
|
|
/**
|
|
* Transforms a point according to this matrix.
|
|
* @method transformPoint
|
|
* @param {Number} x The x component of the point to transform.
|
|
* @param {Number} y The y component of the point to transform.
|
|
* @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned.
|
|
* @return {Point} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.transformPoint = function (x, y, pt) {
|
|
pt = pt || {};
|
|
pt.x = x * this.a + y * this.c + this.tx;
|
|
pt.y = x * this.b + y * this.d + this.ty;
|
|
return pt;
|
|
};
|
|
|
|
/**
|
|
* Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values
|
|
* may not match the transform properties you used to generate the matrix, though they will produce the same visual
|
|
* results.
|
|
* @method decompose
|
|
* @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned.
|
|
* @return {Object} The target, or a new generic object with the transform properties applied.
|
|
*/
|
|
p.decompose = function (target) {
|
|
// TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative
|
|
if (target == null) {
|
|
target = {};
|
|
}
|
|
target.x = this.tx;
|
|
target.y = this.ty;
|
|
target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
|
|
target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);
|
|
|
|
var skewX = Math.atan2(-this.c, this.d);
|
|
var skewY = Math.atan2(this.b, this.a);
|
|
|
|
var delta = Math.abs(1 - skewX / skewY);
|
|
if (delta < 0.00001) {
|
|
// effectively identical, can use rotation:
|
|
target.rotation = skewY / Matrix2D.DEG_TO_RAD;
|
|
if (this.a < 0 && this.d >= 0) {
|
|
target.rotation += target.rotation <= 0 ? 180 : -180;
|
|
}
|
|
target.skewX = target.skewY = 0;
|
|
} else {
|
|
target.skewX = skewX / Matrix2D.DEG_TO_RAD;
|
|
target.skewY = skewY / Matrix2D.DEG_TO_RAD;
|
|
}
|
|
return target;
|
|
};
|
|
|
|
/**
|
|
* Copies all properties from the specified matrix to this matrix.
|
|
* @method copy
|
|
* @param {Matrix2D} matrix The matrix to copy properties from.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
*/
|
|
p.copy = function (matrix) {
|
|
return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Matrix2D instance.
|
|
* @method clone
|
|
* @return {Matrix2D} a clone of the Matrix2D instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Matrix2D (a=" + this.a + " b=" + this.b + " c=" + this.c + " d=" + this.d + " tx=" + this.tx + " ty=" + this.ty + ")]";
|
|
};
|
|
|
|
// this has to be populated after the class is defined:
|
|
Matrix2D.identity = new Matrix2D();
|
|
|
|
createjs.Matrix2D = Matrix2D;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// DisplayProps.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
/**
|
|
* Used for calculating and encapsulating display related properties.
|
|
* @class DisplayProps
|
|
* @param {Number} [visible=true] Visible value.
|
|
* @param {Number} [alpha=1] Alpha value.
|
|
* @param {Number} [shadow=null] A Shadow instance or null.
|
|
* @param {Number} [compositeOperation=null] A compositeOperation value or null.
|
|
* @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix.
|
|
* @constructor
|
|
**/
|
|
|
|
function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.setValues(visible, alpha, shadow, compositeOperation, matrix);
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* Property representing the alpha that will be applied to a display object.
|
|
* @property alpha
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Property representing the shadow that will be applied to a display object.
|
|
* @property shadow
|
|
* @type Shadow
|
|
**/
|
|
|
|
/**
|
|
* Property representing the compositeOperation that will be applied to a display object.
|
|
* You can find a list of valid composite operations at:
|
|
* <a href="https://developer.mozilla.org/en/Canvas_tutorial/Compositing">https://developer.mozilla.org/en/Canvas_tutorial/Compositing</a>
|
|
* @property compositeOperation
|
|
* @type String
|
|
**/
|
|
|
|
/**
|
|
* Property representing the value for visible that will be applied to a display object.
|
|
* @property visible
|
|
* @type Boolean
|
|
**/
|
|
|
|
/**
|
|
* The transformation matrix that will be applied to a display object.
|
|
* @property matrix
|
|
* @type Matrix2D
|
|
**/
|
|
}
|
|
var p = DisplayProps.prototype;
|
|
|
|
// initialization:
|
|
/**
|
|
* Reinitializes the instance with the specified values.
|
|
* @method setValues
|
|
* @param {Number} [visible=true] Visible value.
|
|
* @param {Number} [alpha=1] Alpha value.
|
|
* @param {Number} [shadow=null] A Shadow instance or null.
|
|
* @param {Number} [compositeOperation=null] A compositeOperation value or null.
|
|
* @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix.
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.visible = visible == null ? true : !!visible;
|
|
this.alpha = alpha == null ? 1 : alpha;
|
|
this.shadow = shadow;
|
|
this.compositeOperation = compositeOperation;
|
|
this.matrix = matrix || this.matrix && this.matrix.identity() || new createjs.Matrix2D();
|
|
return this;
|
|
};
|
|
|
|
// public methods:
|
|
/**
|
|
* Appends the specified display properties. This is generally used to apply a child's properties its parent's.
|
|
* @method append
|
|
* @param {Boolean} visible desired visible value
|
|
* @param {Number} alpha desired alpha value
|
|
* @param {Shadow} shadow desired shadow value
|
|
* @param {String} compositeOperation desired composite operation value
|
|
* @param {Matrix2D} [matrix] a Matrix2D instance
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.append = function (visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.alpha *= alpha;
|
|
this.shadow = shadow || this.shadow;
|
|
this.compositeOperation = compositeOperation || this.compositeOperation;
|
|
this.visible = this.visible && visible;
|
|
matrix && this.matrix.appendMatrix(matrix);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Prepends the specified display properties. This is generally used to apply a parent's properties to a child's.
|
|
* For example, to get the combined display properties that would be applied to a child, you could use:
|
|
*
|
|
* var o = myDisplayObject;
|
|
* var props = new createjs.DisplayProps();
|
|
* do {
|
|
* // prepend each parent's props in turn:
|
|
* props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix());
|
|
* } while (o = o.parent);
|
|
*
|
|
* @method prepend
|
|
* @param {Boolean} visible desired visible value
|
|
* @param {Number} alpha desired alpha value
|
|
* @param {Shadow} shadow desired shadow value
|
|
* @param {String} compositeOperation desired composite operation value
|
|
* @param {Matrix2D} [matrix] a Matrix2D instance
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.prepend = function (visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.alpha *= alpha;
|
|
this.shadow = this.shadow || shadow;
|
|
this.compositeOperation = this.compositeOperation || compositeOperation;
|
|
this.visible = this.visible && visible;
|
|
matrix && this.matrix.prependMatrix(matrix);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Resets this instance and its matrix to default values.
|
|
* @method identity
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.identity = function () {
|
|
this.visible = true;
|
|
this.alpha = 1;
|
|
this.shadow = this.compositeOperation = null;
|
|
this.matrix.identity();
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the DisplayProps instance. Clones the associated matrix.
|
|
* @method clone
|
|
* @return {DisplayProps} a clone of the DisplayProps instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone());
|
|
};
|
|
|
|
// private methods:
|
|
|
|
createjs.DisplayProps = DisplayProps;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Point.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Represents a point on a 2 dimensional x / y coordinate system.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var point = new createjs.Point(0, 100);
|
|
*
|
|
* @class Point
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @constructor
|
|
**/
|
|
|
|
function Point(x, y) {
|
|
this.setValues(x, y);
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* X position.
|
|
* @property x
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Y position.
|
|
* @property y
|
|
* @type Number
|
|
**/
|
|
}
|
|
var p = Point.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets the specified values on this instance.
|
|
* @method setValues
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @return {Point} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.setValues = function (x, y) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Copies all properties from the specified point to this point.
|
|
* @method copy
|
|
* @param {Point} point The point to copy properties from.
|
|
* @return {Point} This point. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.copy = function (point) {
|
|
this.x = point.x;
|
|
this.y = point.y;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Point instance.
|
|
* @method clone
|
|
* @return {Point} a clone of the Point instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new Point(this.x, this.y);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Point (x=" + this.x + " y=" + this.y + ")]";
|
|
};
|
|
|
|
createjs.Point = Point;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Rectangle.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Represents a rectangle as defined by the points (x, y) and (x+width, y+height).
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var rect = new createjs.Rectangle(0, 0, 100, 100);
|
|
*
|
|
* @class Rectangle
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @param {Number} [width=0] The width of the Rectangle.
|
|
* @param {Number} [height=0] The height of the Rectangle.
|
|
* @constructor
|
|
**/
|
|
|
|
function Rectangle(x, y, width, height) {
|
|
this.setValues(x, y, width, height);
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* X position.
|
|
* @property x
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Y position.
|
|
* @property y
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Width.
|
|
* @property width
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Height.
|
|
* @property height
|
|
* @type Number
|
|
**/
|
|
}
|
|
var p = Rectangle.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets the specified values on this instance.
|
|
* @method setValues
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @param {Number} [width=0] The width of the Rectangle.
|
|
* @param {Number} [height=0] The height of the Rectangle.
|
|
* @return {Rectangle} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.setValues = function (x, y, width, height) {
|
|
// don't forget to update docs in the constructor if these change:
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
this.width = width || 0;
|
|
this.height = height || 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Extends the rectangle's bounds to include the described point or rectangle.
|
|
* @method extend
|
|
* @param {Number} x X position of the point or rectangle.
|
|
* @param {Number} y Y position of the point or rectangle.
|
|
* @param {Number} [width=0] The width of the rectangle.
|
|
* @param {Number} [height=0] The height of the rectangle.
|
|
* @return {Rectangle} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.extend = function (x, y, width, height) {
|
|
width = width || 0;
|
|
height = height || 0;
|
|
if (x + width > this.x + this.width) {
|
|
this.width = x + width - this.x;
|
|
}
|
|
if (y + height > this.y + this.height) {
|
|
this.height = y + height - this.y;
|
|
}
|
|
if (x < this.x) {
|
|
this.width += this.x - x;this.x = x;
|
|
}
|
|
if (y < this.y) {
|
|
this.height += this.y - y;this.y = y;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adds the specified padding to the rectangle's bounds.
|
|
* @method pad
|
|
* @param {Number} top
|
|
* @param {Number} left
|
|
* @param {Number} right
|
|
* @param {Number} bottom
|
|
* @return {Rectangle} This instance. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.pad = function (top, left, bottom, right) {
|
|
this.x -= left;
|
|
this.y -= top;
|
|
this.width += left + right;
|
|
this.height += top + bottom;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Copies all properties from the specified rectangle to this rectangle.
|
|
* @method copy
|
|
* @param {Rectangle} rectangle The rectangle to copy properties from.
|
|
* @return {Rectangle} This rectangle. Useful for chaining method calls.
|
|
* @chainable
|
|
*/
|
|
p.copy = function (rectangle) {
|
|
return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
|
};
|
|
|
|
/**
|
|
* Returns true if this rectangle fully encloses the described point or rectangle.
|
|
* @method contains
|
|
* @param {Number} x X position of the point or rectangle.
|
|
* @param {Number} y Y position of the point or rectangle.
|
|
* @param {Number} [width=0] The width of the rectangle.
|
|
* @param {Number} [height=0] The height of the rectangle.
|
|
* @return {Boolean} True if the described point or rectangle is contained within this rectangle.
|
|
*/
|
|
p.contains = function (x, y, width, height) {
|
|
width = width || 0;
|
|
height = height || 0;
|
|
return x >= this.x && x + width <= this.x + this.width && y >= this.y && y + height <= this.y + this.height;
|
|
};
|
|
|
|
/**
|
|
* Returns a new rectangle which contains this rectangle and the specified rectangle.
|
|
* @method union
|
|
* @param {Rectangle} rect The rectangle to calculate a union with.
|
|
* @return {Rectangle} A new rectangle describing the union.
|
|
*/
|
|
p.union = function (rect) {
|
|
return this.clone().extend(rect.x, rect.y, rect.width, rect.height);
|
|
};
|
|
|
|
/**
|
|
* Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle,
|
|
* or null if they do not intersect.
|
|
* @method intersection
|
|
* @param {Rectangle} rect The rectangle to calculate an intersection with.
|
|
* @return {Rectangle} A new rectangle describing the intersection or null.
|
|
*/
|
|
p.intersection = function (rect) {
|
|
var x1 = rect.x,
|
|
y1 = rect.y,
|
|
x2 = x1 + rect.width,
|
|
y2 = y1 + rect.height;
|
|
if (this.x > x1) {
|
|
x1 = this.x;
|
|
}
|
|
if (this.y > y1) {
|
|
y1 = this.y;
|
|
}
|
|
if (this.x + this.width < x2) {
|
|
x2 = this.x + this.width;
|
|
}
|
|
if (this.y + this.height < y2) {
|
|
y2 = this.y + this.height;
|
|
}
|
|
return x2 <= x1 || y2 <= y1 ? null : new Rectangle(x1, y1, x2 - x1, y2 - y1);
|
|
};
|
|
|
|
/**
|
|
* Returns true if the specified rectangle intersects (has any overlap) with this rectangle.
|
|
* @method intersects
|
|
* @param {Rectangle} rect The rectangle to compare.
|
|
* @return {Boolean} True if the rectangles intersect.
|
|
*/
|
|
p.intersects = function (rect) {
|
|
return rect.x <= this.x + this.width && this.x <= rect.x + rect.width && rect.y <= this.y + this.height && this.y <= rect.y + rect.height;
|
|
};
|
|
|
|
/**
|
|
* Returns true if the width or height are equal or less than 0.
|
|
* @method isEmpty
|
|
* @return {Boolean} True if the rectangle is empty.
|
|
*/
|
|
p.isEmpty = function () {
|
|
return this.width <= 0 || this.height <= 0;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Rectangle instance.
|
|
* @method clone
|
|
* @return {Rectangle} a clone of the Rectangle instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new Rectangle(this.x, this.y, this.width, this.height);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Rectangle (x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + ")]";
|
|
};
|
|
|
|
createjs.Rectangle = Rectangle;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// ButtonHelper.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or
|
|
* {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and
|
|
* automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}},
|
|
* to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame.
|
|
*
|
|
* The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent
|
|
* garbage collection.
|
|
*
|
|
* Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit");
|
|
* myInstance.addEventListener("click", handleClick);
|
|
* function handleClick(event) {
|
|
* // Click Happened.
|
|
* }
|
|
*
|
|
* @class ButtonHelper
|
|
* @param {Sprite|MovieClip} target The instance to manage.
|
|
* @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button.
|
|
* @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button.
|
|
* @param {String} [downLabel="down"] The label or animation to go to when the user presses the button.
|
|
* @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing
|
|
* states.
|
|
* @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined,
|
|
* then the button's visible states will be used instead. Note that the same instance as the "target" argument can be
|
|
* used for the hitState.
|
|
* @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is
|
|
* null, then the default state of the hitArea will be used. *
|
|
* @constructor
|
|
*/
|
|
|
|
function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) {
|
|
if (!target.addEventListener) {
|
|
return;
|
|
}
|
|
|
|
// public properties:
|
|
/**
|
|
* The target for this button helper.
|
|
* @property target
|
|
* @type MovieClip | Sprite
|
|
* @readonly
|
|
**/
|
|
this.target = target;
|
|
|
|
/**
|
|
* The label name or frame number to display when the user mouses out of the target. Defaults to "over".
|
|
* @property overLabel
|
|
* @type String | Number
|
|
**/
|
|
this.overLabel = overLabel == null ? "over" : overLabel;
|
|
|
|
/**
|
|
* The label name or frame number to display when the user mouses over the target. Defaults to "out".
|
|
* @property outLabel
|
|
* @type String | Number
|
|
**/
|
|
this.outLabel = outLabel == null ? "out" : outLabel;
|
|
|
|
/**
|
|
* The label name or frame number to display when the user presses on the target. Defaults to "down".
|
|
* @property downLabel
|
|
* @type String | Number
|
|
**/
|
|
this.downLabel = downLabel == null ? "down" : downLabel;
|
|
|
|
/**
|
|
* If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false.
|
|
* @property play
|
|
* @default false
|
|
* @type Boolean
|
|
**/
|
|
this.play = play;
|
|
|
|
// private properties
|
|
/**
|
|
* @property _isPressed
|
|
* @type Boolean
|
|
* @protected
|
|
**/
|
|
this._isPressed = false;
|
|
|
|
/**
|
|
* @property _isOver
|
|
* @type Boolean
|
|
* @protected
|
|
**/
|
|
this._isOver = false;
|
|
|
|
/**
|
|
* @property _enabled
|
|
* @type Boolean
|
|
* @protected
|
|
**/
|
|
this._enabled = false;
|
|
|
|
// setup:
|
|
target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes.
|
|
this.enabled = true;
|
|
this.handleEvent({});
|
|
if (hitArea) {
|
|
if (hitLabel) {
|
|
hitArea.actionsEnabled = false;
|
|
hitArea.gotoAndStop && hitArea.gotoAndStop(hitLabel);
|
|
}
|
|
target.hitArea = hitArea;
|
|
}
|
|
}
|
|
var p = ButtonHelper.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
|
|
* @method setEnabled
|
|
* @param {Boolean} value
|
|
* @deprecated
|
|
**/
|
|
p.setEnabled = function (value) {
|
|
// TODO: deprecated.
|
|
if (value == this._enabled) {
|
|
return;
|
|
}
|
|
var o = this.target;
|
|
this._enabled = value;
|
|
if (value) {
|
|
o.cursor = "pointer";
|
|
o.addEventListener("rollover", this);
|
|
o.addEventListener("rollout", this);
|
|
o.addEventListener("mousedown", this);
|
|
o.addEventListener("pressup", this);
|
|
if (o._reset) {
|
|
o.__reset = o._reset;o._reset = this._reset;
|
|
}
|
|
} else {
|
|
o.cursor = null;
|
|
o.removeEventListener("rollover", this);
|
|
o.removeEventListener("rollout", this);
|
|
o.removeEventListener("mousedown", this);
|
|
o.removeEventListener("pressup", this);
|
|
if (o.__reset) {
|
|
o._reset = o.__reset;delete o.__reset;
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
|
|
* @method getEnabled
|
|
* @return {Boolean}
|
|
* @deprecated
|
|
**/
|
|
p.getEnabled = function () {
|
|
return this._enabled;
|
|
};
|
|
|
|
/**
|
|
* Enables or disables the button functionality on the target.
|
|
* @property enabled
|
|
* @type {Boolean}
|
|
**/
|
|
try {
|
|
Object.defineProperties(p, {
|
|
enabled: { get: p.getEnabled, set: p.setEnabled }
|
|
});
|
|
} catch (e) {} // TODO: use Log
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[ButtonHelper]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method handleEvent
|
|
* @param {Object} evt The mouse event to handle.
|
|
* @protected
|
|
**/
|
|
p.handleEvent = function (evt) {
|
|
var label,
|
|
t = this.target,
|
|
type = evt.type;
|
|
if (type == "mousedown") {
|
|
this._isPressed = true;
|
|
label = this.downLabel;
|
|
} else if (type == "pressup") {
|
|
this._isPressed = false;
|
|
label = this._isOver ? this.overLabel : this.outLabel;
|
|
} else if (type == "rollover") {
|
|
this._isOver = true;
|
|
label = this._isPressed ? this.downLabel : this.overLabel;
|
|
} else {
|
|
// rollout and default
|
|
this._isOver = false;
|
|
label = this._isPressed ? this.overLabel : this.outLabel;
|
|
}
|
|
if (this.play) {
|
|
t.gotoAndPlay && t.gotoAndPlay(label);
|
|
} else {
|
|
t.gotoAndStop && t.gotoAndStop(label);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Injected into target. Preserves the paused state through a reset.
|
|
* @method _reset
|
|
* @protected
|
|
**/
|
|
p._reset = function () {
|
|
// TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures.
|
|
var p = this.paused;
|
|
this.__reset();
|
|
this.paused = p;
|
|
};
|
|
|
|
createjs.ButtonHelper = ButtonHelper;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Shadow.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}}
|
|
* via its <code>shadow</code> property.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10);
|
|
*
|
|
* @class Shadow
|
|
* @constructor
|
|
* @param {String} color The color of the shadow. This can be any valid CSS color value.
|
|
* @param {Number} offsetX The x offset of the shadow in pixels.
|
|
* @param {Number} offsetY The y offset of the shadow in pixels.
|
|
* @param {Number} blur The size of the blurring effect.
|
|
**/
|
|
|
|
function Shadow(color, offsetX, offsetY, blur) {
|
|
|
|
// public properties:
|
|
/**
|
|
* The color of the shadow. This can be any valid CSS color value.
|
|
* @property color
|
|
* @type String
|
|
* @default null
|
|
*/
|
|
this.color = color || "black";
|
|
|
|
/** The x offset of the shadow.
|
|
* @property offsetX
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.offsetX = offsetX || 0;
|
|
|
|
/** The y offset of the shadow.
|
|
* @property offsetY
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.offsetY = offsetY || 0;
|
|
|
|
/** The blur of the shadow.
|
|
* @property blur
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.blur = blur || 0;
|
|
}
|
|
var p = Shadow.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// static public properties:
|
|
/**
|
|
* An identity shadow object (all properties are set to 0).
|
|
* @property identity
|
|
* @type Shadow
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
**/
|
|
Shadow.identity = new Shadow("transparent", 0, 0, 0);
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Shadow]";
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Shadow instance.
|
|
* @method clone
|
|
* @return {Shadow} A clone of the current Shadow instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new Shadow(this.color, this.offsetX, this.offsetY, this.blur);
|
|
};
|
|
|
|
createjs.Shadow = Shadow;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// SpriteSheet.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually
|
|
* animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100
|
|
* images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high).
|
|
*
|
|
* The data passed to the SpriteSheet constructor defines:
|
|
* <ol>
|
|
* <li> The source image or images to use.</li>
|
|
* <li> The positions of individual image frames.</li>
|
|
* <li> Sequences of frames that form named animations. Optional.</li>
|
|
* <li> The target playback framerate. Optional.</li>
|
|
* </ol>
|
|
* <h3>SpriteSheet Format</h3>
|
|
* SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties
|
|
* (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON.
|
|
*
|
|
* <h4>images</h4>
|
|
* An array of source images. Images can be either an HTMlimage
|
|
* instance, or a uri to an image. The former is recommended to control preloading.
|
|
*
|
|
* images: [image1, "path/to/image2.png"],
|
|
*
|
|
* <h4>frames</h4>
|
|
* Defines the individual frames. There are two supported formats for frame data:
|
|
* When all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`,
|
|
* and `count` properties.
|
|
*
|
|
* <ul>
|
|
* <li>`width` & `height` are required and specify the dimensions of the frames</li>
|
|
* <li>`regX` & `regY` indicate the registration point or "origin" of the frames</li>
|
|
* <li>`spacing` indicate the spacing between frames</li>
|
|
* <li>`margin` specify the margin around the image(s)</li>
|
|
* <li>`count` allows you to specify the total number of frames in the spritesheet; if omitted, this will
|
|
* be calculated based on the dimensions of the source images and the frames. Frames will be assigned
|
|
* indexes based on their position in the source images (left to right, top to bottom).</li>
|
|
* </ul>
|
|
*
|
|
* frames: {width:64, height:64, count:20, regX: 32, regY:64, spacing:0, margin:0}
|
|
*
|
|
* If the frames are of different sizes, use an array of frame definitions. Each definition is itself an array
|
|
* with 4 required and 3 optional entries, in the order:
|
|
*
|
|
* <ul>
|
|
* <li>The first four, `x`, `y`, `width`, and `height` are required and define the frame rectangle.</li>
|
|
* <li>The fifth, `imageIndex`, specifies the index of the source image (defaults to 0)</li>
|
|
* <li>The last two, `regX` and `regY` specify the registration point of the frame</li>
|
|
* </ul>
|
|
*
|
|
* frames: [
|
|
* // x, y, width, height, imageIndex*, regX*, regY*
|
|
* [64, 0, 96, 64],
|
|
* [0, 0, 64, 64, 1, 32, 32]
|
|
* // etc.
|
|
* ]
|
|
*
|
|
* <h4>animations</h4>
|
|
* Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an
|
|
* animation of the same name. Each animation must specify the frames to play, and may
|
|
* also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and
|
|
* the name of the `next` animation to sequence to after it completes.
|
|
*
|
|
* There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate:
|
|
* <ol>
|
|
* <li>for a single frame animation, you can simply specify the frame index
|
|
*
|
|
* animations: {
|
|
* sit: 7
|
|
* }
|
|
*
|
|
* </li>
|
|
* <li>
|
|
* for an animation of consecutive frames, you can use an array with two required, and two optional entries
|
|
* in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive.
|
|
*
|
|
* animations: {
|
|
* // start, end, next*, speed*
|
|
* run: [0, 8],
|
|
* jump: [9, 12, "run", 2]
|
|
* }
|
|
*
|
|
* </li>
|
|
* <li>
|
|
* for non-consecutive frames, you can use an object with a `frames` property defining an array of frame
|
|
* indexes to play in order. The object can also specify `next` and `speed` properties.
|
|
*
|
|
* animations: {
|
|
* walk: {
|
|
* frames: [1,2,3,3,2,1]
|
|
* },
|
|
* shoot: {
|
|
* frames: [1,4,5,6],
|
|
* next: "walk",
|
|
* speed: 0.5
|
|
* }
|
|
* }
|
|
*
|
|
* </li>
|
|
* </ol>
|
|
* <strong>Note:</strong> the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency`
|
|
* property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in
|
|
* earlier versions, but is 4x normal speed in EaselJS 0.7.0+.
|
|
*
|
|
* <h4>framerate</h4>
|
|
* Optional. Indicates the default framerate to play this spritesheet at in frames per second. See
|
|
* {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information.
|
|
*
|
|
* framerate: 20
|
|
*
|
|
* Note that the Sprite framerate will only work if the stage update method is provided with the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
|
|
* event generated by the {{#crossLink "Ticker"}}{{/crossLink}}.
|
|
*
|
|
* createjs.Ticker.on("tick", handleTick);
|
|
* function handleTick(event) {
|
|
* stage.update(event);
|
|
* }
|
|
*
|
|
* <h3>Example</h3>
|
|
* To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three
|
|
* animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and
|
|
* sequencing back to run.
|
|
*
|
|
* var data = {
|
|
* images: ["sprites.jpg"],
|
|
* frames: {width:50, height:50},
|
|
* animations: {
|
|
* stand:0,
|
|
* run:[1,5],
|
|
* jump:[6,8,"run"]
|
|
* }
|
|
* };
|
|
* var spriteSheet = new createjs.SpriteSheet(data);
|
|
* var animation = new createjs.Sprite(spriteSheet, "run");
|
|
*
|
|
* <h3>Generating SpriteSheet Images</h3>
|
|
* Spritesheets can be created manually by combining images in PhotoShop, and specifying the frame size or
|
|
* coordinates manually, however there are a number of tools that facilitate this.
|
|
* <ul>
|
|
* <li>Exporting SpriteSheets or HTML5 content from Flash Pro supports the EaselJS SpriteSheet format.</li>
|
|
* <li>The popular <a href="https://www.codeandweb.com/texturepacker/easeljs" target="_blank">Texture Packer</a> has
|
|
* EaselJS support.
|
|
* <li>SWF animations in Flash can be exported to SpriteSheets using <a href="http://createjs.com/zoe" target="_blank"></a></li>
|
|
* </ul>
|
|
*
|
|
* <h3>Cross Origin Issues</h3>
|
|
* <strong>Warning:</strong> Images loaded cross-origin will throw cross-origin security errors when interacted with
|
|
* using:
|
|
* <ul>
|
|
* <li>a mouse</li>
|
|
* <li>methods such as {{#crossLink "Container/getObjectUnderPoint"}}{{/crossLink}}</li>
|
|
* <li>Filters (see {{#crossLink "Filter"}}{{/crossLink}})</li>
|
|
* <li>caching (see {{#crossLink "DisplayObject/cache"}}{{/crossLink}})</li>
|
|
* </ul>
|
|
* You can get around this by setting `crossOrigin` property on your images before passing them to EaselJS, or
|
|
* setting the `crossOrigin` property on PreloadJS' LoadQueue or LoadItems.
|
|
*
|
|
* var image = new Image();
|
|
* img.crossOrigin="Anonymous";
|
|
* img.src = "http://server-with-CORS-support.com/path/to/image.jpg";
|
|
*
|
|
* If you pass string paths to SpriteSheets, they will not work cross-origin. The server that stores the image must
|
|
* support cross-origin requests, or this will not work. For more information, check out
|
|
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS" target="_blank">CORS overview on MDN</a>.
|
|
*
|
|
* @class SpriteSheet
|
|
* @constructor
|
|
* @param {Object} data An object describing the SpriteSheet data.
|
|
* @extends EventDispatcher
|
|
**/
|
|
|
|
function SpriteSheet(data) {
|
|
this.EventDispatcher_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* Indicates whether all images are finished loading.
|
|
* @property complete
|
|
* @type Boolean
|
|
* @readonly
|
|
**/
|
|
this.complete = true;
|
|
|
|
/**
|
|
* Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See the Sprite class
|
|
* {{#crossLink "Sprite/framerate:property"}}{{/crossLink}} for more information.
|
|
* @property framerate
|
|
* @type Number
|
|
**/
|
|
this.framerate = 0;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _animations
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._animations = null;
|
|
|
|
/**
|
|
* @property _frames
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._frames = null;
|
|
|
|
/**
|
|
* @property _images
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._images = null;
|
|
|
|
/**
|
|
* @property _data
|
|
* @protected
|
|
* @type Object
|
|
**/
|
|
this._data = null;
|
|
|
|
/**
|
|
* @property _loadCount
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._loadCount = 0;
|
|
|
|
// only used for simple frame defs:
|
|
/**
|
|
* @property _frameHeight
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._frameHeight = 0;
|
|
|
|
/**
|
|
* @property _frameWidth
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._frameWidth = 0;
|
|
|
|
/**
|
|
* @property _numFrames
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._numFrames = 0;
|
|
|
|
/**
|
|
* @property _regX
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._regX = 0;
|
|
|
|
/**
|
|
* @property _regY
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._regY = 0;
|
|
|
|
/**
|
|
* @property _spacing
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._spacing = 0;
|
|
|
|
/**
|
|
* @property _margin
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._margin = 0;
|
|
|
|
// setup:
|
|
this._parseData(data);
|
|
}
|
|
var p = createjs.extend(SpriteSheet, createjs.EventDispatcher);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when all images are loaded. Note that this only fires if the images
|
|
* were not fully loaded when the sprite sheet was initialized. You should check the complete property
|
|
* to prior to adding a listener. Ex.
|
|
*
|
|
* var sheet = new createjs.SpriteSheet(data);
|
|
* if (!sheet.complete) {
|
|
* // not preloaded, listen for the complete event:
|
|
* sheet.addEventListener("complete", handler);
|
|
* }
|
|
*
|
|
* @event complete
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when getFrame is called with a valid frame index. This is primarily intended for use by {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}}
|
|
* when doing on-demand rendering.
|
|
* @event getframe
|
|
* @param {Number} index The frame index.
|
|
* @param {Object} frame The frame object that getFrame will return.
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when an image encounters an error. A SpriteSheet will dispatch an error event for each image that
|
|
* encounters an error, and will still dispatch a {{#crossLink "SpriteSheet/complete:event"}}{{/crossLink}}
|
|
* event once all images are finished processing, even if an error is encountered.
|
|
* @event error
|
|
* @param {String} src The source of the image that failed to load.
|
|
* @since 0.8.2
|
|
*/
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "SpriteSheet/animations:property"}}{{/crossLink}} property instead.
|
|
* @method getAnimations
|
|
* @return {Array}
|
|
* @deprecated
|
|
**/
|
|
p.getAnimations = function () {
|
|
return this._animations.slice();
|
|
};
|
|
|
|
/**
|
|
* Returns an array of all available animation names available on this sprite sheet as strings.
|
|
* @property animations
|
|
* @type {Array}
|
|
* @readonly
|
|
**/
|
|
try {
|
|
Object.defineProperties(p, {
|
|
animations: { get: p.getAnimations }
|
|
});
|
|
} catch (e) {}
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns the total number of frames in the specified animation, or in the whole sprite
|
|
* sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and
|
|
* the images have not been fully loaded.
|
|
* @method getNumFrames
|
|
* @param {String} animation The name of the animation to get a frame count for.
|
|
* @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted.
|
|
*/
|
|
p.getNumFrames = function (animation) {
|
|
if (animation == null) {
|
|
return this._frames ? this._frames.length : this._numFrames || 0;
|
|
} else {
|
|
var data = this._data[animation];
|
|
if (data == null) {
|
|
return 0;
|
|
} else {
|
|
return data.frames.length;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns an object defining the specified animation. The returned object contains:<UL>
|
|
* <li>frames: an array of the frame ids in the animation</li>
|
|
* <li>speed: the playback speed for this animation</li>
|
|
* <li>name: the name of the animation</li>
|
|
* <li>next: the default animation to play next. If the animation loops, the name and next property will be the
|
|
* same.</li>
|
|
* </UL>
|
|
* @method getAnimation
|
|
* @param {String} name The name of the animation to get.
|
|
* @return {Object} a generic object with frames, speed, name, and next properties.
|
|
**/
|
|
p.getAnimation = function (name) {
|
|
return this._data[name];
|
|
};
|
|
|
|
/**
|
|
* Returns an object specifying the image and source rect of the specified frame. The returned object has:<UL>
|
|
* <li>an image property holding a reference to the image object in which the frame is found</li>
|
|
* <li>a rect property containing a Rectangle instance which defines the boundaries for the frame within that
|
|
* image.</li>
|
|
* <li> A regX and regY property corresponding to the regX/Y values for the frame.
|
|
* </UL>
|
|
* @method getFrame
|
|
* @param {Number} frameIndex The index of the frame.
|
|
* @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist.
|
|
**/
|
|
p.getFrame = function (frameIndex) {
|
|
var frame;
|
|
if (this._frames && (frame = this._frames[frameIndex])) {
|
|
return frame;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative
|
|
* to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return:
|
|
*
|
|
* [x=-50, y=-40, width=90, height=70]
|
|
*
|
|
* @method getFrameBounds
|
|
* @param {Number} frameIndex The index of the frame.
|
|
* @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created.
|
|
* @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded.
|
|
**/
|
|
p.getFrameBounds = function (frameIndex, rectangle) {
|
|
var frame = this.getFrame(frameIndex);
|
|
return frame ? (rectangle || new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[SpriteSheet]";
|
|
};
|
|
|
|
/**
|
|
* SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it.
|
|
* @method clone
|
|
**/
|
|
p.clone = function () {
|
|
throw "SpriteSheet cannot be cloned.";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _parseData
|
|
* @param {Object} data An object describing the SpriteSheet data.
|
|
* @protected
|
|
**/
|
|
p._parseData = function (data) {
|
|
var i, l, o, a;
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
|
|
this.framerate = data.framerate || 0;
|
|
|
|
// parse images:
|
|
if (data.images && (l = data.images.length) > 0) {
|
|
a = this._images = [];
|
|
for (i = 0; i < l; i++) {
|
|
var img = data.images[i];
|
|
if (typeof img == "string") {
|
|
var src = img;
|
|
img = document.createElement("img");
|
|
img.src = src;
|
|
}
|
|
a.push(img);
|
|
if (!img.getContext && !img.naturalWidth) {
|
|
this._loadCount++;
|
|
this.complete = false;
|
|
(function (o, src) {
|
|
img.onload = function () {
|
|
o._handleImageLoad(src);
|
|
};
|
|
})(this, src);
|
|
(function (o, src) {
|
|
img.onerror = function () {
|
|
o._handleImageError(src);
|
|
};
|
|
})(this, src);
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse frames:
|
|
if (data.frames == null) {// nothing
|
|
} else if (Array.isArray(data.frames)) {
|
|
this._frames = [];
|
|
a = data.frames;
|
|
for (i = 0, l = a.length; i < l; i++) {
|
|
var arr = a[i];
|
|
this._frames.push({ image: this._images[arr[4] ? arr[4] : 0], rect: new createjs.Rectangle(arr[0], arr[1], arr[2], arr[3]), regX: arr[5] || 0, regY: arr[6] || 0 });
|
|
}
|
|
} else {
|
|
o = data.frames;
|
|
this._frameWidth = o.width;
|
|
this._frameHeight = o.height;
|
|
this._regX = o.regX || 0;
|
|
this._regY = o.regY || 0;
|
|
this._spacing = o.spacing || 0;
|
|
this._margin = o.margin || 0;
|
|
this._numFrames = o.count;
|
|
if (this._loadCount == 0) {
|
|
this._calculateFrames();
|
|
}
|
|
}
|
|
|
|
// parse animations:
|
|
this._animations = [];
|
|
if ((o = data.animations) != null) {
|
|
this._data = {};
|
|
var name;
|
|
for (name in o) {
|
|
var anim = { name: name };
|
|
var obj = o[name];
|
|
if (typeof obj == "number") {
|
|
// single frame
|
|
a = anim.frames = [obj];
|
|
} else if (Array.isArray(obj)) {
|
|
// simple
|
|
if (obj.length == 1) {
|
|
anim.frames = [obj[0]];
|
|
} else {
|
|
anim.speed = obj[3];
|
|
anim.next = obj[2];
|
|
a = anim.frames = [];
|
|
for (i = obj[0]; i <= obj[1]; i++) {
|
|
a.push(i);
|
|
}
|
|
}
|
|
} else {
|
|
// complex
|
|
anim.speed = obj.speed;
|
|
anim.next = obj.next;
|
|
var frames = obj.frames;
|
|
a = anim.frames = typeof frames == "number" ? [frames] : frames.slice(0);
|
|
}
|
|
if (anim.next === true || anim.next === undefined) {
|
|
anim.next = name;
|
|
} // loop
|
|
if (anim.next === false || a.length < 2 && anim.next == name) {
|
|
anim.next = null;
|
|
} // stop
|
|
if (!anim.speed) {
|
|
anim.speed = 1;
|
|
}
|
|
this._animations.push(name);
|
|
this._data[name] = anim;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleImageLoad
|
|
* @protected
|
|
**/
|
|
p._handleImageLoad = function (src) {
|
|
if (--this._loadCount == 0) {
|
|
this._calculateFrames();
|
|
this.complete = true;
|
|
this.dispatchEvent("complete");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleImageError
|
|
* @protected
|
|
*/
|
|
p._handleImageError = function (src) {
|
|
var errorEvent = new createjs.Event("error");
|
|
errorEvent.src = src;
|
|
this.dispatchEvent(errorEvent);
|
|
|
|
// Complete is still dispatched.
|
|
if (--this._loadCount == 0) {
|
|
this.dispatchEvent("complete");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _calculateFrames
|
|
* @protected
|
|
**/
|
|
p._calculateFrames = function () {
|
|
if (this._frames || this._frameWidth == 0) {
|
|
return;
|
|
}
|
|
|
|
this._frames = [];
|
|
|
|
var maxFrames = this._numFrames || 100000; // if we go over this, something is wrong.
|
|
var frameCount = 0,
|
|
frameWidth = this._frameWidth,
|
|
frameHeight = this._frameHeight;
|
|
var spacing = this._spacing,
|
|
margin = this._margin;
|
|
|
|
imgLoop: for (var i = 0, imgs = this._images; i < imgs.length; i++) {
|
|
var img = imgs[i],
|
|
imgW = img.width,
|
|
imgH = img.height;
|
|
|
|
var y = margin;
|
|
while (y <= imgH - margin - frameHeight) {
|
|
var x = margin;
|
|
while (x <= imgW - margin - frameWidth) {
|
|
if (frameCount >= maxFrames) {
|
|
break imgLoop;
|
|
}
|
|
frameCount++;
|
|
this._frames.push({
|
|
image: img,
|
|
rect: new createjs.Rectangle(x, y, frameWidth, frameHeight),
|
|
regX: this._regX,
|
|
regY: this._regY
|
|
});
|
|
x += frameWidth + spacing;
|
|
}
|
|
y += frameHeight + spacing;
|
|
}
|
|
}
|
|
this._numFrames = frameCount;
|
|
};
|
|
|
|
createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Graphics.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a
|
|
* specified context. Note that you can use Graphics without any dependency on the EaselJS framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}}
|
|
* directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the
|
|
* context of an EaselJS display list.
|
|
*
|
|
* There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or
|
|
* instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}.
|
|
* The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes.
|
|
*
|
|
* var g = new createjs.Graphics();
|
|
* g.setStrokeStyle(1);
|
|
* g.beginStroke("#000000");
|
|
* g.beginFill("red");
|
|
* g.drawCircle(0,0,30);
|
|
*
|
|
* All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example,
|
|
* the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill:
|
|
*
|
|
* myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50);
|
|
*
|
|
* Each graphics API call generates a command object (see below). The last command to be created can be accessed via
|
|
* {{#crossLink "Graphics/command:property"}}{{/crossLink}}:
|
|
*
|
|
* var fillCommand = myGraphics.beginFill("red").command;
|
|
* // ... later, update the fill style/color:
|
|
* fillCommand.style = "blue";
|
|
* // or change it to a bitmap fill:
|
|
* fillCommand.bitmap(myImage);
|
|
*
|
|
* For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you
|
|
* need to manage path creation manually, and ensure that fill/stroke is applied to a defined path:
|
|
*
|
|
* // start a new path. Graphics.beginCmd is a reusable BeginPath instance:
|
|
* myGraphics.append(createjs.Graphics.beginCmd);
|
|
* // we need to define the path before applying the fill:
|
|
* var circle = new createjs.Graphics.Circle(0,0,30);
|
|
* myGraphics.append(circle);
|
|
* // fill the path we just defined:
|
|
* var fill = new createjs.Graphics.Fill("red");
|
|
* myGraphics.append(fill);
|
|
*
|
|
* These approaches can be used together, for example to insert a custom command:
|
|
*
|
|
* myGraphics.beginFill("red");
|
|
* var customCommand = new CustomSpiralCommand(etc);
|
|
* myGraphics.append(customCommand);
|
|
* myGraphics.beginFill("blue");
|
|
* myGraphics.drawCircle(0, 0, 30);
|
|
*
|
|
* See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands.
|
|
*
|
|
* <h4>Tiny API</h4>
|
|
* The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the
|
|
* Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS
|
|
* to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected
|
|
* descriptions in the docs.
|
|
*
|
|
* <table>
|
|
* <tr><td><b>Tiny</b></td><td><b>Method</b></td><td><b>Tiny</b></td><td><b>Method</b></td></tr>
|
|
* <tr><td>mt</td><td>{{#crossLink "Graphics/moveTo"}}{{/crossLink}} </td>
|
|
* <td>lt</td> <td>{{#crossLink "Graphics/lineTo"}}{{/crossLink}}</td></tr>
|
|
* <tr><td>a/at</td><td>{{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} </td>
|
|
* <td>bt</td><td>{{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>qt</td><td>{{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo)</td>
|
|
* <td>r</td><td>{{#crossLink "Graphics/rect"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>cp</td><td>{{#crossLink "Graphics/closePath"}}{{/crossLink}} </td>
|
|
* <td>c</td><td>{{#crossLink "Graphics/clear"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>f</td><td>{{#crossLink "Graphics/beginFill"}}{{/crossLink}} </td>
|
|
* <td>lf</td><td>{{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>rf</td><td>{{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} </td>
|
|
* <td>bf</td><td>{{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>ef</td><td>{{#crossLink "Graphics/endFill"}}{{/crossLink}} </td>
|
|
* <td>ss / sd</td><td>{{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} / {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>s</td><td>{{#crossLink "Graphics/beginStroke"}}{{/crossLink}} </td>
|
|
* <td>ls</td><td>{{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>rs</td><td>{{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} </td>
|
|
* <td>bs</td><td>{{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>es</td><td>{{#crossLink "Graphics/endStroke"}}{{/crossLink}} </td>
|
|
* <td>dr</td><td>{{#crossLink "Graphics/drawRect"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>rr</td><td>{{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} </td>
|
|
* <td>rc</td><td>{{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>dc</td><td>{{#crossLink "Graphics/drawCircle"}}{{/crossLink}} </td>
|
|
* <td>de</td><td>{{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>dp</td><td>{{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} </td>
|
|
* <td>p</td><td>{{#crossLink "Graphics/decodePath"}}{{/crossLink}} </td></tr>
|
|
* </table>
|
|
*
|
|
* Here is the above example, using the tiny API instead.
|
|
*
|
|
* myGraphics.s("red").f("blue").r(20, 20, 100, 50);
|
|
*
|
|
* @class Graphics
|
|
* @constructor
|
|
**/
|
|
|
|
function Graphics() {
|
|
|
|
// public properties
|
|
/**
|
|
* Holds a reference to the last command that was created or appended. For example, you could retain a reference
|
|
* to a Fill command in order to dynamically update the color later by using:
|
|
*
|
|
* var myFill = myGraphics.beginFill("red").command;
|
|
* // update color later:
|
|
* myFill.style = "yellow";
|
|
*
|
|
* @property command
|
|
* @type Object
|
|
**/
|
|
this.command = null;
|
|
|
|
// private properties
|
|
/**
|
|
* @property _stroke
|
|
* @protected
|
|
* @type {Stroke}
|
|
**/
|
|
this._stroke = null;
|
|
|
|
/**
|
|
* @property _strokeStyle
|
|
* @protected
|
|
* @type {StrokeStyle}
|
|
**/
|
|
this._strokeStyle = null;
|
|
|
|
/**
|
|
* @property _oldStrokeStyle
|
|
* @protected
|
|
* @type {StrokeStyle}
|
|
**/
|
|
this._oldStrokeStyle = null;
|
|
|
|
/**
|
|
* @property _strokeDash
|
|
* @protected
|
|
* @type {StrokeDash}
|
|
**/
|
|
this._strokeDash = null;
|
|
|
|
/**
|
|
* @property _oldStrokeDash
|
|
* @protected
|
|
* @type {StrokeDash}
|
|
**/
|
|
this._oldStrokeDash = null;
|
|
|
|
/**
|
|
* @property _strokeIgnoreScale
|
|
* @protected
|
|
* @type Boolean
|
|
**/
|
|
this._strokeIgnoreScale = false;
|
|
|
|
/**
|
|
* @property _fill
|
|
* @protected
|
|
* @type {Fill}
|
|
**/
|
|
this._fill = null;
|
|
|
|
/**
|
|
* @property _instructions
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._instructions = [];
|
|
|
|
/**
|
|
* Indicates the last instruction index that was committed.
|
|
* @property _commitIndex
|
|
* @protected
|
|
* @type {Number}
|
|
**/
|
|
this._commitIndex = 0;
|
|
|
|
/**
|
|
* Uncommitted instructions.
|
|
* @property _activeInstructions
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._activeInstructions = [];
|
|
|
|
/**
|
|
* This indicates that there have been changes to the activeInstruction list since the last updateInstructions call.
|
|
* @property _dirty
|
|
* @protected
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
this._dirty = false;
|
|
|
|
/**
|
|
* Index to draw from if a store operation has happened.
|
|
* @property _storeIndex
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._storeIndex = 0;
|
|
|
|
// setup:
|
|
this.clear();
|
|
}
|
|
var p = Graphics.prototype;
|
|
var G = Graphics; // shortcut
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// static public methods:
|
|
/**
|
|
* Returns a CSS compatible color string based on the specified RGB numeric color values in the format
|
|
* "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example,
|
|
*
|
|
* createjs.Graphics.getRGB(50, 100, 150, 0.5);
|
|
* // Returns "rgba(50,100,150,0.5)"
|
|
*
|
|
* It also supports passing a single hex color value as the first param, and an optional alpha value as the second
|
|
* param. For example,
|
|
*
|
|
* createjs.Graphics.getRGB(0xFF00FF, 0.2);
|
|
* // Returns "rgba(255,0,255,0.2)"
|
|
*
|
|
* @method getRGB
|
|
* @static
|
|
* @param {Number} r The red component for the color, between 0 and 0xFF (255).
|
|
* @param {Number} g The green component for the color, between 0 and 0xFF (255).
|
|
* @param {Number} b The blue component for the color, between 0 and 0xFF (255).
|
|
* @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
|
|
* @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format
|
|
* "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)".
|
|
**/
|
|
Graphics.getRGB = function (r, g, b, alpha) {
|
|
if (r != null && b == null) {
|
|
alpha = g;
|
|
b = r & 0xFF;
|
|
g = r >> 8 & 0xFF;
|
|
r = r >> 16 & 0xFF;
|
|
}
|
|
if (alpha == null) {
|
|
return "rgb(" + r + "," + g + "," + b + ")";
|
|
} else {
|
|
return "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)",
|
|
* or if alpha is null then in the format "hsl(360,100,100)".
|
|
*
|
|
* createjs.Graphics.getHSL(150, 100, 70);
|
|
* // Returns "hsl(150,100,70)"
|
|
*
|
|
* @method getHSL
|
|
* @static
|
|
* @param {Number} hue The hue component for the color, between 0 and 360.
|
|
* @param {Number} saturation The saturation component for the color, between 0 and 100.
|
|
* @param {Number} lightness The lightness component for the color, between 0 and 100.
|
|
* @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
|
|
* @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format
|
|
* "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)".
|
|
**/
|
|
Graphics.getHSL = function (hue, saturation, lightness, alpha) {
|
|
if (alpha == null) {
|
|
return "hsl(" + hue % 360 + "," + saturation + "%," + lightness + "%)";
|
|
} else {
|
|
return "hsla(" + hue % 360 + "," + saturation + "%," + lightness + "%," + alpha + ")";
|
|
}
|
|
};
|
|
|
|
// static properties:
|
|
/**
|
|
* A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid
|
|
* unnecessary instantiation.
|
|
* @property beginCmd
|
|
* @type {Graphics.BeginPath}
|
|
* @static
|
|
**/
|
|
// defined at the bottom of this file.
|
|
|
|
/**
|
|
* Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}.
|
|
* @property BASE_64
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @type {Object}
|
|
**/
|
|
Graphics.BASE_64 = { "A": 0, "B": 1, "C": 2, "D": 3, "E": 4, "F": 5, "G": 6, "H": 7, "I": 8, "J": 9, "K": 10, "L": 11, "M": 12, "N": 13, "O": 14, "P": 15, "Q": 16, "R": 17, "S": 18, "T": 19, "U": 20, "V": 21, "W": 22, "X": 23, "Y": 24, "Z": 25, "a": 26, "b": 27, "c": 28, "d": 29, "e": 30, "f": 31, "g": 32, "h": 33, "i": 34, "j": 35, "k": 36, "l": 37, "m": 38, "n": 39, "o": 40, "p": 41, "q": 42, "r": 43, "s": 44, "t": 45, "u": 46, "v": 47, "w": 48, "x": 49, "y": 50, "z": 51, "0": 52, "1": 53, "2": 54, "3": 55, "4": 56, "5": 57, "6": 58, "7": 59, "8": 60, "9": 61, "+": 62, "/": 63 };
|
|
|
|
/**
|
|
* Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
|
|
* corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
|
|
* "butt", 1 to "round", and 2 to "square".
|
|
* For example, to set the line caps to "square":
|
|
*
|
|
* myGraphics.ss(16, 2);
|
|
*
|
|
* @property STROKE_CAPS_MAP
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @type {Array}
|
|
**/
|
|
Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"];
|
|
|
|
/**
|
|
* Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
|
|
* corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
|
|
* "miter", 1 to "round", and 2 to "bevel".
|
|
* For example, to set the line joints to "bevel":
|
|
*
|
|
* myGraphics.ss(16, 0, 2);
|
|
*
|
|
* @property STROKE_JOINTS_MAP
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @type {Array}
|
|
**/
|
|
Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"];
|
|
|
|
/**
|
|
* @property _ctx
|
|
* @static
|
|
* @protected
|
|
* @type {CanvasRenderingContext2D}
|
|
**/
|
|
var canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
if (canvas.getContext) {
|
|
Graphics._ctx = canvas.getContext("2d");
|
|
canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "Graphics/instructions:property"}}{{/crossLink}} property instead.
|
|
* @method getInstructions
|
|
* @return {Array}
|
|
* @deprecated
|
|
**/
|
|
p.getInstructions = function () {
|
|
this._updateInstructions();
|
|
return this._instructions;
|
|
};
|
|
|
|
/**
|
|
* Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect)
|
|
* Modifying the returned array directly is not recommended, and is likely to result in unexpected behaviour.
|
|
*
|
|
* This property is mainly intended for introspection of the instructions (ex. for graphics export).
|
|
* @property instructions
|
|
* @type {Array}
|
|
* @readonly
|
|
**/
|
|
try {
|
|
Object.defineProperties(p, {
|
|
instructions: { get: p.getInstructions }
|
|
});
|
|
} catch (e) {}
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true if this Graphics instance has no drawing commands.
|
|
* @method isEmpty
|
|
* @return {Boolean} Returns true if this Graphics instance has no drawing commands.
|
|
**/
|
|
p.isEmpty = function () {
|
|
return !(this._instructions.length || this._activeInstructions.length);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data.
|
|
**/
|
|
p.draw = function (ctx, data) {
|
|
this._updateInstructions();
|
|
var instr = this._instructions;
|
|
for (var i = this._storeIndex, l = instr.length; i < l; i++) {
|
|
instr[i].exec(ctx, data);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Draws only the path described for this Graphics instance, skipping any non-path instructions, including fill and
|
|
* stroke descriptions. Used for <code>DisplayObject.mask</code> to draw the clipping path, for example.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method drawAsPath
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
**/
|
|
p.drawAsPath = function (ctx) {
|
|
this._updateInstructions();
|
|
var instr,
|
|
instrs = this._instructions;
|
|
for (var i = this._storeIndex, l = instrs.length; i < l; i++) {
|
|
// the first command is always a beginPath command.
|
|
if ((instr = instrs[i]).path !== false) {
|
|
instr.exec(ctx);
|
|
}
|
|
}
|
|
};
|
|
|
|
// public methods that map directly to context 2D calls:
|
|
/**
|
|
* Moves the drawing point to the specified position. A tiny API method "mt" also exists.
|
|
* @method moveTo
|
|
* @param {Number} x The x coordinate the drawing point should move to.
|
|
* @param {Number} y The y coordinate the drawing point should move to.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
|
|
* @chainable
|
|
**/
|
|
p.moveTo = function (x, y) {
|
|
return this.append(new G.MoveTo(x, y), true);
|
|
};
|
|
|
|
/**
|
|
* Draws a line from the current drawing point to the specified position, which become the new current drawing
|
|
* point. Note that you *must* call {{#crossLink "Graphics/moveTo"}}{{/crossLink}} before the first `lineTo()`.
|
|
* A tiny API method "lt" also exists.
|
|
*
|
|
* For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#complex-shapes-(paths)">
|
|
* whatwg spec</a>.
|
|
* @method lineTo
|
|
* @param {Number} x The x coordinate the drawing point should draw to.
|
|
* @param {Number} y The y coordinate the drawing point should draw to.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.lineTo = function (x, y) {
|
|
return this.append(new G.LineTo(x, y));
|
|
};
|
|
|
|
/**
|
|
* Draws an arc with the specified control points and radius. For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arcto">
|
|
* whatwg spec</a>. A tiny API method "at" also exists.
|
|
* @method arcTo
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} x2
|
|
* @param {Number} y2
|
|
* @param {Number} radius
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.arcTo = function (x1, y1, x2, y2, radius) {
|
|
return this.append(new G.ArcTo(x1, y1, x2, y2, radius));
|
|
};
|
|
|
|
/**
|
|
* Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For
|
|
* example, to draw a full circle with a radius of 20 centered at (100, 100):
|
|
*
|
|
* arc(100, 100, 20, 0, Math.PI*2);
|
|
*
|
|
* For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc">whatwg spec</a>.
|
|
* A tiny API method "a" also exists.
|
|
* @method arc
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} startAngle Measured in radians.
|
|
* @param {Number} endAngle Measured in radians.
|
|
* @param {Boolean} anticlockwise
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.arc = function (x, y, radius, startAngle, endAngle, anticlockwise) {
|
|
return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise));
|
|
};
|
|
|
|
/**
|
|
* Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed
|
|
* information, read the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-quadraticcurveto">
|
|
* whatwg spec</a>. A tiny API method "qt" also exists.
|
|
* @method quadraticCurveTo
|
|
* @param {Number} cpx
|
|
* @param {Number} cpy
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.quadraticCurveTo = function (cpx, cpy, x, y) {
|
|
return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y));
|
|
};
|
|
|
|
/**
|
|
* Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x,
|
|
* cp2y). For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-beziercurveto">
|
|
* whatwg spec</a>. A tiny API method "bt" also exists.
|
|
* @method bezierCurveTo
|
|
* @param {Number} cp1x
|
|
* @param {Number} cp1y
|
|
* @param {Number} cp2x
|
|
* @param {Number} cp2y
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y));
|
|
};
|
|
|
|
/**
|
|
* Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke.
|
|
* For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-rect">
|
|
* whatwg spec</a>. A tiny API method "r" also exists.
|
|
* @method rect
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w Width of the rectangle
|
|
* @param {Number} h Height of the rectangle
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.rect = function (x, y, w, h) {
|
|
return this.append(new G.Rect(x, y, w, h));
|
|
};
|
|
|
|
/**
|
|
* Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified
|
|
* since the fill or stroke was last set. A tiny API method "cp" also exists.
|
|
* @method closePath
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.closePath = function () {
|
|
return this._activeInstructions.length ? this.append(new G.ClosePath()) : this;
|
|
};
|
|
|
|
// public methods that roughly map to Flash graphics APIs:
|
|
/**
|
|
* Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need
|
|
* to be redefined to draw shapes following a clear call. A tiny API method "c" also exists.
|
|
* @method clear
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.clear = function () {
|
|
this._instructions.length = this._activeInstructions.length = this._commitIndex = 0;
|
|
this._strokeStyle = this._oldStrokeStyle = this._stroke = this._fill = this._strokeDash = this._oldStrokeDash = null;
|
|
this._dirty = this._strokeIgnoreScale = false;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists.
|
|
* @method beginFill
|
|
* @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
|
|
* null will result in no fill.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginFill = function (color) {
|
|
return this._setFill(color ? new G.Fill(color) : null);
|
|
};
|
|
|
|
/**
|
|
* Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
|
|
* example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
|
|
* square to display it:
|
|
*
|
|
* myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
|
|
*
|
|
* A tiny API method "lf" also exists.
|
|
* @method beginLinearGradientFill
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
|
|
* drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
|
|
* the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginLinearGradientFill = function (colors, ratios, x0, y0, x1, y1) {
|
|
return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1));
|
|
};
|
|
|
|
/**
|
|
* Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to
|
|
* blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it:
|
|
*
|
|
* myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50);
|
|
*
|
|
* A tiny API method "rf" also exists.
|
|
* @method beginRadialGradientFill
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} y0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} r0 Radius of the inner circle that defines the gradient.
|
|
* @param {Number} x1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} y1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} r1 Radius of the outer circle that defines the gradient.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginRadialGradientFill = function (colors, ratios, x0, y0, r0, x1, y1, r1) {
|
|
return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
|
|
};
|
|
|
|
/**
|
|
* Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also
|
|
* exists.
|
|
* @method beginBitmapFill
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
|
|
* as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
|
|
* @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
|
|
* "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
|
|
* "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
|
|
* @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
|
|
* will be applied relative to the parent transform.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginBitmapFill = function (image, repetition, matrix) {
|
|
return this._setFill(new G.Fill(null, matrix).bitmap(image, repetition));
|
|
};
|
|
|
|
/**
|
|
* Ends the current sub-path, and begins a new one with no fill. Functionally identical to <code>beginFill(null)</code>.
|
|
* A tiny API method "ef" also exists.
|
|
* @method endFill
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.endFill = function () {
|
|
return this.beginFill();
|
|
};
|
|
|
|
/**
|
|
* Sets the stroke style. Like all drawing methods, this can be chained, so you can define
|
|
* the stroke style and color in a single line of code like so:
|
|
*
|
|
* myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");
|
|
*
|
|
* A tiny API method "ss" also exists.
|
|
* @method setStrokeStyle
|
|
* @param {Number} thickness The width of the stroke.
|
|
* @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
|
|
* round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
|
|
* the tiny API.
|
|
* @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
|
|
* One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
|
|
* for use with the tiny API.
|
|
* @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
|
|
* controls at what point a mitered joint will be clipped.
|
|
* @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
|
|
* of active transformations.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.setStrokeStyle = function (thickness, caps, joints, miterLimit, ignoreScale) {
|
|
this._updateInstructions(true);
|
|
this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale);
|
|
|
|
// ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery:
|
|
if (this._stroke) {
|
|
this._stroke.ignoreScale = ignoreScale;
|
|
}
|
|
this._strokeIgnoreScale = ignoreScale;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Sets or clears the stroke dash pattern.
|
|
*
|
|
* myGraphics.setStrokeDash([20, 10], 0);
|
|
*
|
|
* A tiny API method `sd` also exists.
|
|
* @method setStrokeDash
|
|
* @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
|
|
* For example, `[20,10]` would create a pattern of 20 pixel lines with 10 pixel gaps between them.
|
|
* Passing null or an empty array will clear the existing stroke dash.
|
|
* @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.setStrokeDash = function (segments, offset) {
|
|
this._updateInstructions(true);
|
|
this._strokeDash = this.command = new G.StrokeDash(segments, offset);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists.
|
|
* @method beginStroke
|
|
* @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
|
|
* null will result in no stroke.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginStroke = function (color) {
|
|
return this._setStroke(color ? new G.Stroke(color) : null);
|
|
};
|
|
|
|
/**
|
|
* Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
|
|
* example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
|
|
* square to display it:
|
|
*
|
|
* myGraphics.setStrokeStyle(10).
|
|
* beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
|
|
*
|
|
* A tiny API method "ls" also exists.
|
|
* @method beginLinearGradientStroke
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginLinearGradientStroke = function (colors, ratios, x0, y0, x1, y1) {
|
|
return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1));
|
|
};
|
|
|
|
/**
|
|
* Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to
|
|
* blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it:
|
|
*
|
|
* myGraphics.setStrokeStyle(10)
|
|
* .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50)
|
|
* .drawRect(50, 90, 150, 110);
|
|
*
|
|
* A tiny API method "rs" also exists.
|
|
* @method beginRadialGradientStroke
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
|
|
* to 100%.
|
|
* @param {Number} x0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} y0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} r0 Radius of the inner circle that defines the gradient.
|
|
* @param {Number} x1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} y1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} r1 Radius of the outer circle that defines the gradient.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginRadialGradientStroke = function (colors, ratios, x0, y0, r0, x1, y1, r1) {
|
|
return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
|
|
};
|
|
|
|
/**
|
|
* Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills,
|
|
* strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs"
|
|
* also exists.
|
|
* @method beginBitmapStroke
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
|
|
* as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
|
|
* @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
|
|
* "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.beginBitmapStroke = function (image, repetition) {
|
|
// NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width.
|
|
return this._setStroke(new G.Stroke().bitmap(image, repetition));
|
|
};
|
|
|
|
/**
|
|
* Ends the current sub-path, and begins a new one with no stroke. Functionally identical to <code>beginStroke(null)</code>.
|
|
* A tiny API method "es" also exists.
|
|
* @method endStroke
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.endStroke = function () {
|
|
return this.beginStroke();
|
|
};
|
|
|
|
/**
|
|
* Maps the familiar ActionScript <code>curveTo()</code> method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}}
|
|
* method.
|
|
* @method quadraticCurveTo
|
|
* @param {Number} cpx
|
|
* @param {Number} cpy
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.curveTo = p.quadraticCurveTo;
|
|
|
|
/**
|
|
*
|
|
* Maps the familiar ActionScript <code>drawRect()</code> method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}}
|
|
* method.
|
|
* @method drawRect
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w Width of the rectangle
|
|
* @param {Number} h Height of the rectangle
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.drawRect = p.rect;
|
|
|
|
/**
|
|
* Draws a rounded rectangle with all corners with the specified radius.
|
|
* @method drawRoundRect
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
* @param {Number} radius Corner radius.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.drawRoundRect = function (x, y, w, h, radius) {
|
|
return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius);
|
|
};
|
|
|
|
/**
|
|
* Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API
|
|
* method "rc" also exists.
|
|
* @method drawRoundRectComplex
|
|
* @param {Number} x The horizontal coordinate to draw the round rect.
|
|
* @param {Number} y The vertical coordinate to draw the round rect.
|
|
* @param {Number} w The width of the round rect.
|
|
* @param {Number} h The height of the round rect.
|
|
* @param {Number} radiusTL Top left corner radius.
|
|
* @param {Number} radiusTR Top right corner radius.
|
|
* @param {Number} radiusBR Bottom right corner radius.
|
|
* @param {Number} radiusBL Bottom left corner radius.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.drawRoundRectComplex = function (x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
|
|
return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL));
|
|
};
|
|
|
|
/**
|
|
* Draws a circle with the specified radius at (x, y).
|
|
*
|
|
* var g = new createjs.Graphics();
|
|
* g.setStrokeStyle(1);
|
|
* g.beginStroke(createjs.Graphics.getRGB(0,0,0));
|
|
* g.beginFill(createjs.Graphics.getRGB(255,0,0));
|
|
* g.drawCircle(0,0,3);
|
|
*
|
|
* var s = new createjs.Shape(g);
|
|
* s.x = 100;
|
|
* s.y = 100;
|
|
*
|
|
* stage.addChild(s);
|
|
* stage.update();
|
|
*
|
|
* A tiny API method "dc" also exists.
|
|
* @method drawCircle
|
|
* @param {Number} x x coordinate center point of circle.
|
|
* @param {Number} y y coordinate center point of circle.
|
|
* @param {Number} radius Radius of circle.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.drawCircle = function (x, y, radius) {
|
|
return this.append(new G.Circle(x, y, radius));
|
|
};
|
|
|
|
/**
|
|
* Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}},
|
|
* except the width and height can be different. A tiny API method "de" also exists.
|
|
* @method drawEllipse
|
|
* @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
|
|
* which draws from center.
|
|
* @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
|
|
* which draws from the center.
|
|
* @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
|
|
* number.
|
|
* @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.drawEllipse = function (x, y, w, h) {
|
|
return this.append(new G.Ellipse(x, y, w, h));
|
|
};
|
|
|
|
/**
|
|
* Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of
|
|
* points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a
|
|
* radius of 50:
|
|
*
|
|
* myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90);
|
|
* // Note: -90 makes the first point vertical
|
|
*
|
|
* A tiny API method "dp" also exists.
|
|
*
|
|
* @method drawPolyStar
|
|
* @param {Number} x Position of the center of the shape.
|
|
* @param {Number} y Position of the center of the shape.
|
|
* @param {Number} radius The outer radius of the shape.
|
|
* @param {Number} sides The number of points on the star or sides on the polygon.
|
|
* @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
|
|
* polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
|
|
* @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
|
|
* directly to the right of the center.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.drawPolyStar = function (x, y, radius, sides, pointSize, angle) {
|
|
return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle));
|
|
};
|
|
|
|
// TODO: deprecated.
|
|
/**
|
|
* Removed in favour of using custom command objects with {{#crossLink "Graphics/append"}}{{/crossLink}}.
|
|
* @method inject
|
|
* @deprecated
|
|
**/
|
|
|
|
/**
|
|
* Appends a graphics command object to the graphics queue. Command objects expose an "exec" method
|
|
* that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into
|
|
* {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw.
|
|
*
|
|
* This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert
|
|
* built-in or custom graphics commands. For example:
|
|
*
|
|
* // attach data to our shape, so we can access it during the draw:
|
|
* myShape.color = "red";
|
|
*
|
|
* // append a Circle command object:
|
|
* myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30));
|
|
*
|
|
* // append a custom command object with an exec method that sets the fill style
|
|
* // based on the shape's data, and then fills the circle.
|
|
* myShape.graphics.append({exec:function(ctx, shape) {
|
|
* ctx.fillStyle = shape.color;
|
|
* ctx.fill();
|
|
* }});
|
|
*
|
|
* @method append
|
|
* @param {Object} command A graphics command object exposing an "exec" method.
|
|
* @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.append = function (command, clean) {
|
|
this._activeInstructions.push(command);
|
|
this.command = command;
|
|
if (!clean) {
|
|
this._dirty = true;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Decodes a compact encoded path string into a series of draw instructions.
|
|
* This format is not intended to be human readable, and is meant for use by authoring tools.
|
|
* The format uses a base64 character set, with each character representing 6 bits, to define a series of draw
|
|
* commands.
|
|
*
|
|
* Each command is comprised of a single "header" character followed by a variable number of alternating x and y
|
|
* position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the
|
|
* type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4
|
|
* indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the
|
|
* latter. Bits 5 and 6 are currently unused.
|
|
*
|
|
* Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo)
|
|
* parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the
|
|
* 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed
|
|
* by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the
|
|
* case of move operations which are absolute, this value is a delta from the previous x or y position (as
|
|
* appropriate).
|
|
*
|
|
* For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0.
|
|
* <br />A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per
|
|
* parameter.
|
|
* <br />n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits
|
|
* indicate 1500 tenths of a pixel.
|
|
* <br />AA - 000000000000. Absolute y position of 0.
|
|
* <br />I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter.
|
|
* <br />Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to
|
|
* provide an absolute position of +150.0px.
|
|
* <br />AAA - 000000000000000000. A y delta value of 0.
|
|
*
|
|
* A tiny API method "p" also exists.
|
|
* @method decodePath
|
|
* @param {String} str The path string to decode.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.decodePath = function (str) {
|
|
var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath];
|
|
var paramCount = [2, 2, 4, 6, 0];
|
|
var i = 0,
|
|
l = str.length;
|
|
var params = [];
|
|
var x = 0,
|
|
y = 0;
|
|
var base64 = Graphics.BASE_64;
|
|
|
|
while (i < l) {
|
|
var c = str.charAt(i);
|
|
var n = base64[c];
|
|
var fi = n >> 3; // highest order bits 1-3 code for operation.
|
|
var f = instructions[fi];
|
|
// check that we have a valid instruction & that the unused bits are empty:
|
|
if (!f || n & 3) {
|
|
throw "bad path data (@" + i + "): " + c;
|
|
}
|
|
var pl = paramCount[fi];
|
|
if (!fi) {
|
|
x = y = 0;
|
|
} // move operations reset the position.
|
|
params.length = 0;
|
|
i++;
|
|
var charCount = (n >> 2 & 1) + 2; // 4th header bit indicates number size for this operation.
|
|
for (var p = 0; p < pl; p++) {
|
|
var num = base64[str.charAt(i)];
|
|
var sign = num >> 5 ? -1 : 1;
|
|
num = (num & 31) << 6 | base64[str.charAt(i + 1)];
|
|
if (charCount == 3) {
|
|
num = num << 6 | base64[str.charAt(i + 2)];
|
|
}
|
|
num = sign * num / 10;
|
|
if (p % 2) {
|
|
x = num += x;
|
|
} else {
|
|
y = num += y;
|
|
}
|
|
params[p] = num;
|
|
i += charCount;
|
|
}
|
|
f.apply(this, params);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to
|
|
* the existing store. This also affects `drawAsPath()`.
|
|
*
|
|
* This is useful in cases where you are creating vector graphics in an iterative manner (ex. generative art), so
|
|
* that only new graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all
|
|
* of the vector instructions for later use (ex. scaling, modifying, or exporting).
|
|
*
|
|
* Note that calling store() will force the active path (if any) to be ended in a manner similar to changing
|
|
* the fill or stroke.
|
|
*
|
|
* For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of
|
|
* segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}},
|
|
* and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG.
|
|
*
|
|
* // set up cache:
|
|
* myShape.cache(0,0,500,500,scale);
|
|
*
|
|
* // when the user drags, draw a new line:
|
|
* myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY);
|
|
* // then draw it into the existing cache:
|
|
* myShape.updateCache("source-over");
|
|
* // store the new line, so it isn't redrawn next time:
|
|
* myShape.store();
|
|
*
|
|
* // then, when the window resizes, we can re-render at a different scale:
|
|
* // first, unstore all our lines:
|
|
* myShape.unstore();
|
|
* // then cache using the new scale:
|
|
* myShape.cache(0,0,500,500,newScale);
|
|
* // finally, store the existing commands again:
|
|
* myShape.store();
|
|
*
|
|
* @method store
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.store = function () {
|
|
this._updateInstructions(true);
|
|
this._storeIndex = this._instructions.length;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}}
|
|
* so that they will be executed in subsequent draw calls.
|
|
*
|
|
* @method unstore
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.unstore = function () {
|
|
this._storeIndex = 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Graphics instance. Note that the individual command objects are not cloned.
|
|
* @method clone
|
|
* @return {Graphics} A clone of the current Graphics instance.
|
|
**/
|
|
p.clone = function () {
|
|
var o = new Graphics();
|
|
o.command = this.command;
|
|
o._stroke = this._stroke;
|
|
o._strokeStyle = this._strokeStyle;
|
|
o._strokeDash = this._strokeDash;
|
|
o._strokeIgnoreScale = this._strokeIgnoreScale;
|
|
o._fill = this._fill;
|
|
o._instructions = this._instructions.slice();
|
|
o._commitIndex = this._commitIndex;
|
|
o._activeInstructions = this._activeInstructions.slice();
|
|
o._dirty = this._dirty;
|
|
o._storeIndex = this._storeIndex;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Graphics]";
|
|
};
|
|
|
|
// tiny API:
|
|
/**
|
|
* Shortcut to moveTo.
|
|
* @method mt
|
|
* @param {Number} x The x coordinate the drawing point should move to.
|
|
* @param {Number} y The y coordinate the drawing point should move to.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.mt = p.moveTo;
|
|
|
|
/**
|
|
* Shortcut to lineTo.
|
|
* @method lt
|
|
* @param {Number} x The x coordinate the drawing point should draw to.
|
|
* @param {Number} y The y coordinate the drawing point should draw to.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.lt = p.lineTo;
|
|
|
|
/**
|
|
* Shortcut to arcTo.
|
|
* @method at
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} x2
|
|
* @param {Number} y2
|
|
* @param {Number} radius
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.at = p.arcTo;
|
|
|
|
/**
|
|
* Shortcut to bezierCurveTo.
|
|
* @method bt
|
|
* @param {Number} cp1x
|
|
* @param {Number} cp1y
|
|
* @param {Number} cp2x
|
|
* @param {Number} cp2y
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.bt = p.bezierCurveTo;
|
|
|
|
/**
|
|
* Shortcut to quadraticCurveTo / curveTo.
|
|
* @method qt
|
|
* @param {Number} cpx
|
|
* @param {Number} cpy
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @protected
|
|
* @chainable
|
|
**/
|
|
p.qt = p.quadraticCurveTo;
|
|
|
|
/**
|
|
* Shortcut to arc.
|
|
* @method a
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} startAngle Measured in radians.
|
|
* @param {Number} endAngle Measured in radians.
|
|
* @param {Boolean} anticlockwise
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @protected
|
|
* @chainable
|
|
**/
|
|
p.a = p.arc;
|
|
|
|
/**
|
|
* Shortcut to rect.
|
|
* @method r
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w Width of the rectangle
|
|
* @param {Number} h Height of the rectangle
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.r = p.rect;
|
|
|
|
/**
|
|
* Shortcut to closePath.
|
|
* @method cp
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.cp = p.closePath;
|
|
|
|
/**
|
|
* Shortcut to clear.
|
|
* @method c
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.c = p.clear;
|
|
|
|
/**
|
|
* Shortcut to beginFill.
|
|
* @method f
|
|
* @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
|
|
* null will result in no fill.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.f = p.beginFill;
|
|
|
|
/**
|
|
* Shortcut to beginLinearGradientFill.
|
|
* @method lf
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
|
|
* drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
|
|
* the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.lf = p.beginLinearGradientFill;
|
|
|
|
/**
|
|
* Shortcut to beginRadialGradientFill.
|
|
* @method rf
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} y0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} r0 Radius of the inner circle that defines the gradient.
|
|
* @param {Number} x1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} y1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} r1 Radius of the outer circle that defines the gradient.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.rf = p.beginRadialGradientFill;
|
|
|
|
/**
|
|
* Shortcut to beginBitmapFill.
|
|
* @method bf
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
|
|
* as the pattern.
|
|
* @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
|
|
* "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
|
|
* "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
|
|
* @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
|
|
* will be applied relative to the parent transform.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.bf = p.beginBitmapFill;
|
|
|
|
/**
|
|
* Shortcut to endFill.
|
|
* @method ef
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.ef = p.endFill;
|
|
|
|
/**
|
|
* Shortcut to setStrokeStyle.
|
|
* @method ss
|
|
* @param {Number} thickness The width of the stroke.
|
|
* @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
|
|
* round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
|
|
* the tiny API.
|
|
* @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
|
|
* One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
|
|
* for use with the tiny API.
|
|
* @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
|
|
* controls at what point a mitered joint will be clipped.
|
|
* @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
|
|
* of active transformations.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.ss = p.setStrokeStyle;
|
|
|
|
/**
|
|
* Shortcut to setStrokeDash.
|
|
* @method sd
|
|
* @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
|
|
* For example, [20,10] would create a pattern of 20 pixel lines with 10 pixel gaps between them.
|
|
* Passing null or an empty array will clear any existing dash.
|
|
* @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.sd = p.setStrokeDash;
|
|
|
|
/**
|
|
* Shortcut to beginStroke.
|
|
* @method s
|
|
* @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
|
|
* null will result in no stroke.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.s = p.beginStroke;
|
|
|
|
/**
|
|
* Shortcut to beginLinearGradientStroke.
|
|
* @method ls
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.ls = p.beginLinearGradientStroke;
|
|
|
|
/**
|
|
* Shortcut to beginRadialGradientStroke.
|
|
* @method rs
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
|
|
* to 100%.
|
|
* @param {Number} x0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} y0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} r0 Radius of the inner circle that defines the gradient.
|
|
* @param {Number} x1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} y1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} r1 Radius of the outer circle that defines the gradient.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.rs = p.beginRadialGradientStroke;
|
|
|
|
/**
|
|
* Shortcut to beginBitmapStroke.
|
|
* @method bs
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
|
|
* as the pattern.
|
|
* @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
|
|
* "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.bs = p.beginBitmapStroke;
|
|
|
|
/**
|
|
* Shortcut to endStroke.
|
|
* @method es
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.es = p.endStroke;
|
|
|
|
/**
|
|
* Shortcut to drawRect.
|
|
* @method dr
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w Width of the rectangle
|
|
* @param {Number} h Height of the rectangle
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.dr = p.drawRect;
|
|
|
|
/**
|
|
* Shortcut to drawRoundRect.
|
|
* @method rr
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
* @param {Number} radius Corner radius.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.rr = p.drawRoundRect;
|
|
|
|
/**
|
|
* Shortcut to drawRoundRectComplex.
|
|
* @method rc
|
|
* @param {Number} x The horizontal coordinate to draw the round rect.
|
|
* @param {Number} y The vertical coordinate to draw the round rect.
|
|
* @param {Number} w The width of the round rect.
|
|
* @param {Number} h The height of the round rect.
|
|
* @param {Number} radiusTL Top left corner radius.
|
|
* @param {Number} radiusTR Top right corner radius.
|
|
* @param {Number} radiusBR Bottom right corner radius.
|
|
* @param {Number} radiusBL Bottom left corner radius.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.rc = p.drawRoundRectComplex;
|
|
|
|
/**
|
|
* Shortcut to drawCircle.
|
|
* @method dc
|
|
* @param {Number} x x coordinate center point of circle.
|
|
* @param {Number} y y coordinate center point of circle.
|
|
* @param {Number} radius Radius of circle.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.dc = p.drawCircle;
|
|
|
|
/**
|
|
* Shortcut to drawEllipse.
|
|
* @method de
|
|
* @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
|
|
* which draws from center.
|
|
* @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
|
|
* which draws from the center.
|
|
* @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
|
|
* number.
|
|
* @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.de = p.drawEllipse;
|
|
|
|
/**
|
|
* Shortcut to drawPolyStar.
|
|
* @method dp
|
|
* @param {Number} x Position of the center of the shape.
|
|
* @param {Number} y Position of the center of the shape.
|
|
* @param {Number} radius The outer radius of the shape.
|
|
* @param {Number} sides The number of points on the star or sides on the polygon.
|
|
* @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
|
|
* polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
|
|
* @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
|
|
* directly to the right of the center.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.dp = p.drawPolyStar;
|
|
|
|
/**
|
|
* Shortcut to decodePath.
|
|
* @method p
|
|
* @param {String} str The path string to decode.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
* @protected
|
|
**/
|
|
p.p = p.decodePath;
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _updateInstructions
|
|
* @param commit
|
|
* @protected
|
|
**/
|
|
p._updateInstructions = function (commit) {
|
|
var instr = this._instructions,
|
|
active = this._activeInstructions,
|
|
commitIndex = this._commitIndex;
|
|
|
|
if (this._dirty && active.length) {
|
|
instr.length = commitIndex; // remove old, uncommitted commands
|
|
instr.push(Graphics.beginCmd);
|
|
|
|
var l = active.length,
|
|
ll = instr.length;
|
|
instr.length = ll + l;
|
|
for (var i = 0; i < l; i++) {
|
|
instr[i + ll] = active[i];
|
|
}
|
|
|
|
if (this._fill) {
|
|
instr.push(this._fill);
|
|
}
|
|
if (this._stroke) {
|
|
// doesn't need to be re-applied if it hasn't changed.
|
|
if (this._strokeDash !== this._oldStrokeDash) {
|
|
this._oldStrokeDash = this._strokeDash;
|
|
instr.push(this._strokeDash);
|
|
}
|
|
if (this._strokeStyle !== this._oldStrokeStyle) {
|
|
this._oldStrokeStyle = this._strokeStyle;
|
|
instr.push(this._strokeStyle);
|
|
}
|
|
instr.push(this._stroke);
|
|
}
|
|
|
|
this._dirty = false;
|
|
}
|
|
|
|
if (commit) {
|
|
active.length = 0;
|
|
this._commitIndex = instr.length;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _setFill
|
|
* @param fill
|
|
* @protected
|
|
**/
|
|
p._setFill = function (fill) {
|
|
this._updateInstructions(true);
|
|
this.command = this._fill = fill;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* @method _setStroke
|
|
* @param stroke
|
|
* @protected
|
|
**/
|
|
p._setStroke = function (stroke) {
|
|
this._updateInstructions(true);
|
|
if (this.command = this._stroke = stroke) {
|
|
stroke.ignoreScale = this._strokeIgnoreScale;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
// Command Objects:
|
|
/**
|
|
* @namespace Graphics
|
|
*/
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/lineTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class LineTo
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.LineTo = function (x, y) {
|
|
this.x = x;this.y = y;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.lineTo(this.x, this.y);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/moveTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class MoveTo
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
*/
|
|
(G.MoveTo = function (x, y) {
|
|
this.x = x;this.y = y;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.moveTo(this.x, this.y);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/arcTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class ArcTo
|
|
* @constructor
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} x2
|
|
* @param {Number} y2
|
|
* @param {Number} radius
|
|
**/
|
|
/**
|
|
* @property x1
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y1
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property x2
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y2
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.ArcTo = function (x1, y1, x2, y2, radius) {
|
|
this.x1 = x1;this.y1 = y1;
|
|
this.x2 = x2;this.y2 = y2;
|
|
this.radius = radius;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.arcTo(this.x1, this.y1, this.x2, this.y2, this.radius);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/arc"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Arc
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} startAngle
|
|
* @param {Number} endAngle
|
|
* @param {Number} anticlockwise
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property startAngle
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property endAngle
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property anticlockwise
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.Arc = function (x, y, radius, startAngle, endAngle, anticlockwise) {
|
|
this.x = x;this.y = y;
|
|
this.radius = radius;
|
|
this.startAngle = startAngle;this.endAngle = endAngle;
|
|
this.anticlockwise = !!anticlockwise;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.arc(this.x, this.y, this.radius, this.startAngle, this.endAngle, this.anticlockwise);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class QuadraticCurveTo
|
|
* @constructor
|
|
* @param {Number} cpx
|
|
* @param {Number} cpy
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property cpx
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cpy
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.QuadraticCurveTo = function (cpx, cpy, x, y) {
|
|
this.cpx = cpx;this.cpy = cpy;
|
|
this.x = x;this.y = y;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.quadraticCurveTo(this.cpx, this.cpy, this.x, this.y);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class BezierCurveTo
|
|
* @constructor
|
|
* @param {Number} cp1x
|
|
* @param {Number} cp1y
|
|
* @param {Number} cp2x
|
|
* @param {Number} cp2y
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property cp1x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cp1y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cp2x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cp2y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.BezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
this.cp1x = cp1x;this.cp1y = cp1y;
|
|
this.cp2x = cp2x;this.cp2y = cp2y;
|
|
this.x = x;this.y = y;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.bezierCurveTo(this.cp1x, this.cp1y, this.cp2x, this.cp2y, this.x, this.y);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/rect"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Rect
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property w
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property h
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.Rect = function (x, y, w, h) {
|
|
this.x = x;this.y = y;
|
|
this.w = w;this.h = h;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.rect(this.x, this.y, this.w, this.h);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/closePath"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class ClosePath
|
|
* @constructor
|
|
**/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.ClosePath = function () {}).prototype.exec = function (ctx) {
|
|
ctx.closePath();
|
|
};
|
|
|
|
/**
|
|
* Graphics command object to begin a new path. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class BeginPath
|
|
* @constructor
|
|
**/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.BeginPath = function () {}).prototype.exec = function (ctx) {
|
|
ctx.beginPath();
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/beginFill"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Fill
|
|
* @constructor
|
|
* @param {Object} style A valid Context2D fillStyle.
|
|
* @param {Matrix2D} matrix
|
|
**/
|
|
/**
|
|
* A valid Context2D fillStyle.
|
|
* @property style
|
|
* @type Object
|
|
*/
|
|
/**
|
|
* @property matrix
|
|
* @type Matrix2D
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
p = (G.Fill = function (style, matrix) {
|
|
this.style = style;
|
|
this.matrix = matrix;
|
|
}).prototype;
|
|
p.exec = function (ctx) {
|
|
if (!this.style) {
|
|
return;
|
|
}
|
|
ctx.fillStyle = this.style;
|
|
var mtx = this.matrix;
|
|
if (mtx) {
|
|
ctx.save();ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
|
|
}
|
|
ctx.fill();
|
|
if (mtx) {
|
|
ctx.restore();
|
|
}
|
|
};
|
|
/**
|
|
* Creates a linear gradient style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
|
|
* See {{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} for more information.
|
|
* @method linearGradient
|
|
* @param {Array} colors
|
|
*
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @return {Fill} Returns this Fill object for chaining or assignment.
|
|
*/
|
|
p.linearGradient = function (colors, ratios, x0, y0, x1, y1) {
|
|
var o = this.style = Graphics._ctx.createLinearGradient(x0, y0, x1, y1);
|
|
for (var i = 0, l = colors.length; i < l; i++) {
|
|
o.addColorStop(ratios[i], colors[i]);
|
|
}
|
|
o.props = { colors: colors, ratios: ratios, x0: x0, y0: y0, x1: x1, y1: y1, type: "linear" };
|
|
return this;
|
|
};
|
|
/**
|
|
* Creates a radial gradient style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
|
|
* See {{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} for more information.
|
|
* @method radialGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} r0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} r1
|
|
* @return {Fill} Returns this Fill object for chaining or assignment.
|
|
*/
|
|
p.radialGradient = function (colors, ratios, x0, y0, r0, x1, y1, r1) {
|
|
var o = this.style = Graphics._ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
|
|
for (var i = 0, l = colors.length; i < l; i++) {
|
|
o.addColorStop(ratios[i], colors[i]);
|
|
}
|
|
o.props = { colors: colors, ratios: ratios, x0: x0, y0: y0, r0: r0, x1: x1, y1: y1, r1: r1, type: "radial" };
|
|
return this;
|
|
};
|
|
/**
|
|
* Creates a bitmap fill style and assigns it to the {{#crossLink "Fill/style:property"}}{{/crossLink}}.
|
|
* See {{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} for more information.
|
|
* @method bitmap
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image Must be loaded prior to creating a bitmap fill, or the fill will be empty.
|
|
* @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
|
|
* @return {Fill} Returns this Fill object for chaining or assignment.
|
|
*/
|
|
p.bitmap = function (image, repetition) {
|
|
if (image.naturalWidth || image.getContext || image.readyState >= 2) {
|
|
var o = this.style = Graphics._ctx.createPattern(image, repetition || "");
|
|
o.props = { image: image, repetition: repetition, type: "bitmap" };
|
|
}
|
|
return this;
|
|
};
|
|
p.path = false;
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Stroke
|
|
* @constructor
|
|
* @param {Object} style A valid Context2D fillStyle.
|
|
* @param {Boolean} ignoreScale
|
|
**/
|
|
/**
|
|
* A valid Context2D strokeStyle.
|
|
* @property style
|
|
* @type Object
|
|
*/
|
|
/**
|
|
* @property ignoreScale
|
|
* @type Boolean
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
p = (G.Stroke = function (style, ignoreScale) {
|
|
this.style = style;
|
|
this.ignoreScale = ignoreScale;
|
|
}).prototype;
|
|
p.exec = function (ctx) {
|
|
if (!this.style) {
|
|
return;
|
|
}
|
|
ctx.strokeStyle = this.style;
|
|
if (this.ignoreScale) {
|
|
ctx.save();ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
}
|
|
ctx.stroke();
|
|
if (this.ignoreScale) {
|
|
ctx.restore();
|
|
}
|
|
};
|
|
/**
|
|
* Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
|
|
* See {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} for more information.
|
|
* @method linearGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @return {Fill} Returns this Stroke object for chaining or assignment.
|
|
*/
|
|
p.linearGradient = G.Fill.prototype.linearGradient;
|
|
/**
|
|
* Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
|
|
* See {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} for more information.
|
|
* @method radialGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} r0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} r1
|
|
* @return {Fill} Returns this Stroke object for chaining or assignment.
|
|
*/
|
|
p.radialGradient = G.Fill.prototype.radialGradient;
|
|
/**
|
|
* Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
|
|
* See {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} for more information.
|
|
* @method bitmap
|
|
* @param {HTMLImageElement} image
|
|
* @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
|
|
* @return {Fill} Returns this Stroke object for chaining or assignment.
|
|
*/
|
|
p.bitmap = G.Fill.prototype.bitmap;
|
|
p.path = false;
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class StrokeStyle
|
|
* @constructor
|
|
* @param {Number} width
|
|
* @param {String} [caps=butt]
|
|
* @param {String} [joints=miter]
|
|
* @param {Number} [miterLimit=10]
|
|
* @param {Boolean} [ignoreScale=false]
|
|
**/
|
|
/**
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* One of: butt, round, square
|
|
* @property caps
|
|
* @type String
|
|
*/
|
|
/**
|
|
* One of: round, bevel, miter
|
|
* @property joints
|
|
* @type String
|
|
*/
|
|
/**
|
|
* @property miterLimit
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
p = (G.StrokeStyle = function (width, caps, joints, miterLimit, ignoreScale) {
|
|
this.width = width;
|
|
this.caps = caps;
|
|
this.joints = joints;
|
|
this.miterLimit = miterLimit;
|
|
this.ignoreScale = ignoreScale;
|
|
}).prototype;
|
|
p.exec = function (ctx) {
|
|
ctx.lineWidth = this.width == null ? "1" : this.width;
|
|
ctx.lineCap = this.caps == null ? "butt" : isNaN(this.caps) ? this.caps : Graphics.STROKE_CAPS_MAP[this.caps];
|
|
ctx.lineJoin = this.joints == null ? "miter" : isNaN(this.joints) ? this.joints : Graphics.STROKE_JOINTS_MAP[this.joints];
|
|
ctx.miterLimit = this.miterLimit == null ? "10" : this.miterLimit;
|
|
ctx.ignoreScale = this.ignoreScale == null ? false : this.ignoreScale;
|
|
};
|
|
p.path = false;
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class StrokeDash
|
|
* @constructor
|
|
* @param {Array} [segments]
|
|
* @param {Number} [offset=0]
|
|
**/
|
|
/**
|
|
* @property segments
|
|
* @type Array
|
|
*/
|
|
/**
|
|
* @property offset
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.StrokeDash = function (segments, offset) {
|
|
this.segments = segments;
|
|
this.offset = offset || 0;
|
|
}).prototype.exec = function (ctx) {
|
|
if (ctx.setLineDash) {
|
|
// feature detection.
|
|
ctx.setLineDash(this.segments || G.StrokeDash.EMPTY_SEGMENTS); // instead of [] to reduce churn.
|
|
ctx.lineDashOffset = this.offset || 0;
|
|
}
|
|
};
|
|
/**
|
|
* The default value for segments (ie. no dash).
|
|
* @property EMPTY_SEGMENTS
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
G.StrokeDash.EMPTY_SEGMENTS = [];
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class RoundRect
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
* @param {Number} radiusTL
|
|
* @param {Number} radiusTR
|
|
* @param {Number} radiusBR
|
|
* @param {Number} radiusBL
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property w
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property h
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusTL
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusTR
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusBR
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusBL
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.RoundRect = function (x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
|
|
this.x = x;this.y = y;
|
|
this.w = w;this.h = h;
|
|
this.radiusTL = radiusTL;this.radiusTR = radiusTR;
|
|
this.radiusBR = radiusBR;this.radiusBL = radiusBL;
|
|
}).prototype.exec = function (ctx) {
|
|
var max = (w < h ? w : h) / 2;
|
|
var mTL = 0,
|
|
mTR = 0,
|
|
mBR = 0,
|
|
mBL = 0;
|
|
var x = this.x,
|
|
y = this.y,
|
|
w = this.w,
|
|
h = this.h;
|
|
var rTL = this.radiusTL,
|
|
rTR = this.radiusTR,
|
|
rBR = this.radiusBR,
|
|
rBL = this.radiusBL;
|
|
|
|
if (rTL < 0) {
|
|
rTL *= mTL = -1;
|
|
}
|
|
if (rTL > max) {
|
|
rTL = max;
|
|
}
|
|
if (rTR < 0) {
|
|
rTR *= mTR = -1;
|
|
}
|
|
if (rTR > max) {
|
|
rTR = max;
|
|
}
|
|
if (rBR < 0) {
|
|
rBR *= mBR = -1;
|
|
}
|
|
if (rBR > max) {
|
|
rBR = max;
|
|
}
|
|
if (rBL < 0) {
|
|
rBL *= mBL = -1;
|
|
}
|
|
if (rBL > max) {
|
|
rBL = max;
|
|
}
|
|
|
|
ctx.moveTo(x + w - rTR, y);
|
|
ctx.arcTo(x + w + rTR * mTR, y - rTR * mTR, x + w, y + rTR, rTR);
|
|
ctx.lineTo(x + w, y + h - rBR);
|
|
ctx.arcTo(x + w + rBR * mBR, y + h + rBR * mBR, x + w - rBR, y + h, rBR);
|
|
ctx.lineTo(x + rBL, y + h);
|
|
ctx.arcTo(x - rBL * mBL, y + h + rBL * mBL, x, y + h - rBL, rBL);
|
|
ctx.lineTo(x, y + rTL);
|
|
ctx.arcTo(x - rTL * mTL, y - rTL * mTL, x + rTL, y, rTL);
|
|
ctx.closePath();
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Circle
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.Circle = function (x, y, radius) {
|
|
this.x = x;this.y = y;
|
|
this.radius = radius;
|
|
}).prototype.exec = function (ctx) {
|
|
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Ellipse
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property w
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property h
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.Ellipse = function (x, y, w, h) {
|
|
this.x = x;this.y = y;
|
|
this.w = w;this.h = h;
|
|
}).prototype.exec = function (ctx) {
|
|
var x = this.x,
|
|
y = this.y;
|
|
var w = this.w,
|
|
h = this.h;
|
|
|
|
var k = 0.5522848;
|
|
var ox = w / 2 * k;
|
|
var oy = h / 2 * k;
|
|
var xe = x + w;
|
|
var ye = y + h;
|
|
var xm = x + w / 2;
|
|
var ym = y + h / 2;
|
|
|
|
ctx.moveTo(x, ym);
|
|
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
|
|
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
|
|
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
|
|
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class PolyStar
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} sides
|
|
* @param {Number} pointSize
|
|
* @param {Number} angle
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property sides
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property pointSize
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property angle
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* Execute the Graphics command in the provided Canvas context.
|
|
* @method exec
|
|
* @param {CanvasRenderingContext2D} ctx The canvas rendering context
|
|
*/
|
|
(G.PolyStar = function (x, y, radius, sides, pointSize, angle) {
|
|
this.x = x;this.y = y;
|
|
this.radius = radius;
|
|
this.sides = sides;
|
|
this.pointSize = pointSize;
|
|
this.angle = angle;
|
|
}).prototype.exec = function (ctx) {
|
|
var x = this.x,
|
|
y = this.y;
|
|
var radius = this.radius;
|
|
var angle = (this.angle || 0) / 180 * Math.PI;
|
|
var sides = this.sides;
|
|
var ps = 1 - (this.pointSize || 0);
|
|
var a = Math.PI / sides;
|
|
|
|
ctx.moveTo(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);
|
|
for (var i = 0; i < sides; i++) {
|
|
angle += a;
|
|
if (ps != 1) {
|
|
ctx.lineTo(x + Math.cos(angle) * radius * ps, y + Math.sin(angle) * radius * ps);
|
|
}
|
|
angle += a;
|
|
ctx.lineTo(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);
|
|
}
|
|
ctx.closePath();
|
|
};
|
|
|
|
// docced above.
|
|
Graphics.beginCmd = new G.BeginPath(); // so we don't have to instantiate multiple instances.
|
|
|
|
|
|
createjs.Graphics = Graphics;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// DisplayObject.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* DisplayObject is an abstract class that should not be constructed directly. Instead construct subclasses such as
|
|
* {{#crossLink "Container"}}{{/crossLink}}, {{#crossLink "Bitmap"}}{{/crossLink}}, and {{#crossLink "Shape"}}{{/crossLink}}.
|
|
* DisplayObject is the base class for all display classes in the EaselJS library. It defines the core properties and
|
|
* methods that are shared between all display objects, such as transformation properties (x, y, scaleX, scaleY, etc),
|
|
* caching, and mouse handlers.
|
|
* @class DisplayObject
|
|
* @extends EventDispatcher
|
|
* @constructor
|
|
**/
|
|
|
|
function DisplayObject() {
|
|
this.EventDispatcher_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque.
|
|
* @property alpha
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this.alpha = 1;
|
|
|
|
/**
|
|
* If a cache is active, this returns the canvas that holds the cached version of this display object. See {{#crossLink "cache"}}{{/crossLink}}
|
|
* for more information.
|
|
* @property cacheCanvas
|
|
* @type {HTMLCanvasElement | Object}
|
|
* @default null
|
|
* @readonly
|
|
**/
|
|
this.cacheCanvas = null;
|
|
|
|
/**
|
|
* Returns an ID number that uniquely identifies the current cache for this display object. This can be used to
|
|
* determine if the cache has changed since a previous check.
|
|
* @property cacheID
|
|
* @type {Number}
|
|
* @default 0
|
|
*/
|
|
this.cacheID = 0;
|
|
|
|
/**
|
|
* Unique ID for this display object. Makes display objects easier for some uses.
|
|
* @property id
|
|
* @type {Number}
|
|
* @default -1
|
|
**/
|
|
this.id = createjs.UID.get();
|
|
|
|
/**
|
|
* Indicates whether to include this object when running mouse interactions. Setting this to `false` for children
|
|
* of a {{#crossLink "Container"}}{{/crossLink}} will cause events on the Container to not fire when that child is
|
|
* clicked. Setting this property to `false` does not prevent the {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}
|
|
* method from returning the child.
|
|
*
|
|
* <strong>Note:</strong> In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please
|
|
* check out the latest NEXT version in <a href="https://github.com/CreateJS/EaselJS/tree/master/lib">GitHub</a> for an updated version with this issue resolved. The fix will be
|
|
* provided in the next release of EaselJS.
|
|
* @property mouseEnabled
|
|
* @type {Boolean}
|
|
* @default true
|
|
**/
|
|
this.mouseEnabled = true;
|
|
|
|
/**
|
|
* If false, the tick will not run on this display object (or its children). This can provide some performance benefits.
|
|
* In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
|
|
* on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
|
|
* @property tickEnabled
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.tickEnabled = true;
|
|
|
|
/**
|
|
* An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for
|
|
* debugging.
|
|
* @property name
|
|
* @type {String}
|
|
* @default null
|
|
**/
|
|
this.name = null;
|
|
|
|
/**
|
|
* A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that
|
|
* contains this display object, or null if it has not been added
|
|
* to one.
|
|
* @property parent
|
|
* @final
|
|
* @type {Container}
|
|
* @default null
|
|
* @readonly
|
|
**/
|
|
this.parent = null;
|
|
|
|
/**
|
|
* The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate
|
|
* around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50.
|
|
* @property regX
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.regX = 0;
|
|
|
|
/**
|
|
* The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around
|
|
* its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50.
|
|
* @property regY
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.regY = 0;
|
|
|
|
/**
|
|
* The rotation in degrees for this display object.
|
|
* @property rotation
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.rotation = 0;
|
|
|
|
/**
|
|
* The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display
|
|
* object to twice its nominal width. To horizontally flip an object, set the scale to a negative number.
|
|
* @property scaleX
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this.scaleX = 1;
|
|
|
|
/**
|
|
* The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display
|
|
* object to half its nominal height. To vertically flip an object, set the scale to a negative number.
|
|
* @property scaleY
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this.scaleY = 1;
|
|
|
|
/**
|
|
* The factor to skew this display object horizontally.
|
|
* @property skewX
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.skewX = 0;
|
|
|
|
/**
|
|
* The factor to skew this display object vertically.
|
|
* @property skewY
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.skewY = 0;
|
|
|
|
/**
|
|
* A shadow object that defines the shadow to render on this display object. Set to `null` to remove a shadow. If
|
|
* null, this property is inherited from the parent container.
|
|
* @property shadow
|
|
* @type {Shadow}
|
|
* @default null
|
|
**/
|
|
this.shadow = null;
|
|
|
|
/**
|
|
* Indicates whether this display object should be rendered to the canvas and included when running the Stage
|
|
* {{#crossLink "Stage/getObjectsUnderPoint"}}{{/crossLink}} method.
|
|
* @property visible
|
|
* @type {Boolean}
|
|
* @default true
|
|
**/
|
|
this.visible = true;
|
|
|
|
/**
|
|
* The x (horizontal) position of the display object, relative to its parent.
|
|
* @property x
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.x = 0;
|
|
|
|
/** The y (vertical) position of the display object, relative to its parent.
|
|
* @property y
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.y = 0;
|
|
|
|
/**
|
|
* If set, defines the transformation for this display object, overriding all other transformation properties
|
|
* (x, y, rotation, scale, skew).
|
|
* @property transformMatrix
|
|
* @type {Matrix2D}
|
|
* @default null
|
|
**/
|
|
this.transformMatrix = null;
|
|
|
|
/**
|
|
* The composite operation indicates how the pixels of this display object will be composited with the elements
|
|
* behind it. If `null`, this property is inherited from the parent container. For more information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">
|
|
* whatwg spec on compositing</a>.
|
|
* @property compositeOperation
|
|
* @type {String}
|
|
* @default null
|
|
**/
|
|
this.compositeOperation = null;
|
|
|
|
/**
|
|
* Indicates whether the display object should be drawn to a whole pixel when
|
|
* {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole
|
|
* categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true).
|
|
* @property snapToPixel
|
|
* @type {Boolean}
|
|
* @default true
|
|
**/
|
|
this.snapToPixel = true;
|
|
|
|
/**
|
|
* An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}}
|
|
* or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is
|
|
* cached.
|
|
* @property filters
|
|
* @type {Array}
|
|
* @default null
|
|
**/
|
|
this.filters = null;
|
|
|
|
/**
|
|
* A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation
|
|
* will be applied relative to the display object's parent coordinates (as if it were a child of the parent).
|
|
* @property mask
|
|
* @type {Shape}
|
|
* @default null
|
|
*/
|
|
this.mask = null;
|
|
|
|
/**
|
|
* A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}.
|
|
* The hit area will have its transformation applied relative to this display object's coordinate space (as though
|
|
* the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested
|
|
* using only its own `alpha` value regardless of the alpha value on the target display object, or the target's
|
|
* ancestors (parents).
|
|
*
|
|
* If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events.
|
|
* This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false.
|
|
*
|
|
* Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}.
|
|
* @property hitArea
|
|
* @type {DisplayObject}
|
|
* @default null
|
|
*/
|
|
this.hitArea = null;
|
|
|
|
/**
|
|
* A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display
|
|
* object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to
|
|
* use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants.
|
|
* @property cursor
|
|
* @type {String}
|
|
* @default null
|
|
*/
|
|
this.cursor = null;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _cacheOffsetX
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._cacheOffsetX = 0;
|
|
|
|
/**
|
|
* @property _cacheOffsetY
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._cacheOffsetY = 0;
|
|
|
|
/**
|
|
* @property _filterOffsetX
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._filterOffsetX = 0;
|
|
|
|
/**
|
|
* @property _filterOffsetY
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._filterOffsetY = 0;
|
|
|
|
/**
|
|
* @property _cacheScale
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this._cacheScale = 1;
|
|
|
|
/**
|
|
* @property _cacheDataURLID
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
*/
|
|
this._cacheDataURLID = 0;
|
|
|
|
/**
|
|
* @property _cacheDataURL
|
|
* @protected
|
|
* @type {String}
|
|
* @default null
|
|
*/
|
|
this._cacheDataURL = null;
|
|
|
|
/**
|
|
* @property _props
|
|
* @protected
|
|
* @type {DisplayObject}
|
|
* @default null
|
|
**/
|
|
this._props = new createjs.DisplayProps();
|
|
|
|
/**
|
|
* @property _rectangle
|
|
* @protected
|
|
* @type {Rectangle}
|
|
* @default null
|
|
**/
|
|
this._rectangle = new createjs.Rectangle();
|
|
|
|
/**
|
|
* @property _bounds
|
|
* @protected
|
|
* @type {Rectangle}
|
|
* @default null
|
|
**/
|
|
this._bounds = null;
|
|
}
|
|
var p = createjs.extend(DisplayObject, createjs.EventDispatcher);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
// static properties:
|
|
/**
|
|
* Listing of mouse event names. Used in _hasMouseEventListener.
|
|
* @property _MOUSE_EVENTS
|
|
* @protected
|
|
* @static
|
|
* @type {Array}
|
|
**/
|
|
DisplayObject._MOUSE_EVENTS = ["click", "dblclick", "mousedown", "mouseout", "mouseover", "pressmove", "pressup", "rollout", "rollover"];
|
|
|
|
/**
|
|
* Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}}
|
|
* with cross domain content.
|
|
* @property suppressCrossDomainErrors
|
|
* @static
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
DisplayObject.suppressCrossDomainErrors = false;
|
|
|
|
/**
|
|
* @property _snapToPixelEnabled
|
|
* @protected
|
|
* @static
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access.
|
|
|
|
/**
|
|
* @property _hitTestCanvas
|
|
* @type {HTMLCanvasElement | Object}
|
|
* @static
|
|
* @protected
|
|
**/
|
|
/**
|
|
* @property _hitTestContext
|
|
* @type {CanvasRenderingContext2D}
|
|
* @static
|
|
* @protected
|
|
**/
|
|
var canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas"); // prevent errors on load in browsers without canvas.
|
|
if (canvas.getContext) {
|
|
DisplayObject._hitTestCanvas = canvas;
|
|
DisplayObject._hitTestContext = canvas.getContext("2d");
|
|
canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
/**
|
|
* @property _nextCacheID
|
|
* @type {Number}
|
|
* @static
|
|
* @protected
|
|
**/
|
|
DisplayObject._nextCacheID = 1;
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when the user presses their left mouse button over the display object. See the
|
|
* {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event mousedown
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user presses their left mouse button and then releases it while over the display object.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event click
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user double clicks their left mouse button over this display object.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event dblclick
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user's mouse enters this display object. This event must be enabled using
|
|
* {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event mouseover
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user's mouse leaves this display object. This event must be enabled using
|
|
* {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event mouseout
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* This event is similar to {{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}, with the following
|
|
* differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
|
|
* aggregate of their content.
|
|
*
|
|
* For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
|
|
* shapeA and then directly on to shapeB. With a listener for {{#crossLink "mouseover:event"}}{{/crossLink}} on
|
|
* myContainer, two events would be received, each targeting a child element:<OL>
|
|
* <LI>when the mouse enters shapeA (target=shapeA)</LI>
|
|
* <LI>when the mouse enters shapeB (target=shapeB)</LI>
|
|
* </OL>
|
|
* However, with a listener for "rollover" instead, only a single event is received when the mouse first enters
|
|
* the aggregate myContainer content (target=myContainer).
|
|
*
|
|
* This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event rollover
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following
|
|
* differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
|
|
* aggregate of their content.
|
|
*
|
|
* For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
|
|
* shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}}
|
|
* on myContainer, two events would be received, each targeting a child element:<OL>
|
|
* <LI>when the mouse leaves shapeA (target=shapeA)</LI>
|
|
* <LI>when the mouse leaves shapeB (target=shapeB)</LI>
|
|
* </OL>
|
|
* However, with a listener for "rollout" instead, only a single event is received when the mouse leaves
|
|
* the aggregate myContainer content (target=myContainer).
|
|
*
|
|
* This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event rollout
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove
|
|
* event will be generated on that object whenever the mouse moves until the mouse press is released. This can be
|
|
* useful for dragging and similar operations.
|
|
* @event pressmove
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event
|
|
* will be generated on that object when that mouse press is released. This can be useful for dragging and similar
|
|
* operations.
|
|
* @event pressup
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the display object is added to a parent container.
|
|
* @event added
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the display object is removed from its parent container.
|
|
* @event removed
|
|
*/
|
|
|
|
/**
|
|
* Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the
|
|
* rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on
|
|
* the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their
|
|
* {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being
|
|
* dispatched on their parent.
|
|
* @event tick
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For
|
|
* example if you called stage.update("hello"), then the params would be ["hello"].
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead.
|
|
* @method getStage
|
|
* @return {Stage}
|
|
* @deprecated
|
|
**/
|
|
p.getStage = function () {
|
|
// uses dynamic access to avoid circular dependencies;
|
|
var o = this,
|
|
_Stage = createjs["Stage"];
|
|
while (o.parent) {
|
|
o = o.parent;
|
|
}
|
|
if (o instanceof _Stage) {
|
|
return o;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one.
|
|
* @property stage
|
|
* @type {Stage}
|
|
* @readonly
|
|
**/
|
|
try {
|
|
Object.defineProperties(p, {
|
|
stage: { get: p.getStage }
|
|
});
|
|
} catch (e) {}
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns <code>true</code> if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
|
|
* used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
|
|
* @return {Boolean}
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
var cacheCanvas = this.cacheCanvas;
|
|
if (ignoreCache || !cacheCanvas) {
|
|
return false;
|
|
}
|
|
var scale = this._cacheScale;
|
|
ctx.drawImage(cacheCanvas, this._cacheOffsetX + this._filterOffsetX, this._cacheOffsetY + this._filterOffsetY, cacheCanvas.width / scale, cacheCanvas.height / scale);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow
|
|
* to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}.
|
|
* @method updateContext
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D to update.
|
|
**/
|
|
p.updateContext = function (ctx) {
|
|
var o = this,
|
|
mask = o.mask,
|
|
mtx = o._props.matrix;
|
|
|
|
if (mask && mask.graphics && !mask.graphics.isEmpty()) {
|
|
mask.getMatrix(mtx);
|
|
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
|
|
|
|
mask.graphics.drawAsPath(ctx);
|
|
ctx.clip();
|
|
|
|
mtx.invert();
|
|
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
|
|
}
|
|
|
|
this.getMatrix(mtx);
|
|
var tx = mtx.tx,
|
|
ty = mtx.ty;
|
|
if (DisplayObject._snapToPixelEnabled && o.snapToPixel) {
|
|
tx = tx + (tx < 0 ? -0.5 : 0.5) | 0;
|
|
ty = ty + (ty < 0 ? -0.5 : 0.5) | 0;
|
|
}
|
|
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty);
|
|
ctx.globalAlpha *= o.alpha;
|
|
if (o.compositeOperation) {
|
|
ctx.globalCompositeOperation = o.compositeOperation;
|
|
}
|
|
if (o.shadow) {
|
|
this._applyShadow(ctx, o.shadow);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into a new canvas, which is then used for subsequent draws. For complex content
|
|
* that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape),
|
|
* this can provide for much faster rendering because the content does not need to be re-rendered each tick. The
|
|
* cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must
|
|
* manually update the cache by calling <code>updateCache()</code> or <code>cache()</code> again. You must specify
|
|
* the cache area via the x, y, w, and h parameters. This defines the rectangle that will be rendered and cached
|
|
* using this display object's coordinates.
|
|
*
|
|
* <h4>Example</h4>
|
|
* For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25:
|
|
*
|
|
* var shape = new createjs.Shape();
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25);
|
|
* myShape.cache(-25, -25, 50, 50);
|
|
*
|
|
* Note that filters need to be defined <em>before</em> the cache is applied. Check out the {{#crossLink "Filter"}}{{/crossLink}}
|
|
* class for more information. Some filters (ex. BlurFilter) will not work as expected in conjunction with the scale param.
|
|
*
|
|
* Usually, the resulting cacheCanvas will have the dimensions width*scale by height*scale, however some filters (ex. BlurFilter)
|
|
* will add padding to the canvas dimensions.
|
|
*
|
|
* @method cache
|
|
* @param {Number} x The x coordinate origin for the cache region.
|
|
* @param {Number} y The y coordinate origin for the cache region.
|
|
* @param {Number} width The width of the cache region.
|
|
* @param {Number} height The height of the cache region.
|
|
* @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using
|
|
* myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate
|
|
* cached elements with greater fidelity. Default is 1.
|
|
**/
|
|
p.cache = function (x, y, width, height, scale) {
|
|
// draw to canvas.
|
|
scale = scale || 1;
|
|
if (!this.cacheCanvas) {
|
|
this.cacheCanvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
}
|
|
this._cacheWidth = width;
|
|
this._cacheHeight = height;
|
|
this._cacheOffsetX = x;
|
|
this._cacheOffsetY = y;
|
|
this._cacheScale = scale;
|
|
this.updateCache();
|
|
};
|
|
|
|
/**
|
|
* Redraws the display object to its cache. Calling updateCache without an active cache will throw an error.
|
|
* If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object
|
|
* will be drawn over the existing cache using the specified compositeOperation.
|
|
*
|
|
* <h4>Example</h4>
|
|
* Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line
|
|
* will be drawn on top of the old one.
|
|
*
|
|
* // Not shown: Creating the shape, and caching it.
|
|
* shapeInstance.clear();
|
|
* shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200);
|
|
* shapeInstance.updateCache();
|
|
*
|
|
* @method updateCache
|
|
* @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it.
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">
|
|
* whatwg spec on compositing</a>.
|
|
**/
|
|
p.updateCache = function (compositeOperation) {
|
|
var cacheCanvas = this.cacheCanvas;
|
|
if (!cacheCanvas) {
|
|
throw "cache() must be called before updateCache()";
|
|
}
|
|
var scale = this._cacheScale,
|
|
offX = this._cacheOffsetX * scale,
|
|
offY = this._cacheOffsetY * scale;
|
|
var w = this._cacheWidth,
|
|
h = this._cacheHeight,
|
|
ctx = cacheCanvas.getContext("2d");
|
|
|
|
var fBounds = this._getFilterBounds();
|
|
offX += this._filterOffsetX = fBounds.x;
|
|
offY += this._filterOffsetY = fBounds.y;
|
|
|
|
w = Math.ceil(w * scale) + fBounds.width;
|
|
h = Math.ceil(h * scale) + fBounds.height;
|
|
if (w != cacheCanvas.width || h != cacheCanvas.height) {
|
|
// TODO: it would be nice to preserve the content if there is a compositeOperation.
|
|
cacheCanvas.width = w;
|
|
cacheCanvas.height = h;
|
|
} else if (!compositeOperation) {
|
|
ctx.clearRect(0, 0, w + 1, h + 1);
|
|
}
|
|
|
|
ctx.save();
|
|
ctx.globalCompositeOperation = compositeOperation;
|
|
ctx.setTransform(scale, 0, 0, scale, -offX, -offY);
|
|
this.draw(ctx, true);
|
|
// TODO: filters and cache scale don't play well together at present.
|
|
this._applyFilters();
|
|
ctx.restore();
|
|
this.cacheID = DisplayObject._nextCacheID++;
|
|
};
|
|
|
|
/**
|
|
* Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information.
|
|
* @method uncache
|
|
**/
|
|
p.uncache = function () {
|
|
this._cacheDataURL = this.cacheCanvas = null;
|
|
this.cacheID = this._cacheOffsetX = this._cacheOffsetY = this._filterOffsetX = this._filterOffsetY = 0;
|
|
this._cacheScale = 1;
|
|
};
|
|
|
|
/**
|
|
* Returns a data URL for the cache, or null if this display object is not cached.
|
|
* Uses cacheID to ensure a new data URL is not generated if the cache has not changed.
|
|
* @method getCacheDataURL
|
|
* @return {String} The image data url for the cache.
|
|
**/
|
|
p.getCacheDataURL = function () {
|
|
if (!this.cacheCanvas) {
|
|
return null;
|
|
}
|
|
if (this.cacheID != this._cacheDataURLID) {
|
|
this._cacheDataURL = this.cacheCanvas.toDataURL();
|
|
}
|
|
return this._cacheDataURL;
|
|
};
|
|
|
|
/**
|
|
* Transforms the specified x and y position from the coordinate space of the display object
|
|
* to the global (stage) coordinate space. For example, this could be used to position an HTML label
|
|
* over a specific point on a nested display object. Returns a Point instance with x and y properties
|
|
* correlating to the transformed coordinates on the stage.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.x = 300;
|
|
* displayObject.y = 200;
|
|
* stage.addChild(displayObject);
|
|
* var point = displayObject.localToGlobal(100, 100);
|
|
* // Results in x=400, y=300
|
|
*
|
|
* @method localToGlobal
|
|
* @param {Number} x The x position in the source display object to transform.
|
|
* @param {Number} y The y position in the source display object to transform.
|
|
* @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
|
|
* @return {Point} A Point instance with x and y properties correlating to the transformed coordinates
|
|
* on the stage.
|
|
**/
|
|
p.localToGlobal = function (x, y, pt) {
|
|
return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x, y, pt || new createjs.Point());
|
|
};
|
|
|
|
/**
|
|
* Transforms the specified x and y position from the global (stage) coordinate space to the
|
|
* coordinate space of the display object. For example, this could be used to determine
|
|
* the current mouse position within the display object. Returns a Point instance with x and y properties
|
|
* correlating to the transformed position in the display object's coordinate space.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.x = 300;
|
|
* displayObject.y = 200;
|
|
* stage.addChild(displayObject);
|
|
* var point = displayObject.globalToLocal(100, 100);
|
|
* // Results in x=-200, y=-100
|
|
*
|
|
* @method globalToLocal
|
|
* @param {Number} x The x position on the stage to transform.
|
|
* @param {Number} y The y position on the stage to transform.
|
|
* @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
|
|
* @return {Point} A Point instance with x and y properties correlating to the transformed position in the
|
|
* display object's coordinate space.
|
|
**/
|
|
p.globalToLocal = function (x, y, pt) {
|
|
return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x, y, pt || new createjs.Point());
|
|
};
|
|
|
|
/**
|
|
* Transforms the specified x and y position from the coordinate space of this display object to the coordinate
|
|
* space of the target display object. Returns a Point instance with x and y properties correlating to the
|
|
* transformed position in the target's coordinate space. Effectively the same as using the following code with
|
|
* {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
|
|
*
|
|
* var pt = this.localToGlobal(x, y);
|
|
* pt = target.globalToLocal(pt.x, pt.y);
|
|
*
|
|
* @method localToLocal
|
|
* @param {Number} x The x position in the source display object to transform.
|
|
* @param {Number} y The y position on the source display object to transform.
|
|
* @param {DisplayObject} target The target display object to which the coordinates will be transformed.
|
|
* @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
|
|
* @return {Point} Returns a Point instance with x and y properties correlating to the transformed position
|
|
* in the target's coordinate space.
|
|
**/
|
|
p.localToLocal = function (x, y, target, pt) {
|
|
pt = this.localToGlobal(x, y, pt);
|
|
return target.globalToLocal(pt.x, pt.y, pt);
|
|
};
|
|
|
|
/**
|
|
* Shortcut method to quickly set the transform properties on the display object. All parameters are optional.
|
|
* Omitted parameters will have the default value set.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.setTransform(100, 100, 2, 2);
|
|
*
|
|
* @method setTransform
|
|
* @param {Number} [x=0] The horizontal translation (x position) in pixels
|
|
* @param {Number} [y=0] The vertical translation (y position) in pixels
|
|
* @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1
|
|
* @param {Number} [scaleY=1] the vertical scale, as a percentage of 1
|
|
* @param {Number} [rotation=0] The rotation, in degrees
|
|
* @param {Number} [skewX=0] The horizontal skew factor
|
|
* @param {Number} [skewY=0] The vertical skew factor
|
|
* @param {Number} [regX=0] The horizontal registration point in pixels
|
|
* @param {Number} [regY=0] The vertical registration point in pixels
|
|
* @return {DisplayObject} Returns this instance. Useful for chaining commands.
|
|
* @chainable
|
|
*/
|
|
p.setTransform = function (x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
this.scaleX = scaleX == null ? 1 : scaleX;
|
|
this.scaleY = scaleY == null ? 1 : scaleY;
|
|
this.rotation = rotation || 0;
|
|
this.skewX = skewX || 0;
|
|
this.skewY = skewY || 0;
|
|
this.regX = regX || 0;
|
|
this.regY = regY || 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a matrix based on this object's current transform.
|
|
* @method getMatrix
|
|
* @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new
|
|
* Matrix object is returned.
|
|
* @return {Matrix2D} A matrix representing this display object's transform.
|
|
**/
|
|
p.getMatrix = function (matrix) {
|
|
var o = this,
|
|
mtx = matrix && matrix.identity() || new createjs.Matrix2D();
|
|
return o.transformMatrix ? mtx.copy(o.transformMatrix) : mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
|
|
};
|
|
|
|
/**
|
|
* Generates a Matrix2D object representing the combined transform of the display object and all of its
|
|
* parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can
|
|
* be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
|
|
* and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
|
|
* @method getConcatenatedMatrix
|
|
* @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values.
|
|
* If null, a new Matrix2D object is returned.
|
|
* @return {Matrix2D} The combined matrix.
|
|
**/
|
|
p.getConcatenatedMatrix = function (matrix) {
|
|
var o = this,
|
|
mtx = this.getMatrix(matrix);
|
|
while (o = o.parent) {
|
|
mtx.prependMatrix(o.getMatrix(o._props.matrix));
|
|
}
|
|
return mtx;
|
|
};
|
|
|
|
/**
|
|
* Generates a DisplayProps object representing the combined display properties of the object and all of its
|
|
* parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}).
|
|
* @method getConcatenatedDisplayProps
|
|
* @param {DisplayProps} [props] A {{#crossLink "DisplayProps"}}{{/crossLink}} object to populate with the calculated values.
|
|
* If null, a new DisplayProps object is returned.
|
|
* @return {DisplayProps} The combined display properties.
|
|
**/
|
|
p.getConcatenatedDisplayProps = function (props) {
|
|
props = props ? props.identity() : new createjs.DisplayProps();
|
|
var o = this,
|
|
mtx = o.getMatrix(props.matrix);
|
|
do {
|
|
props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation);
|
|
|
|
// we do this to avoid problems with the matrix being used for both operations when o._props.matrix is passed in as the props param.
|
|
// this could be simplified (ie. just done as part of the prepend above) if we switched to using a pool.
|
|
if (o != this) {
|
|
mtx.prependMatrix(o.getMatrix(o._props.matrix));
|
|
}
|
|
} while (o = o.parent);
|
|
return props;
|
|
};
|
|
|
|
/**
|
|
* Tests whether the display object intersects the specified point in <em>local</em> coordinates (ie. draws a pixel
|
|
* with alpha > 0 at the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation
|
|
* of the display object.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var myShape = new createjs.Shape();
|
|
* myShape.graphics.beginFill("red").drawRect(100, 100, 20, 50);
|
|
*
|
|
* console.log(myShape.hitTest(10,10); // false
|
|
* console.log(myShape.hitTest(110, 25); // true
|
|
*
|
|
* Note that to use Stage coordinates (such as {{#crossLink "Stage/mouseX:property"}}{{/crossLink}}), they must
|
|
* first be converted to local coordinates:
|
|
*
|
|
* stage.addEventListener("stagemousedown", handleMouseDown);
|
|
* function handleMouseDown(event) {
|
|
* var p = myShape.globalToLocal(stage.mouseX, stage.mouseY);
|
|
* var hit = myShape.hitTest(p.x, p.y);
|
|
* }
|
|
*
|
|
* Shape-to-shape collision is not currently supported by EaselJS.
|
|
*
|
|
* @method hitTest
|
|
* @param {Number} x The x position to check in the display object's local coordinates.
|
|
* @param {Number} y The y position to check in the display object's local coordinates.
|
|
* @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject intersect the specified
|
|
* local Point.
|
|
*/
|
|
p.hitTest = function (x, y) {
|
|
var ctx = DisplayObject._hitTestContext;
|
|
ctx.setTransform(1, 0, 0, 1, -x, -y);
|
|
this.draw(ctx);
|
|
|
|
var hit = this._testHit(ctx);
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, 2, 2);
|
|
return hit;
|
|
};
|
|
|
|
/**
|
|
* Provides a chainable shortcut method for setting a number of properties on the instance.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25);
|
|
* var shape = stage.addChild(new createjs.Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5});
|
|
*
|
|
* @method set
|
|
* @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
|
|
* @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
*/
|
|
p.set = function (props) {
|
|
for (var n in props) {
|
|
this[n] = props[n];
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation).
|
|
* Objects that have been cached will return the bounds of the cache.
|
|
*
|
|
* Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
|
|
* {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
|
|
* bounds.
|
|
*
|
|
* <table>
|
|
* <tr><td><b>All</b></td><td>
|
|
* All display objects support setting bounds manually using setBounds(). Likewise, display objects that
|
|
* have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override
|
|
* the automatic calculations listed below.
|
|
* </td></tr>
|
|
* <tr><td><b>Bitmap</b></td><td>
|
|
* Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0).
|
|
* </td></tr>
|
|
* <tr><td><b>Sprite</b></td><td>
|
|
* Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified
|
|
* in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}}
|
|
* </td></tr>
|
|
* <tr><td><b>Container</b></td><td>
|
|
* Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds().
|
|
* </td></tr>
|
|
* <tr><td><b>Shape</b></td><td>
|
|
* Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds.
|
|
* </td></tr>
|
|
* <tr><td><b>Text</b></td><td>
|
|
* Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are
|
|
* not, especially when using textBaseline values other than "top".
|
|
* </td></tr>
|
|
* <tr><td><b>BitmapText</b></td><td>
|
|
* Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close
|
|
* to (x=0,y=0).
|
|
* </td></tr>
|
|
* </table>
|
|
*
|
|
* Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and
|
|
* are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the
|
|
* bounds explicitly:
|
|
*
|
|
* var bounds = obj.getBounds();
|
|
* obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
* // getBounds will now use the set values, instead of recalculating
|
|
*
|
|
* To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
|
|
* values if you need to retain it.
|
|
*
|
|
* var myBounds = obj.getBounds().clone();
|
|
* // OR:
|
|
* myRect.copy(obj.getBounds());
|
|
*
|
|
* @method getBounds
|
|
* @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this
|
|
* object.
|
|
**/
|
|
p.getBounds = function () {
|
|
if (this._bounds) {
|
|
return this._rectangle.copy(this._bounds);
|
|
}
|
|
var cacheCanvas = this.cacheCanvas;
|
|
if (cacheCanvas) {
|
|
var scale = this._cacheScale;
|
|
return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width / scale, cacheCanvas.height / scale);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied).
|
|
* Objects that have been cached will return the transformed bounds of the cache.
|
|
*
|
|
* Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
|
|
* {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
|
|
* bounds.
|
|
*
|
|
* To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
|
|
* values if you need to retain it.
|
|
*
|
|
* Container instances calculate aggregate bounds for all children that return bounds via getBounds.
|
|
* @method getTransformedBounds
|
|
* @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object.
|
|
**/
|
|
p.getTransformedBounds = function () {
|
|
return this._getBounds();
|
|
};
|
|
|
|
/**
|
|
* Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape &
|
|
* Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always
|
|
* override calculated bounds.
|
|
*
|
|
* The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance
|
|
* with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50).
|
|
* @method setBounds
|
|
* @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds.
|
|
* @param {Number} y The y origin of the bounds.
|
|
* @param {Number} width The width of the bounds.
|
|
* @param {Number} height The height of the bounds.
|
|
**/
|
|
p.setBounds = function (x, y, width, height) {
|
|
if (x == null) {
|
|
this._bounds = x;
|
|
}
|
|
this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are
|
|
* reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements
|
|
* are copied by reference (masks, individual filter instances, hit area)
|
|
* @method clone
|
|
* @return {DisplayObject} A clone of the current DisplayObject instance.
|
|
**/
|
|
p.clone = function () {
|
|
return this._cloneProps(new DisplayObject());
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[DisplayObject (name=" + this.name + ")]";
|
|
};
|
|
|
|
// private methods:
|
|
// separated so it can be used more easily in subclasses:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject
|
|
* instance copied into.
|
|
* @return {DisplayObject} o
|
|
* @protected
|
|
**/
|
|
p._cloneProps = function (o) {
|
|
o.alpha = this.alpha;
|
|
o.mouseEnabled = this.mouseEnabled;
|
|
o.tickEnabled = this.tickEnabled;
|
|
o.name = this.name;
|
|
o.regX = this.regX;
|
|
o.regY = this.regY;
|
|
o.rotation = this.rotation;
|
|
o.scaleX = this.scaleX;
|
|
o.scaleY = this.scaleY;
|
|
o.shadow = this.shadow;
|
|
o.skewX = this.skewX;
|
|
o.skewY = this.skewY;
|
|
o.visible = this.visible;
|
|
o.x = this.x;
|
|
o.y = this.y;
|
|
o.compositeOperation = this.compositeOperation;
|
|
o.snapToPixel = this.snapToPixel;
|
|
o.filters = this.filters == null ? null : this.filters.slice(0);
|
|
o.mask = this.mask;
|
|
o.hitArea = this.hitArea;
|
|
o.cursor = this.cursor;
|
|
o._bounds = this._bounds;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _applyShadow
|
|
* @protected
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @param {Shadow} shadow
|
|
**/
|
|
p._applyShadow = function (ctx, shadow) {
|
|
shadow = shadow || Shadow.identity;
|
|
ctx.shadowColor = shadow.color;
|
|
ctx.shadowOffsetX = shadow.offsetX;
|
|
ctx.shadowOffsetY = shadow.offsetY;
|
|
ctx.shadowBlur = shadow.blur;
|
|
};
|
|
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* @protected
|
|
**/
|
|
p._tick = function (evtObj) {
|
|
// because tick can be really performance sensitive, check for listeners before calling dispatchEvent.
|
|
var ls = this._listeners;
|
|
if (ls && ls["tick"]) {
|
|
// reset & reuse the event object to avoid construction / GC costs:
|
|
evtObj.target = null;
|
|
evtObj.propagationStopped = evtObj.immediatePropagationStopped = false;
|
|
this.dispatchEvent(evtObj);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _testHit
|
|
* @protected
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @return {Boolean}
|
|
**/
|
|
p._testHit = function (ctx) {
|
|
try {
|
|
var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
|
|
} catch (e) {
|
|
if (!DisplayObject.suppressCrossDomainErrors) {
|
|
throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
|
|
}
|
|
}
|
|
return hit;
|
|
};
|
|
|
|
/**
|
|
* @method _applyFilters
|
|
* @protected
|
|
**/
|
|
p._applyFilters = function () {
|
|
if (!this.filters || this.filters.length == 0 || !this.cacheCanvas) {
|
|
return;
|
|
}
|
|
var l = this.filters.length;
|
|
var ctx = this.cacheCanvas.getContext("2d");
|
|
var w = this.cacheCanvas.width;
|
|
var h = this.cacheCanvas.height;
|
|
for (var i = 0; i < l; i++) {
|
|
this.filters[i].applyFilter(ctx, 0, 0, w, h);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _getFilterBounds
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._getFilterBounds = function (rect) {
|
|
var l,
|
|
filters = this.filters,
|
|
bounds = this._rectangle.setValues(0, 0, 0, 0);
|
|
if (!filters || !(l = filters.length)) {
|
|
return bounds;
|
|
}
|
|
|
|
for (var i = 0; i < l; i++) {
|
|
var f = this.filters[i];
|
|
f.getBounds && f.getBounds(bounds);
|
|
}
|
|
return bounds;
|
|
};
|
|
|
|
/**
|
|
* @method _getBounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform If true, does not apply this object's transform.
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._getBounds = function (matrix, ignoreTransform) {
|
|
return this._transformBounds(this.getBounds(), matrix, ignoreTransform);
|
|
};
|
|
|
|
/**
|
|
* @method _transformBounds
|
|
* @param {Rectangle} bounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._transformBounds = function (bounds, matrix, ignoreTransform) {
|
|
if (!bounds) {
|
|
return bounds;
|
|
}
|
|
var x = bounds.x,
|
|
y = bounds.y,
|
|
width = bounds.width,
|
|
height = bounds.height,
|
|
mtx = this._props.matrix;
|
|
mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
|
|
|
|
if (x || y) {
|
|
mtx.appendTransform(0, 0, 1, 1, 0, 0, 0, -x, -y);
|
|
} // TODO: simplify this.
|
|
if (matrix) {
|
|
mtx.prependMatrix(matrix);
|
|
}
|
|
|
|
var x_a = width * mtx.a,
|
|
x_b = width * mtx.b;
|
|
var y_c = height * mtx.c,
|
|
y_d = height * mtx.d;
|
|
var tx = mtx.tx,
|
|
ty = mtx.ty;
|
|
|
|
var minX = tx,
|
|
maxX = tx,
|
|
minY = ty,
|
|
maxY = ty;
|
|
|
|
if ((x = x_a + tx) < minX) {
|
|
minX = x;
|
|
} else if (x > maxX) {
|
|
maxX = x;
|
|
}
|
|
if ((x = x_a + y_c + tx) < minX) {
|
|
minX = x;
|
|
} else if (x > maxX) {
|
|
maxX = x;
|
|
}
|
|
if ((x = y_c + tx) < minX) {
|
|
minX = x;
|
|
} else if (x > maxX) {
|
|
maxX = x;
|
|
}
|
|
|
|
if ((y = x_b + ty) < minY) {
|
|
minY = y;
|
|
} else if (y > maxY) {
|
|
maxY = y;
|
|
}
|
|
if ((y = x_b + y_d + ty) < minY) {
|
|
minY = y;
|
|
} else if (y > maxY) {
|
|
maxY = y;
|
|
}
|
|
if ((y = y_d + ty) < minY) {
|
|
minY = y;
|
|
} else if (y > maxY) {
|
|
maxY = y;
|
|
}
|
|
|
|
return bounds.setValues(minX, minY, maxX - minX, maxY - minY);
|
|
};
|
|
|
|
/**
|
|
* Indicates whether the display object has any mouse event listeners or a cursor.
|
|
* @method _isMouseOpaque
|
|
* @return {Boolean}
|
|
* @protected
|
|
**/
|
|
p._hasMouseEventListener = function () {
|
|
var evts = DisplayObject._MOUSE_EVENTS;
|
|
for (var i = 0, l = evts.length; i < l; i++) {
|
|
if (this.hasEventListener(evts[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return !!this.cursor;
|
|
};
|
|
|
|
createjs.DisplayObject = createjs.promote(DisplayObject, "EventDispatcher");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Container.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* A Container is a nestable display list that allows you to work with compound display elements. For example you could
|
|
* group arm, leg, torso and head {{#crossLink "Bitmap"}}{{/crossLink}} instances together into a Person Container, and
|
|
* transform them as a group, while still being able to move the individual parts relative to each other. Children of
|
|
* containers have their <code>transform</code> and <code>alpha</code> properties concatenated with their parent
|
|
* Container.
|
|
*
|
|
* For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with <code>x=50</code>
|
|
* and <code>alpha=0.7</code> will be rendered to the canvas at <code>x=150</code> and <code>alpha=0.35</code>.
|
|
* Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var container = new createjs.Container();
|
|
* container.addChild(bitmapInstance, shapeInstance);
|
|
* container.x = 100;
|
|
*
|
|
* @class Container
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
**/
|
|
|
|
function Container() {
|
|
this.DisplayObject_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The array of children in the display list. You should usually use the child management methods such as
|
|
* {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}},
|
|
* {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is
|
|
* included for advanced uses.
|
|
* @property children
|
|
* @type Array
|
|
* @default null
|
|
**/
|
|
this.children = [];
|
|
|
|
/**
|
|
* Indicates whether the children of this container are independently enabled for mouse/pointer interaction.
|
|
* If false, the children will be aggregated under the container - for example, a click on a child shape would
|
|
* trigger a click event on the container.
|
|
* @property mouseChildren
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.mouseChildren = true;
|
|
|
|
/**
|
|
* If false, the tick will not be propagated to children of this Container. This can provide some performance benefits.
|
|
* In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
|
|
* on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
|
|
* @property tickChildren
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.tickChildren = true;
|
|
}
|
|
var p = createjs.extend(Container, createjs.DisplayObject);
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "Container/numChildren:property"}}{{/crossLink}} property instead.
|
|
* @method getNumChildren
|
|
* @return {Number}
|
|
* @deprecated
|
|
**/
|
|
p.getNumChildren = function () {
|
|
return this.children.length;
|
|
};
|
|
|
|
/**
|
|
* Returns the number of children in the container.
|
|
* @property numChildren
|
|
* @type {Number}
|
|
* @readonly
|
|
**/
|
|
try {
|
|
Object.defineProperties(p, {
|
|
numChildren: { get: p.getNumChildren }
|
|
});
|
|
} catch (e) {}
|
|
|
|
// public methods:
|
|
/**
|
|
* Constructor alias for backwards compatibility. This method will be removed in future versions.
|
|
* Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
|
|
* @method initialize
|
|
* @deprecated in favour of `createjs.promote()`
|
|
**/
|
|
p.initialize = Container; // TODO: deprecated.
|
|
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
var hasContent = this.cacheCanvas || this.children.length;
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) {
|
|
return true;
|
|
}
|
|
|
|
// this ensures we don't have issues with display list changes that occur during a draw:
|
|
var list = this.children.slice();
|
|
for (var i = 0, l = list.length; i < l; i++) {
|
|
var child = list[i];
|
|
if (!child.isVisible()) {
|
|
continue;
|
|
}
|
|
|
|
// draw the child:
|
|
ctx.save();
|
|
child.updateContext(ctx);
|
|
child.draw(ctx);
|
|
ctx.restore();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Adds a child to the top of the display list.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.addChild(bitmapInstance);
|
|
*
|
|
* You can also add multiple children at once:
|
|
*
|
|
* container.addChild(bitmapInstance, shapeInstance, textInstance);
|
|
*
|
|
* @method addChild
|
|
* @param {DisplayObject} child The display object to add.
|
|
* @return {DisplayObject} The child that was added, or the last child if multiple children were added.
|
|
**/
|
|
p.addChild = function (child) {
|
|
if (child == null) {
|
|
return child;
|
|
}
|
|
var l = arguments.length;
|
|
if (l > 1) {
|
|
for (var i = 0; i < l; i++) {
|
|
this.addChild(arguments[i]);
|
|
}
|
|
return arguments[l - 1];
|
|
}
|
|
if (child.parent) {
|
|
child.parent.removeChild(child);
|
|
}
|
|
child.parent = this;
|
|
this.children.push(child);
|
|
child.dispatchEvent("added");
|
|
return child;
|
|
};
|
|
|
|
/**
|
|
* Adds a child to the display list at the specified index, bumping children at equal or greater indexes up one, and
|
|
* setting its parent to this Container.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* addChildAt(child1, index);
|
|
*
|
|
* You can also add multiple children, such as:
|
|
*
|
|
* addChildAt(child1, child2, ..., index);
|
|
*
|
|
* The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list,
|
|
* you could use:
|
|
*
|
|
* container.addChildAt(myShape, container.getChildIndex(otherShape));
|
|
*
|
|
* This would also bump otherShape's index up by one. Fails silently if the index is out of range.
|
|
*
|
|
* @method addChildAt
|
|
* @param {DisplayObject} child The display object to add.
|
|
* @param {Number} index The index to add the child at.
|
|
* @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added.
|
|
**/
|
|
p.addChildAt = function (child, index) {
|
|
var l = arguments.length;
|
|
var indx = arguments[l - 1]; // can't use the same name as the index param or it replaces arguments[1]
|
|
if (indx < 0 || indx > this.children.length) {
|
|
return arguments[l - 2];
|
|
}
|
|
if (l > 2) {
|
|
for (var i = 0; i < l - 1; i++) {
|
|
this.addChildAt(arguments[i], indx + i);
|
|
}
|
|
return arguments[l - 2];
|
|
}
|
|
if (child.parent) {
|
|
child.parent.removeChild(child);
|
|
}
|
|
child.parent = this;
|
|
this.children.splice(index, 0, child);
|
|
child.dispatchEvent("added");
|
|
return child;
|
|
};
|
|
|
|
/**
|
|
* Removes the specified child from the display list. Note that it is faster to use removeChildAt() if the index is
|
|
* already known.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.removeChild(child);
|
|
*
|
|
* You can also remove multiple children:
|
|
*
|
|
* removeChild(child1, child2, ...);
|
|
*
|
|
* Returns true if the child (or children) was removed, or false if it was not in the display list.
|
|
* @method removeChild
|
|
* @param {DisplayObject} child The child to remove.
|
|
* @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list.
|
|
**/
|
|
p.removeChild = function (child) {
|
|
var l = arguments.length;
|
|
if (l > 1) {
|
|
var good = true;
|
|
for (var i = 0; i < l; i++) {
|
|
good = good && this.removeChild(arguments[i]);
|
|
}
|
|
return good;
|
|
}
|
|
return this.removeChildAt(createjs.indexOf(this.children, child));
|
|
};
|
|
|
|
/**
|
|
* Removes the child at the specified index from the display list, and sets its parent to null.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.removeChildAt(2);
|
|
*
|
|
* You can also remove multiple children:
|
|
*
|
|
* container.removeChild(2, 7, ...)
|
|
*
|
|
* Returns true if the child (or children) was removed, or false if any index was out of range.
|
|
* @method removeChildAt
|
|
* @param {Number} index The index of the child to remove.
|
|
* @return {Boolean} true if the child (or children) was removed, or false if any index was out of range.
|
|
**/
|
|
p.removeChildAt = function (index) {
|
|
var l = arguments.length;
|
|
if (l > 1) {
|
|
var a = [];
|
|
for (var i = 0; i < l; i++) {
|
|
a[i] = arguments[i];
|
|
}
|
|
a.sort(function (a, b) {
|
|
return b - a;
|
|
});
|
|
var good = true;
|
|
for (var i = 0; i < l; i++) {
|
|
good = good && this.removeChildAt(a[i]);
|
|
}
|
|
return good;
|
|
}
|
|
if (index < 0 || index > this.children.length - 1) {
|
|
return false;
|
|
}
|
|
var child = this.children[index];
|
|
if (child) {
|
|
child.parent = null;
|
|
}
|
|
this.children.splice(index, 1);
|
|
child.dispatchEvent("removed");
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Removes all children from the display list.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.removeAllChildren();
|
|
*
|
|
* @method removeAllChildren
|
|
**/
|
|
p.removeAllChildren = function () {
|
|
var kids = this.children;
|
|
while (kids.length) {
|
|
this.removeChildAt(0);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the child at the specified index.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.getChildAt(2);
|
|
*
|
|
* @method getChildAt
|
|
* @param {Number} index The index of the child to return.
|
|
* @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index.
|
|
**/
|
|
p.getChildAt = function (index) {
|
|
return this.children[index];
|
|
};
|
|
|
|
/**
|
|
* Returns the child with the specified name.
|
|
* @method getChildByName
|
|
* @param {String} name The name of the child to return.
|
|
* @return {DisplayObject} The child with the specified name.
|
|
**/
|
|
p.getChildByName = function (name) {
|
|
var kids = this.children;
|
|
for (var i = 0, l = kids.length; i < l; i++) {
|
|
if (kids[i].name == name) {
|
|
return kids[i];
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Performs an array sort operation on the child list.
|
|
*
|
|
* <h4>Example: Display children with a higher y in front.</h4>
|
|
*
|
|
* var sortFunction = function(obj1, obj2, options) {
|
|
* if (obj1.y > obj2.y) { return 1; }
|
|
* if (obj1.y < obj2.y) { return -1; }
|
|
* return 0;
|
|
* }
|
|
* container.sortChildren(sortFunction);
|
|
*
|
|
* @method sortChildren
|
|
* @param {Function} sortFunction the function to use to sort the child list. See JavaScript's <code>Array.sort</code>
|
|
* documentation for details.
|
|
**/
|
|
p.sortChildren = function (sortFunction) {
|
|
this.children.sort(sortFunction);
|
|
};
|
|
|
|
/**
|
|
* Returns the index of the specified child in the display list, or -1 if it is not in the display list.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var index = container.getChildIndex(child);
|
|
*
|
|
* @method getChildIndex
|
|
* @param {DisplayObject} child The child to return the index of.
|
|
* @return {Number} The index of the specified child. -1 if the child is not found.
|
|
**/
|
|
p.getChildIndex = function (child) {
|
|
return createjs.indexOf(this.children, child);
|
|
};
|
|
|
|
/**
|
|
* Swaps the children at the specified indexes. Fails silently if either index is out of range.
|
|
* @method swapChildrenAt
|
|
* @param {Number} index1
|
|
* @param {Number} index2
|
|
**/
|
|
p.swapChildrenAt = function (index1, index2) {
|
|
var kids = this.children;
|
|
var o1 = kids[index1];
|
|
var o2 = kids[index2];
|
|
if (!o1 || !o2) {
|
|
return;
|
|
}
|
|
kids[index1] = o2;
|
|
kids[index2] = o1;
|
|
};
|
|
|
|
/**
|
|
* Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this
|
|
* Container.
|
|
* @method swapChildren
|
|
* @param {DisplayObject} child1
|
|
* @param {DisplayObject} child2
|
|
**/
|
|
p.swapChildren = function (child1, child2) {
|
|
var kids = this.children;
|
|
var index1, index2;
|
|
for (var i = 0, l = kids.length; i < l; i++) {
|
|
if (kids[i] == child1) {
|
|
index1 = i;
|
|
}
|
|
if (kids[i] == child2) {
|
|
index2 = i;
|
|
}
|
|
if (index1 != null && index2 != null) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == l) {
|
|
return;
|
|
} // TODO: throw error?
|
|
kids[index1] = child2;
|
|
kids[index2] = child1;
|
|
};
|
|
|
|
/**
|
|
* Changes the depth of the specified child. Fails silently if the child is not a child of this container, or the index is out of range.
|
|
* @param {DisplayObject} child
|
|
* @param {Number} index
|
|
* @method setChildIndex
|
|
**/
|
|
p.setChildIndex = function (child, index) {
|
|
var kids = this.children,
|
|
l = kids.length;
|
|
if (child.parent != this || index < 0 || index >= l) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < l; i++) {
|
|
if (kids[i] == child) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == l || i == index) {
|
|
return;
|
|
}
|
|
kids.splice(i, 1);
|
|
kids.splice(index, 0, child);
|
|
};
|
|
|
|
/**
|
|
* Returns true if the specified display object either is this container or is a descendent (child, grandchild, etc)
|
|
* of this container.
|
|
* @method contains
|
|
* @param {DisplayObject} child The DisplayObject to be checked.
|
|
* @return {Boolean} true if the specified display object either is this container or is a descendent.
|
|
**/
|
|
p.contains = function (child) {
|
|
while (child) {
|
|
if (child == this) {
|
|
return true;
|
|
}
|
|
child = child.parent;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Tests whether the display object intersects the specified local point (ie. draws a pixel with alpha > 0 at the
|
|
* specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all
|
|
* transform properties including regX/Y.
|
|
* @method hitTest
|
|
* @param {Number} x The x position to check in the display object's local coordinates.
|
|
* @param {Number} y The y position to check in the display object's local coordinates.
|
|
* @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified
|
|
* coordinates.
|
|
**/
|
|
p.hitTest = function (x, y) {
|
|
// TODO: optimize to use the fast cache check where possible.
|
|
return this.getObjectUnderPoint(x, y) != null;
|
|
};
|
|
|
|
/**
|
|
* Returns an array of all display objects under the specified coordinates that are in this container's display
|
|
* list. This routine ignores any display objects with {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
|
|
* set to `false`. The array will be sorted in order of visual depth, with the top-most display object at index 0.
|
|
* This uses shape based hit detection, and can be an expensive operation to run, so it is best to use it carefully.
|
|
* For example, if testing for objects under the mouse, test on tick (instead of on {{#crossLink "DisplayObject/mousemove:event"}}{{/crossLink}}),
|
|
* and only if the mouse's position has changed.
|
|
*
|
|
* <ul>
|
|
* <li>By default (mode=0) this method evaluates all display objects.</li>
|
|
* <li>By setting the `mode` parameter to `1`, the {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
|
|
* and {{#crossLink "mouseChildren:property"}}{{/crossLink}} properties will be respected.</li>
|
|
* <li>Setting the `mode` to `2` additionally excludes display objects that do not have active mouse event
|
|
* listeners or a {{#crossLink "DisplayObject:cursor:property"}}{{/crossLink}} property. That is, only objects
|
|
* that would normally intercept mouse interaction will be included. This can significantly improve performance
|
|
* in some cases by reducing the number of display objects that need to be tested.</li>
|
|
* </li>
|
|
*
|
|
* This method accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}.
|
|
* @method getObjectsUnderPoint
|
|
* @param {Number} x The x position in the container to test.
|
|
* @param {Number} y The y position in the container to test.
|
|
* @param {Number} [mode=0] The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
|
|
* @return {Array} An Array of DisplayObjects under the specified coordinates.
|
|
**/
|
|
p.getObjectsUnderPoint = function (x, y, mode) {
|
|
var arr = [];
|
|
var pt = this.localToGlobal(x, y);
|
|
this._getObjectsUnderPoint(pt.x, pt.y, arr, mode > 0, mode == 1);
|
|
return arr;
|
|
};
|
|
|
|
/**
|
|
* Similar to {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}, but returns only the top-most display
|
|
* object. This runs significantly faster than <code>getObjectsUnderPoint()</code>, but is still potentially an expensive
|
|
* operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information.
|
|
* @method getObjectUnderPoint
|
|
* @param {Number} x The x position in the container to test.
|
|
* @param {Number} y The y position in the container to test.
|
|
* @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
|
|
* @return {DisplayObject} The top-most display object under the specified coordinates.
|
|
**/
|
|
p.getObjectUnderPoint = function (x, y, mode) {
|
|
var pt = this.localToGlobal(x, y);
|
|
return this._getObjectsUnderPoint(pt.x, pt.y, null, mode > 0, mode == 1);
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getBounds = function () {
|
|
return this._getBounds(null, true);
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getTransformedBounds = function () {
|
|
return this._getBounds();
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Container. Some properties that are specific to this instance's current context are
|
|
* reverted to their defaults (for example .parent).
|
|
* @method clone
|
|
* @param {Boolean} [recursive=false] If true, all of the descendants of this container will be cloned recursively. If false, the
|
|
* properties of the container will be cloned, but the new instance will not have any children.
|
|
* @return {Container} A clone of the current Container instance.
|
|
**/
|
|
p.clone = function (recursive) {
|
|
var o = this._cloneProps(new Container());
|
|
if (recursive) {
|
|
this._cloneChildren(o);
|
|
}
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Container (name=" + this.name + ")]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* @protected
|
|
**/
|
|
p._tick = function (evtObj) {
|
|
if (this.tickChildren) {
|
|
for (var i = this.children.length - 1; i >= 0; i--) {
|
|
var child = this.children[i];
|
|
if (child.tickEnabled && child._tick) {
|
|
child._tick(evtObj);
|
|
}
|
|
}
|
|
}
|
|
this.DisplayObject__tick(evtObj);
|
|
};
|
|
|
|
/**
|
|
* Recursively clones all children of this container, and adds them to the target container.
|
|
* @method cloneChildren
|
|
* @protected
|
|
* @param {Container} o The target container.
|
|
**/
|
|
p._cloneChildren = function (o) {
|
|
if (o.children.length) {
|
|
o.removeAllChildren();
|
|
}
|
|
var arr = o.children;
|
|
for (var i = 0, l = this.children.length; i < l; i++) {
|
|
var clone = this.children[i].clone(true);
|
|
clone.parent = o;
|
|
arr.push(clone);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _getObjectsUnderPoint
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Array} arr
|
|
* @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners.
|
|
* @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object.
|
|
* @param {Number} currentDepth Indicates the current depth of the search.
|
|
* @return {DisplayObject}
|
|
* @protected
|
|
**/
|
|
p._getObjectsUnderPoint = function (x, y, arr, mouse, activeListener, currentDepth) {
|
|
currentDepth = currentDepth || 0;
|
|
if (!currentDepth && !this._testMask(this, x, y)) {
|
|
return null;
|
|
}
|
|
var mtx,
|
|
ctx = createjs.DisplayObject._hitTestContext;
|
|
activeListener = activeListener || mouse && this._hasMouseEventListener();
|
|
|
|
// draw children one at a time, and check if we get a hit:
|
|
var children = this.children,
|
|
l = children.length;
|
|
for (var i = l - 1; i >= 0; i--) {
|
|
var child = children[i];
|
|
var hitArea = child.hitArea;
|
|
if (!child.visible || !hitArea && !child.isVisible() || mouse && !child.mouseEnabled) {
|
|
continue;
|
|
}
|
|
if (!hitArea && !this._testMask(child, x, y)) {
|
|
continue;
|
|
}
|
|
|
|
// if a child container has a hitArea then we only need to check its hitArea, so we can treat it as a normal DO:
|
|
if (!hitArea && child instanceof Container) {
|
|
var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth + 1);
|
|
if (!arr && result) {
|
|
return mouse && !this.mouseChildren ? this : result;
|
|
}
|
|
} else {
|
|
if (mouse && !activeListener && !child._hasMouseEventListener()) {
|
|
continue;
|
|
}
|
|
|
|
// TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense.
|
|
var props = child.getConcatenatedDisplayProps(child._props);
|
|
mtx = props.matrix;
|
|
|
|
if (hitArea) {
|
|
mtx.appendMatrix(hitArea.getMatrix(hitArea._props.matrix));
|
|
props.alpha = hitArea.alpha;
|
|
}
|
|
|
|
ctx.globalAlpha = props.alpha;
|
|
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx - x, mtx.ty - y);
|
|
(hitArea || child).draw(ctx);
|
|
if (!this._testHit(ctx)) {
|
|
continue;
|
|
}
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, 2, 2);
|
|
if (arr) {
|
|
arr.push(child);
|
|
} else {
|
|
return mouse && !this.mouseChildren ? this : child;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @method _testMask
|
|
* @param {DisplayObject} target
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Boolean} Indicates whether the x/y is within the masked region.
|
|
* @protected
|
|
**/
|
|
p._testMask = function (target, x, y) {
|
|
var mask = target.mask;
|
|
if (!mask || !mask.graphics || mask.graphics.isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
var mtx = this._props.matrix,
|
|
parent = target.parent;
|
|
mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity();
|
|
mtx = mask.getMatrix(mask._props.matrix).prependMatrix(mtx);
|
|
|
|
var ctx = createjs.DisplayObject._hitTestContext;
|
|
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx - x, mtx.ty - y);
|
|
|
|
// draw the mask as a solid fill:
|
|
mask.graphics.drawAsPath(ctx);
|
|
ctx.fillStyle = "#000";
|
|
ctx.fill();
|
|
|
|
if (!this._testHit(ctx)) {
|
|
return false;
|
|
}
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, 2, 2);
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @method _getBounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform If true, does not apply this object's transform.
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._getBounds = function (matrix, ignoreTransform) {
|
|
var bounds = this.DisplayObject_getBounds();
|
|
if (bounds) {
|
|
return this._transformBounds(bounds, matrix, ignoreTransform);
|
|
}
|
|
|
|
var mtx = this._props.matrix;
|
|
mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
|
|
if (matrix) {
|
|
mtx.prependMatrix(matrix);
|
|
}
|
|
|
|
var l = this.children.length,
|
|
rect = null;
|
|
for (var i = 0; i < l; i++) {
|
|
var child = this.children[i];
|
|
if (!child.visible || !(bounds = child._getBounds(mtx))) {
|
|
continue;
|
|
}
|
|
if (rect) {
|
|
rect.extend(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
} else {
|
|
rect = bounds.clone();
|
|
}
|
|
}
|
|
return rect;
|
|
};
|
|
|
|
createjs.Container = createjs.promote(Container, "DisplayObject");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Stage.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* A stage is the root level {{#crossLink "Container"}}{{/crossLink}} for a display list. Each time its {{#crossLink "Stage/tick"}}{{/crossLink}}
|
|
* method is called, it will render its display list to its target canvas.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child
|
|
* and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}.
|
|
*
|
|
* var stage = new createjs.Stage("canvasElementId");
|
|
* var image = new createjs.Bitmap("imagePath.png");
|
|
* stage.addChild(image);
|
|
* createjs.Ticker.addEventListener("tick", handleTick);
|
|
* function handleTick(event) {
|
|
* image.x += 10;
|
|
* stage.update();
|
|
* }
|
|
*
|
|
* @class Stage
|
|
* @extends Container
|
|
* @constructor
|
|
* @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id
|
|
* of a canvas object in the current document.
|
|
**/
|
|
|
|
function Stage(canvas) {
|
|
this.Container_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* Indicates whether the stage should automatically clear the canvas before each render. You can set this to <code>false</code>
|
|
* to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for
|
|
* example).
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var stage = new createjs.Stage("canvasId");
|
|
* stage.autoClear = false;
|
|
*
|
|
* @property autoClear
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.autoClear = true;
|
|
|
|
/**
|
|
* The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the
|
|
* first stage that will be ticked (or they will clear each other's render).
|
|
*
|
|
* When changing the canvas property you must disable the events on the old canvas, and enable events on the
|
|
* new canvas or mouse events will not work as expected. For example:
|
|
*
|
|
* myStage.enableDOMEvents(false);
|
|
* myStage.canvas = anotherCanvas;
|
|
* myStage.enableDOMEvents(true);
|
|
*
|
|
* @property canvas
|
|
* @type HTMLCanvasElement | Object
|
|
**/
|
|
this.canvas = typeof canvas == "string" ? document.getElementById(canvas) : canvas;
|
|
|
|
/**
|
|
* The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
|
|
* position over the canvas, and mouseInBounds will be set to false.
|
|
* @property mouseX
|
|
* @type Number
|
|
* @readonly
|
|
**/
|
|
this.mouseX = 0;
|
|
|
|
/**
|
|
* The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
|
|
* position over the canvas, and mouseInBounds will be set to false.
|
|
* @property mouseY
|
|
* @type Number
|
|
* @readonly
|
|
**/
|
|
this.mouseY = 0;
|
|
|
|
/**
|
|
* Specifies the area of the stage to affect when calling update. This can be use to selectively
|
|
* re-draw specific regions of the canvas. If null, the whole canvas area is drawn.
|
|
* @property drawRect
|
|
* @type {Rectangle}
|
|
*/
|
|
this.drawRect = null;
|
|
|
|
/**
|
|
* Indicates whether display objects should be rendered on whole pixels. You can set the
|
|
* {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of
|
|
* display objects to false to enable/disable this behaviour on a per instance basis.
|
|
* @property snapToPixelEnabled
|
|
* @type Boolean
|
|
* @default false
|
|
**/
|
|
this.snapToPixelEnabled = false;
|
|
|
|
/**
|
|
* Indicates whether the mouse is currently within the bounds of the canvas.
|
|
* @property mouseInBounds
|
|
* @type Boolean
|
|
* @default false
|
|
**/
|
|
this.mouseInBounds = false;
|
|
|
|
/**
|
|
* If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas.
|
|
* @property tickOnUpdate
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.tickOnUpdate = true;
|
|
|
|
/**
|
|
* If true, mouse move events will continue to be called when the mouse leaves the target canvas. See
|
|
* {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* x/y/rawX/rawY.
|
|
* @property mouseMoveOutside
|
|
* @type Boolean
|
|
* @default false
|
|
**/
|
|
this.mouseMoveOutside = false;
|
|
|
|
/**
|
|
* Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas.
|
|
* This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas.
|
|
* @property preventSelection
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.preventSelection = true;
|
|
|
|
/**
|
|
* The hitArea property is not supported for Stage.
|
|
* @property hitArea
|
|
* @type {DisplayObject}
|
|
* @default null
|
|
*/
|
|
|
|
// private properties:
|
|
/**
|
|
* Holds objects with data for each active pointer id. Each object has the following properties:
|
|
* x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position)
|
|
* @property _pointerData
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._pointerData = {};
|
|
|
|
/**
|
|
* Number of active pointers.
|
|
* @property _pointerCount
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._pointerCount = 0;
|
|
|
|
/**
|
|
* The ID of the primary pointer.
|
|
* @property _primaryPointerID
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._primaryPointerID = null;
|
|
|
|
/**
|
|
* @property _mouseOverIntervalID
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._mouseOverIntervalID = null;
|
|
|
|
/**
|
|
* @property _nextStage
|
|
* @protected
|
|
* @type Stage
|
|
**/
|
|
this._nextStage = null;
|
|
|
|
/**
|
|
* @property _prevStage
|
|
* @protected
|
|
* @type Stage
|
|
**/
|
|
this._prevStage = null;
|
|
|
|
// initialize:
|
|
this.enableDOMEvents(true);
|
|
}
|
|
var p = createjs.extend(Stage, createjs.Container);
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when the user moves the mouse over the canvas.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event stagemousemove
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* class for a listing of event properties.
|
|
* @event stagemousedown
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user the user presses somewhere on the stage, then releases the mouse button anywhere that the page can detect it (this varies slightly between browsers).
|
|
* You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event stagemouseup
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false).
|
|
* This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* class for a listing of event properties.
|
|
* @event mouseleave
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true).
|
|
* This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* class for a listing of event properties.
|
|
* @event mouseenter
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately before the tick event is propagated through the display list.
|
|
* You can call preventDefault on the event object to cancel propagating the tick event.
|
|
* @event tickstart
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if
|
|
* tickOnUpdate is false. Precedes the "drawstart" event.
|
|
* @event tickend
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately before the canvas is cleared and the display list is drawn to it.
|
|
* You can call preventDefault on the event object to cancel the draw.
|
|
* @event drawstart
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
|
|
* @event drawend
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them.
|
|
* This can be useful in cases where you have multiple layered canvases and want user interactions
|
|
* events to pass through. For example, this would relay mouse events from topStage to bottomStage:
|
|
*
|
|
* topStage.nextStage = bottomStage;
|
|
*
|
|
* To disable relaying, set nextStage to null.
|
|
*
|
|
* MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings
|
|
* of the top-most stage, but are only processed if the target stage has mouse over interactions enabled.
|
|
* Considerations when using roll over in relay targets:<OL>
|
|
* <LI> The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)</LI>
|
|
* <LI> All stages that wish to participate in mouse over interaction must enable them via enableMouseOver</LI>
|
|
* <LI> All relay targets will share the frequency value of the top-most stage</LI>
|
|
* </OL>
|
|
* To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing
|
|
* 30 as it's desired frequency):
|
|
* topStage.nextStage = targetStage;
|
|
* topStage.enableMouseOver(10);
|
|
* targetStage.enableMouseOver(30);
|
|
*
|
|
* If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its
|
|
* DOM events using:
|
|
*
|
|
* targetStage.enableDOMEvents(false);
|
|
*
|
|
* @property nextStage
|
|
* @type {Stage}
|
|
**/
|
|
p._get_nextStage = function () {
|
|
return this._nextStage;
|
|
};
|
|
p._set_nextStage = function (value) {
|
|
if (this._nextStage) {
|
|
this._nextStage._prevStage = null;
|
|
}
|
|
if (value) {
|
|
value._prevStage = this;
|
|
}
|
|
this._nextStage = value;
|
|
};
|
|
|
|
try {
|
|
Object.defineProperties(p, {
|
|
nextStage: { get: p._get_nextStage, set: p._set_nextStage }
|
|
});
|
|
} catch (e) {} // TODO: use Log
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}}
|
|
* unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false,
|
|
* and then render the display list to the canvas.
|
|
*
|
|
* @method update
|
|
* @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property.
|
|
**/
|
|
p.update = function (props) {
|
|
if (!this.canvas) {
|
|
return;
|
|
}
|
|
if (this.tickOnUpdate) {
|
|
this.tick(props);
|
|
}
|
|
if (this.dispatchEvent("drawstart", false, true) === false) {
|
|
return;
|
|
}
|
|
createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
|
|
var r = this.drawRect,
|
|
ctx = this.canvas.getContext("2d");
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
if (this.autoClear) {
|
|
if (r) {
|
|
ctx.clearRect(r.x, r.y, r.width, r.height);
|
|
} else {
|
|
ctx.clearRect(0, 0, this.canvas.width + 1, this.canvas.height + 1);
|
|
}
|
|
}
|
|
ctx.save();
|
|
if (this.drawRect) {
|
|
ctx.beginPath();
|
|
ctx.rect(r.x, r.y, r.width, r.height);
|
|
ctx.clip();
|
|
}
|
|
this.updateContext(ctx);
|
|
this.draw(ctx, false);
|
|
ctx.restore();
|
|
this.dispatchEvent("drawend");
|
|
};
|
|
|
|
/**
|
|
* Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
|
|
* unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
|
|
*
|
|
* If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is
|
|
* propagated to listeners.
|
|
*
|
|
* Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
|
|
* a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be
|
|
* passed as the `props` parameter to `tick()`. For example:
|
|
*
|
|
* Ticker.on("tick", handleTick);
|
|
* function handleTick(evtObj) {
|
|
* // clone the event object from Ticker, and add some custom data to it:
|
|
* var evt = evtObj.clone().set({greeting:"hello", name:"world"});
|
|
*
|
|
* // pass it to stage.update():
|
|
* myStage.update(evt); // subsequently calls tick() with the same param
|
|
* }
|
|
*
|
|
* // ...
|
|
* myDisplayObject.on("tick", handleDisplayObjectTick);
|
|
* function handleDisplayObjectTick(evt) {
|
|
* console.log(evt.delta); // the delta property from the Ticker tick event object
|
|
* console.log(evt.greeting, evt.name); // custom data: "hello world"
|
|
* }
|
|
*
|
|
* @method tick
|
|
* @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property.
|
|
**/
|
|
p.tick = function (props) {
|
|
if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) {
|
|
return;
|
|
}
|
|
var evtObj = new createjs.Event("tick");
|
|
if (props) {
|
|
for (var n in props) {
|
|
if (props.hasOwnProperty(n)) {
|
|
evtObj[n] = props[n];
|
|
}
|
|
}
|
|
}
|
|
this._tick(evtObj);
|
|
this.dispatchEvent("tickend");
|
|
};
|
|
|
|
/**
|
|
* Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}}
|
|
* event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}}
|
|
* directly, using:
|
|
*
|
|
* Ticker.addEventListener("tick", myStage");
|
|
*
|
|
* Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to
|
|
* display object tick handlers, instead of <code>delta</code> and <code>paused</code> parameters.
|
|
* @property handleEvent
|
|
* @type Function
|
|
**/
|
|
p.handleEvent = function (evt) {
|
|
if (evt.type == "tick") {
|
|
this.update(evt);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`.
|
|
* @method clear
|
|
**/
|
|
p.clear = function () {
|
|
if (!this.canvas) {
|
|
return;
|
|
}
|
|
var ctx = this.canvas.getContext("2d");
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, this.canvas.width + 1, this.canvas.height + 1);
|
|
};
|
|
|
|
/**
|
|
* Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can
|
|
* be specified as the src value of an image element.
|
|
* @method toDataURL
|
|
* @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color
|
|
* value is allowed. The default value is a transparent background.
|
|
* @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type
|
|
* is passed in, or if the browser does not support the specified MIME type, the default value will be used.
|
|
* @return {String} a Base64 encoded image.
|
|
**/
|
|
p.toDataURL = function (backgroundColor, mimeType) {
|
|
var data,
|
|
ctx = this.canvas.getContext('2d'),
|
|
w = this.canvas.width,
|
|
h = this.canvas.height;
|
|
|
|
if (backgroundColor) {
|
|
data = ctx.getImageData(0, 0, w, h);
|
|
var compositeOperation = ctx.globalCompositeOperation;
|
|
ctx.globalCompositeOperation = "destination-over";
|
|
|
|
ctx.fillStyle = backgroundColor;
|
|
ctx.fillRect(0, 0, w, h);
|
|
}
|
|
|
|
var dataURL = this.canvas.toDataURL(mimeType || "image/png");
|
|
|
|
if (backgroundColor) {
|
|
ctx.putImageData(data, 0, 0);
|
|
ctx.globalCompositeOperation = compositeOperation;
|
|
}
|
|
|
|
return dataURL;
|
|
};
|
|
|
|
/**
|
|
* Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}
|
|
* and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}
|
|
* and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can
|
|
* be expensive to generate, so they are disabled by default. The frequency of the events can be controlled
|
|
* independently of mouse move events via the optional `frequency` parameter.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var stage = new createjs.Stage("canvasId");
|
|
* stage.enableMouseOver(10); // 10 updates per second
|
|
*
|
|
* @method enableMouseOver
|
|
* @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast
|
|
* mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less
|
|
* responsive, but uses less CPU.
|
|
**/
|
|
p.enableMouseOver = function (frequency) {
|
|
if (this._mouseOverIntervalID) {
|
|
clearInterval(this._mouseOverIntervalID);
|
|
this._mouseOverIntervalID = null;
|
|
if (frequency == 0) {
|
|
this._testMouseOver(true);
|
|
}
|
|
}
|
|
if (frequency == null) {
|
|
frequency = 20;
|
|
} else if (frequency <= 0) {
|
|
return;
|
|
}
|
|
var o = this;
|
|
this._mouseOverIntervalID = setInterval(function () {
|
|
o._testMouseOver();
|
|
}, 1000 / Math.min(50, frequency));
|
|
};
|
|
|
|
/**
|
|
* Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good
|
|
* practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive
|
|
* events from the page.
|
|
*
|
|
* When changing the canvas property you must disable the events on the old canvas, and enable events on the
|
|
* new canvas or mouse events will not work as expected. For example:
|
|
*
|
|
* myStage.enableDOMEvents(false);
|
|
* myStage.canvas = anotherCanvas;
|
|
* myStage.enableDOMEvents(true);
|
|
*
|
|
* @method enableDOMEvents
|
|
* @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true.
|
|
**/
|
|
p.enableDOMEvents = function (enable) {
|
|
if (enable == null) {
|
|
enable = true;
|
|
}
|
|
var n,
|
|
o,
|
|
ls = this._eventListeners;
|
|
if (!enable && ls) {
|
|
for (n in ls) {
|
|
o = ls[n];
|
|
o.t.removeEventListener(n, o.f, false);
|
|
}
|
|
this._eventListeners = null;
|
|
} else if (enable && !ls && this.canvas) {
|
|
var t = window.addEventListener ? window : document;
|
|
var _this = this;
|
|
ls = this._eventListeners = {};
|
|
ls["mouseup"] = { t: t, f: function f(e) {
|
|
_this._handleMouseUp(e);
|
|
} };
|
|
ls["mousemove"] = { t: t, f: function f(e) {
|
|
_this._handleMouseMove(e);
|
|
} };
|
|
ls["dblclick"] = { t: this.canvas, f: function f(e) {
|
|
_this._handleDoubleClick(e);
|
|
} };
|
|
ls["mousedown"] = { t: this.canvas, f: function f(e) {
|
|
_this._handleMouseDown(e);
|
|
} };
|
|
|
|
for (n in ls) {
|
|
o = ls[n];
|
|
o.t.addEventListener(n, o.f, false);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stage instances cannot be cloned.
|
|
* @method clone
|
|
**/
|
|
p.clone = function () {
|
|
throw "Stage cannot be cloned.";
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Stage (name=" + this.name + ")]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _getElementRect
|
|
* @protected
|
|
* @param {HTMLElement} e
|
|
**/
|
|
p._getElementRect = function (e) {
|
|
var bounds;
|
|
try {
|
|
bounds = e.getBoundingClientRect();
|
|
} // this can fail on disconnected DOM elements in IE9
|
|
catch (err) {
|
|
bounds = { top: e.offsetTop, left: e.offsetLeft, width: e.offsetWidth, height: e.offsetHeight };
|
|
}
|
|
|
|
var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0);
|
|
var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || document.body.clientTop || 0);
|
|
|
|
var styles = window.getComputedStyle ? getComputedStyle(e, null) : e.currentStyle; // IE <9 compatibility.
|
|
var padL = parseInt(styles.paddingLeft) + parseInt(styles.borderLeftWidth);
|
|
var padT = parseInt(styles.paddingTop) + parseInt(styles.borderTopWidth);
|
|
var padR = parseInt(styles.paddingRight) + parseInt(styles.borderRightWidth);
|
|
var padB = parseInt(styles.paddingBottom) + parseInt(styles.borderBottomWidth);
|
|
|
|
// note: in some browsers bounds properties are read only.
|
|
return {
|
|
left: bounds.left + offX + padL,
|
|
right: bounds.right + offX - padR,
|
|
top: bounds.top + offY + padT,
|
|
bottom: bounds.bottom + offY - padB
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @method _getPointerData
|
|
* @protected
|
|
* @param {Number} id
|
|
**/
|
|
p._getPointerData = function (id) {
|
|
var data = this._pointerData[id];
|
|
if (!data) {
|
|
data = this._pointerData[id] = { x: 0, y: 0 };
|
|
}
|
|
return data;
|
|
};
|
|
|
|
/**
|
|
* @method _handleMouseMove
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
**/
|
|
p._handleMouseMove = function (e) {
|
|
if (!e) {
|
|
e = window.event;
|
|
}
|
|
this._handlePointerMove(-1, e, e.pageX, e.pageY);
|
|
};
|
|
|
|
/**
|
|
* @method _handlePointerMove
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Number} pageX
|
|
* @param {Number} pageY
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handlePointerMove = function (id, e, pageX, pageY, owner) {
|
|
if (this._prevStage && owner === undefined) {
|
|
return;
|
|
} // redundant listener.
|
|
if (!this.canvas) {
|
|
return;
|
|
}
|
|
var nextStage = this._nextStage,
|
|
o = this._getPointerData(id);
|
|
|
|
var inBounds = o.inBounds;
|
|
this._updatePointerPosition(id, e, pageX, pageY);
|
|
if (inBounds || o.inBounds || this.mouseMoveOutside) {
|
|
if (id === -1 && o.inBounds == !inBounds) {
|
|
this._dispatchMouseEvent(this, inBounds ? "mouseleave" : "mouseenter", false, id, o, e);
|
|
}
|
|
|
|
this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e);
|
|
this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e);
|
|
}
|
|
|
|
nextStage && nextStage._handlePointerMove(id, e, pageX, pageY, null);
|
|
};
|
|
|
|
/**
|
|
* @method _updatePointerPosition
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Number} pageX
|
|
* @param {Number} pageY
|
|
**/
|
|
p._updatePointerPosition = function (id, e, pageX, pageY) {
|
|
var rect = this._getElementRect(this.canvas);
|
|
pageX -= rect.left;
|
|
pageY -= rect.top;
|
|
|
|
var w = this.canvas.width;
|
|
var h = this.canvas.height;
|
|
pageX /= (rect.right - rect.left) / w;
|
|
pageY /= (rect.bottom - rect.top) / h;
|
|
var o = this._getPointerData(id);
|
|
if (o.inBounds = pageX >= 0 && pageY >= 0 && pageX <= w - 1 && pageY <= h - 1) {
|
|
o.x = pageX;
|
|
o.y = pageY;
|
|
} else if (this.mouseMoveOutside) {
|
|
o.x = pageX < 0 ? 0 : pageX > w - 1 ? w - 1 : pageX;
|
|
o.y = pageY < 0 ? 0 : pageY > h - 1 ? h - 1 : pageY;
|
|
}
|
|
|
|
o.posEvtObj = e;
|
|
o.rawX = pageX;
|
|
o.rawY = pageY;
|
|
|
|
if (id === this._primaryPointerID || id === -1) {
|
|
this.mouseX = o.x;
|
|
this.mouseY = o.y;
|
|
this.mouseInBounds = o.inBounds;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleMouseUp
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
**/
|
|
p._handleMouseUp = function (e) {
|
|
this._handlePointerUp(-1, e, false);
|
|
};
|
|
|
|
/**
|
|
* @method _handlePointerUp
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Boolean} clear
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handlePointerUp = function (id, e, clear, owner) {
|
|
var nextStage = this._nextStage,
|
|
o = this._getPointerData(id);
|
|
if (this._prevStage && owner === undefined) {
|
|
return;
|
|
} // redundant listener.
|
|
|
|
var target = null,
|
|
oTarget = o.target;
|
|
if (!owner && (oTarget || nextStage)) {
|
|
target = this._getObjectsUnderPoint(o.x, o.y, null, true);
|
|
}
|
|
|
|
if (o.down) {
|
|
this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target);o.down = false;
|
|
}
|
|
|
|
if (target == oTarget) {
|
|
this._dispatchMouseEvent(oTarget, "click", true, id, o, e);
|
|
}
|
|
this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e);
|
|
|
|
if (clear) {
|
|
if (id == this._primaryPointerID) {
|
|
this._primaryPointerID = null;
|
|
}
|
|
delete this._pointerData[id];
|
|
} else {
|
|
o.target = null;
|
|
}
|
|
|
|
nextStage && nextStage._handlePointerUp(id, e, clear, owner || target && this);
|
|
};
|
|
|
|
/**
|
|
* @method _handleMouseDown
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
**/
|
|
p._handleMouseDown = function (e) {
|
|
this._handlePointerDown(-1, e, e.pageX, e.pageY);
|
|
};
|
|
|
|
/**
|
|
* @method _handlePointerDown
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Number} pageX
|
|
* @param {Number} pageY
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handlePointerDown = function (id, e, pageX, pageY, owner) {
|
|
if (this.preventSelection) {
|
|
e.preventDefault();
|
|
}
|
|
if (this._primaryPointerID == null || id === -1) {
|
|
this._primaryPointerID = id;
|
|
} // mouse always takes over.
|
|
|
|
if (pageY != null) {
|
|
this._updatePointerPosition(id, e, pageX, pageY);
|
|
}
|
|
var target = null,
|
|
nextStage = this._nextStage,
|
|
o = this._getPointerData(id);
|
|
if (!owner) {
|
|
target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true);
|
|
}
|
|
|
|
if (o.inBounds) {
|
|
this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target);o.down = true;
|
|
}
|
|
this._dispatchMouseEvent(target, "mousedown", true, id, o, e);
|
|
|
|
nextStage && nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this);
|
|
};
|
|
|
|
/**
|
|
* @method _testMouseOver
|
|
* @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target)
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
* @param {Stage} eventTarget The stage that the cursor is actively over.
|
|
* @protected
|
|
**/
|
|
p._testMouseOver = function (clear, owner, eventTarget) {
|
|
if (this._prevStage && owner === undefined) {
|
|
return;
|
|
} // redundant listener.
|
|
|
|
var nextStage = this._nextStage;
|
|
if (!this._mouseOverIntervalID) {
|
|
// not enabled for mouseover, but should still relay the event.
|
|
nextStage && nextStage._testMouseOver(clear, owner, eventTarget);
|
|
return;
|
|
}
|
|
var o = this._getPointerData(-1);
|
|
// only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs.
|
|
if (!o || !clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds) {
|
|
return;
|
|
}
|
|
|
|
var e = o.posEvtObj;
|
|
var isEventTarget = eventTarget || e && e.target == this.canvas;
|
|
var target = null,
|
|
common = -1,
|
|
cursor = "",
|
|
t,
|
|
i,
|
|
l;
|
|
|
|
if (!owner && (clear || this.mouseInBounds && isEventTarget)) {
|
|
target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true);
|
|
this._mouseOverX = this.mouseX;
|
|
this._mouseOverY = this.mouseY;
|
|
}
|
|
|
|
var oldList = this._mouseOverTarget || [];
|
|
var oldTarget = oldList[oldList.length - 1];
|
|
var list = this._mouseOverTarget = [];
|
|
|
|
// generate ancestor list and check for cursor:
|
|
t = target;
|
|
while (t) {
|
|
list.unshift(t);
|
|
if (!cursor) {
|
|
cursor = t.cursor;
|
|
}
|
|
t = t.parent;
|
|
}
|
|
this.canvas.style.cursor = cursor;
|
|
if (!owner && eventTarget) {
|
|
eventTarget.canvas.style.cursor = cursor;
|
|
}
|
|
|
|
// find common ancestor:
|
|
for (i = 0, l = list.length; i < l; i++) {
|
|
if (list[i] != oldList[i]) {
|
|
break;
|
|
}
|
|
common = i;
|
|
}
|
|
|
|
if (oldTarget != target) {
|
|
this._dispatchMouseEvent(oldTarget, "mouseout", true, -1, o, e, target);
|
|
}
|
|
|
|
for (i = oldList.length - 1; i > common; i--) {
|
|
this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e, target);
|
|
}
|
|
|
|
for (i = list.length - 1; i > common; i--) {
|
|
this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e, oldTarget);
|
|
}
|
|
|
|
if (oldTarget != target) {
|
|
this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget);
|
|
}
|
|
|
|
nextStage && nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this);
|
|
};
|
|
|
|
/**
|
|
* @method _handleDoubleClick
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handleDoubleClick = function (e, owner) {
|
|
var target = null,
|
|
nextStage = this._nextStage,
|
|
o = this._getPointerData(-1);
|
|
if (!owner) {
|
|
target = this._getObjectsUnderPoint(o.x, o.y, null, true);
|
|
this._dispatchMouseEvent(target, "dblclick", true, -1, o, e);
|
|
}
|
|
nextStage && nextStage._handleDoubleClick(e, owner || target && this);
|
|
};
|
|
|
|
/**
|
|
* @method _dispatchMouseEvent
|
|
* @protected
|
|
* @param {DisplayObject} target
|
|
* @param {String} type
|
|
* @param {Boolean} bubbles
|
|
* @param {Number} pointerId
|
|
* @param {Object} o
|
|
* @param {MouseEvent} [nativeEvent]
|
|
* @param {DisplayObject} [relatedTarget]
|
|
**/
|
|
p._dispatchMouseEvent = function (target, type, bubbles, pointerId, o, nativeEvent, relatedTarget) {
|
|
// TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC.
|
|
if (!target || !bubbles && !target.hasEventListener(type)) {
|
|
return;
|
|
}
|
|
/*
|
|
// TODO: account for stage transformations?
|
|
this._mtx = this.getConcatenatedMatrix(this._mtx).invert();
|
|
var pt = this._mtx.transformPoint(o.x, o.y);
|
|
var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID || pointerId==-1, o.rawX, o.rawY);
|
|
*/
|
|
var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId === this._primaryPointerID || pointerId === -1, o.rawX, o.rawY, relatedTarget);
|
|
target.dispatchEvent(evt);
|
|
};
|
|
|
|
createjs.Stage = createjs.promote(Stage, "Container");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Bitmap.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
|
|
/**
|
|
* A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
|
|
* HTML element, or a string.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var bitmap = new createjs.Bitmap("imagePath.jpg");
|
|
*
|
|
* <strong>Notes:</strong>
|
|
* <ol>
|
|
* <li>When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
|
|
* will be displayed.</li>
|
|
* <li>Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
|
|
* the Bitmap can be cached.</li>
|
|
* <li>Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
|
|
* happens in all browsers except recent Firefox builds.</li>
|
|
* <li>Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using
|
|
* methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting
|
|
* `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`</li>
|
|
* </ol>
|
|
*
|
|
* @class Bitmap
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
|
|
* display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
|
|
* If it is a URI, a new Image object will be constructed and assigned to the .image property.
|
|
**/
|
|
function Bitmap(imageOrUri) {
|
|
this.DisplayObject_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The image to render. This can be an Image, a Canvas, or a Video. Not all browsers (especially
|
|
* mobile browsers) support drawing video to a canvas.
|
|
* @property image
|
|
* @type HTMLImageElement | HTMLCanvasElement | HTMLVideoElement
|
|
**/
|
|
if (typeof imageOrUri == "string") {
|
|
this.image = document.createElement("img");
|
|
this.image.src = imageOrUri;
|
|
} else {
|
|
this.image = imageOrUri;
|
|
}
|
|
|
|
/**
|
|
* Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
|
|
* Note that video sources must have a width / height set to work correctly with `sourceRect`.
|
|
* @property sourceRect
|
|
* @type Rectangle
|
|
* @default null
|
|
*/
|
|
this.sourceRect = null;
|
|
}
|
|
var p = createjs.extend(Bitmap, createjs.DisplayObject);
|
|
|
|
// public methods:
|
|
/**
|
|
* Constructor alias for backwards compatibility. This method will be removed in future versions.
|
|
* Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
|
|
* @method initialize
|
|
* @deprecated in favour of `createjs.promote()`
|
|
**/
|
|
p.initialize = Bitmap; // TODO: deprecated.
|
|
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
var image = this.image;
|
|
var hasContent = this.cacheCanvas || image && (image.naturalWidth || image.getContext || image.readyState >= 2);
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
* @return {Boolean}
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache) || !this.image) {
|
|
return true;
|
|
}
|
|
var img = this.image,
|
|
rect = this.sourceRect;
|
|
if (rect) {
|
|
// some browsers choke on out of bound values, so we'll fix them:
|
|
var x1 = rect.x,
|
|
y1 = rect.y,
|
|
x2 = x1 + rect.width,
|
|
y2 = y1 + rect.height,
|
|
x = 0,
|
|
y = 0,
|
|
w = img.width,
|
|
h = img.height;
|
|
if (x1 < 0) {
|
|
x -= x1;x1 = 0;
|
|
}
|
|
if (x2 > w) {
|
|
x2 = w;
|
|
}
|
|
if (y1 < 0) {
|
|
y -= y1;y1 = 0;
|
|
}
|
|
if (y2 > h) {
|
|
y2 = h;
|
|
}
|
|
ctx.drawImage(img, x1, y1, x2 - x1, y2 - y1, x, y, x2 - x1, y2 - y1);
|
|
} else {
|
|
ctx.drawImage(img, 0, 0);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
//Note, the doc sections below document using the specified APIs (from DisplayObject) from
|
|
//Bitmap. This is why they have no method implementations.
|
|
|
|
/**
|
|
* Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
|
|
* You should <b>not</b> cache Bitmap instances as it can degrade performance.
|
|
*
|
|
* <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
|
|
* To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
|
|
* method.
|
|
* @method cache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
|
|
* You should <b>not</b> cache Bitmap instances as it can degrade performance.
|
|
*
|
|
* <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
|
|
* To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
|
|
* method.
|
|
* @method updateCache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
|
|
* You should <b>not</b> cache Bitmap instances as it can degrade performance.
|
|
*
|
|
* <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
|
|
* To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
|
|
* method.
|
|
* @method uncache
|
|
**/
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getBounds = function () {
|
|
var rect = this.DisplayObject_getBounds();
|
|
if (rect) {
|
|
return rect;
|
|
}
|
|
var image = this.image,
|
|
o = this.sourceRect || image;
|
|
var hasContent = image && (image.naturalWidth || image.getContext || image.readyState >= 2);
|
|
return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Bitmap instance.
|
|
* @method clone
|
|
* @return {Bitmap} a clone of the Bitmap instance.
|
|
**/
|
|
p.clone = function () {
|
|
var o = new Bitmap(this.image);
|
|
if (this.sourceRect) {
|
|
o.sourceRect = this.sourceRect.clone();
|
|
}
|
|
this._cloneProps(o);
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Bitmap (name=" + this.name + ")]";
|
|
};
|
|
|
|
createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Sprite.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of
|
|
* images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100
|
|
* images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames,
|
|
* play frames as an animation, and even sequence animations together.
|
|
*
|
|
* See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var instance = new createjs.Sprite(spriteSheet);
|
|
* instance.gotoAndStop("frameName");
|
|
*
|
|
* Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called,
|
|
* only the first defined frame defined in the sprite sheet will be displayed.
|
|
*
|
|
* @class Sprite
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame
|
|
* dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
|
|
* @param {String|Number} [frameOrAnimation] The frame number or animation to play initially.
|
|
**/
|
|
|
|
function Sprite(spriteSheet, frameOrAnimation) {
|
|
this.DisplayObject_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}}
|
|
* definitions, this will advance non-sequentially. This will always be an integer value.
|
|
* @property currentFrame
|
|
* @type {Number}
|
|
* @default 0
|
|
* @readonly
|
|
**/
|
|
this.currentFrame = 0;
|
|
|
|
/**
|
|
* Returns the name of the currently playing animation.
|
|
* @property currentAnimation
|
|
* @type {String}
|
|
* @final
|
|
* @readonly
|
|
**/
|
|
this.currentAnimation = null;
|
|
|
|
/**
|
|
* Prevents the animation from advancing each tick automatically. For example, you could create a sprite
|
|
* sheet of icons, set paused to true, and display the appropriate icon by setting <code>currentFrame</code>.
|
|
* @property paused
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
this.paused = true;
|
|
|
|
/**
|
|
* The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame
|
|
* data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
|
|
* @property spriteSheet
|
|
* @type {SpriteSheet}
|
|
* @readonly
|
|
**/
|
|
this.spriteSheet = spriteSheet;
|
|
|
|
/**
|
|
* Specifies the current frame index within the currently playing animation. When playing normally, this will increase
|
|
* from 0 to n-1, where n is the number of frames in the current animation.
|
|
*
|
|
* This could be a non-integer value if
|
|
* using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is
|
|
* not an integer.
|
|
* @property currentAnimationFrame
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.currentAnimationFrame = 0;
|
|
|
|
/**
|
|
* By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related
|
|
* SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
|
|
* framerate.
|
|
*
|
|
* For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will
|
|
* advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
|
|
* vary slightly between frames.
|
|
*
|
|
* This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
|
|
* passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
|
|
* @property framerate
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.framerate = 0;
|
|
|
|
// private properties:
|
|
/**
|
|
* Current animation object.
|
|
* @property _animation
|
|
* @protected
|
|
* @type {Object}
|
|
* @default null
|
|
**/
|
|
this._animation = null;
|
|
|
|
/**
|
|
* Current frame index.
|
|
* @property _currentFrame
|
|
* @protected
|
|
* @type {Number}
|
|
* @default null
|
|
**/
|
|
this._currentFrame = null;
|
|
|
|
/**
|
|
* Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame
|
|
* @property _skipAdvance
|
|
* @protected
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
this._skipAdvance = false;
|
|
|
|
if (frameOrAnimation != null) {
|
|
this.gotoAndPlay(frameOrAnimation);
|
|
}
|
|
}
|
|
var p = createjs.extend(Sprite, createjs.DisplayObject);
|
|
|
|
/**
|
|
* Constructor alias for backwards compatibility. This method will be removed in future versions.
|
|
* Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
|
|
* @method initialize
|
|
* @deprecated in favour of `createjs.promote()`
|
|
**/
|
|
p.initialize = Sprite; // TODO: Deprecated. This is for backwards support of FlashCC spritesheet export.
|
|
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when an animation reaches its ends.
|
|
* @event animationend
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {String} name The name of the animation that just ended.
|
|
* @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick,
|
|
* or calling gotoAndPlay() or gotoAndStop().
|
|
* @event change
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
*/
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
var hasContent = this.cacheCanvas || this.spriteSheet.complete;
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) {
|
|
return true;
|
|
}
|
|
this._normalizeFrame();
|
|
var o = this.spriteSheet.getFrame(this._currentFrame | 0);
|
|
if (!o) {
|
|
return false;
|
|
}
|
|
var rect = o.rect;
|
|
if (rect.width && rect.height) {
|
|
ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
//Note, the doc sections below document using the specified APIs (from DisplayObject) from
|
|
//Bitmap. This is why they have no method implementations.
|
|
|
|
/**
|
|
* Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
|
|
* You should not cache Sprite instances as it can degrade performance.
|
|
* @method cache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
|
|
* You should not cache Sprite instances as it can degrade performance.
|
|
* @method updateCache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
|
|
* You should not cache Sprite instances as it can degrade performance.
|
|
* @method uncache
|
|
**/
|
|
|
|
/**
|
|
* Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}}
|
|
* or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain
|
|
* unchanged.
|
|
* @method play
|
|
**/
|
|
p.play = function () {
|
|
this.paused = false;
|
|
};
|
|
|
|
/**
|
|
* Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}
|
|
* is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}}
|
|
* will resume playback.
|
|
* @method stop
|
|
**/
|
|
p.stop = function () {
|
|
this.paused = true;
|
|
};
|
|
|
|
/**
|
|
* Sets paused to false and plays the specified animation name, named frame, or frame number.
|
|
* @method gotoAndPlay
|
|
* @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
|
|
* and begin playing.
|
|
**/
|
|
p.gotoAndPlay = function (frameOrAnimation) {
|
|
this.paused = false;
|
|
this._skipAdvance = true;
|
|
this._goto(frameOrAnimation);
|
|
};
|
|
|
|
/**
|
|
* Sets paused to true and seeks to the specified animation name, named frame, or frame number.
|
|
* @method gotoAndStop
|
|
* @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
|
|
* and stop.
|
|
**/
|
|
p.gotoAndStop = function (frameOrAnimation) {
|
|
this.paused = true;
|
|
this._goto(frameOrAnimation);
|
|
};
|
|
|
|
/**
|
|
* Advances the playhead. This occurs automatically each tick by default.
|
|
* @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite
|
|
* or its SpriteSheet.
|
|
* @method advance
|
|
*/
|
|
p.advance = function (time) {
|
|
var fps = this.framerate || this.spriteSheet.framerate;
|
|
var t = fps && time != null ? time / (1000 / fps) : 1;
|
|
this._normalizeFrame(t);
|
|
};
|
|
|
|
/**
|
|
* Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to
|
|
* the origin. For example, a 90 x 70 frame with <code>regX=50</code> and <code>regY=40</code> would return a
|
|
* rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object.
|
|
*
|
|
* Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method.
|
|
* @method getBounds
|
|
* @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully
|
|
* loaded.
|
|
**/
|
|
p.getBounds = function () {
|
|
// TODO: should this normalizeFrame?
|
|
return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned
|
|
* instances.
|
|
* @method clone
|
|
* @return {Sprite} a clone of the Sprite instance.
|
|
**/
|
|
p.clone = function () {
|
|
return this._cloneProps(new Sprite(this.spriteSheet));
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Sprite (name=" + this.name + ")]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {Sprite} o
|
|
* @return {Sprite} o
|
|
* @protected
|
|
**/
|
|
p._cloneProps = function (o) {
|
|
this.DisplayObject__cloneProps(o);
|
|
o.currentFrame = this.currentFrame;
|
|
o.currentAnimation = this.currentAnimation;
|
|
o.paused = this.paused;
|
|
o.currentAnimationFrame = this.currentAnimationFrame;
|
|
o.framerate = this.framerate;
|
|
|
|
o._animation = this._animation;
|
|
o._currentFrame = this._currentFrame;
|
|
o._skipAdvance = this._skipAdvance;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Advances the <code>currentFrame</code> if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}}
|
|
* ticks.
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* @protected
|
|
* @method _tick
|
|
**/
|
|
p._tick = function (evtObj) {
|
|
if (!this.paused) {
|
|
if (!this._skipAdvance) {
|
|
this.advance(evtObj && evtObj.delta);
|
|
}
|
|
this._skipAdvance = false;
|
|
}
|
|
this.DisplayObject__tick(evtObj);
|
|
};
|
|
|
|
/**
|
|
* Normalizes the current frame, advancing animations and dispatching callbacks as appropriate.
|
|
* @protected
|
|
* @method _normalizeFrame
|
|
**/
|
|
p._normalizeFrame = function (frameDelta) {
|
|
frameDelta = frameDelta || 0;
|
|
var animation = this._animation;
|
|
var paused = this.paused;
|
|
var frame = this._currentFrame;
|
|
var l;
|
|
|
|
if (animation) {
|
|
var speed = animation.speed || 1;
|
|
var animFrame = this.currentAnimationFrame;
|
|
l = animation.frames.length;
|
|
if (animFrame + frameDelta * speed >= l) {
|
|
var next = animation.next;
|
|
if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) {
|
|
// something changed in the event stack, so we shouldn't make any more changes here.
|
|
return;
|
|
} else if (next) {
|
|
// sequence. Automatically calls _normalizeFrame again with the remaining frames.
|
|
return this._goto(next, frameDelta - (l - animFrame) / speed);
|
|
} else {
|
|
// end.
|
|
this.paused = true;
|
|
animFrame = animation.frames.length - 1;
|
|
}
|
|
} else {
|
|
animFrame += frameDelta * speed;
|
|
}
|
|
this.currentAnimationFrame = animFrame;
|
|
this._currentFrame = animation.frames[animFrame | 0];
|
|
} else {
|
|
frame = this._currentFrame += frameDelta;
|
|
l = this.spriteSheet.getNumFrames();
|
|
if (frame >= l && l > 0) {
|
|
if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) {
|
|
// looped.
|
|
if ((this._currentFrame -= l) >= l) {
|
|
return this._normalizeFrame();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
frame = this._currentFrame | 0;
|
|
if (this.currentFrame != frame) {
|
|
this.currentFrame = frame;
|
|
this.dispatchEvent("change");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}},
|
|
* {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.)
|
|
* @property _dispatchAnimationEnd
|
|
* @private
|
|
* @type {Function}
|
|
**/
|
|
p._dispatchAnimationEnd = function (animation, frame, paused, next, end) {
|
|
var name = animation ? animation.name : null;
|
|
if (this.hasEventListener("animationend")) {
|
|
var evt = new createjs.Event("animationend");
|
|
evt.name = name;
|
|
evt.next = next;
|
|
this.dispatchEvent(evt);
|
|
}
|
|
// did the animation get changed in the event stack?:
|
|
var changed = this._animation != animation || this._currentFrame != frame;
|
|
// if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame:
|
|
if (!changed && !paused && this.paused) {
|
|
this.currentAnimationFrame = end;changed = true;
|
|
}
|
|
return changed;
|
|
};
|
|
|
|
/**
|
|
* Moves the playhead to the specified frame number or animation.
|
|
* @method _goto
|
|
* @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to.
|
|
* @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0.
|
|
* @protected
|
|
**/
|
|
p._goto = function (frameOrAnimation, frame) {
|
|
this.currentAnimationFrame = 0;
|
|
if (isNaN(frameOrAnimation)) {
|
|
var data = this.spriteSheet.getAnimation(frameOrAnimation);
|
|
if (data) {
|
|
this._animation = data;
|
|
this.currentAnimation = frameOrAnimation;
|
|
this._normalizeFrame(frame);
|
|
}
|
|
} else {
|
|
this.currentAnimation = this._animation = null;
|
|
this._currentFrame = frameOrAnimation;
|
|
this._normalizeFrame();
|
|
}
|
|
};
|
|
|
|
createjs.Sprite = createjs.promote(Sprite, "DisplayObject");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Shape.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}}
|
|
* instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape
|
|
* instances to display the same vector graphics with different positions or transforms.
|
|
*
|
|
* If the vector art will not
|
|
* change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the
|
|
* rendering cost.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100);
|
|
* var shape = new createjs.Shape(graphics);
|
|
*
|
|
* //Alternatively use can also use the graphics property of the Shape class to renderer the same as above.
|
|
* var shape = new createjs.Shape();
|
|
* shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
|
|
*
|
|
* @class Shape
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created.
|
|
**/
|
|
|
|
function Shape(graphics) {
|
|
this.DisplayObject_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The graphics instance to display.
|
|
* @property graphics
|
|
* @type Graphics
|
|
**/
|
|
this.graphics = graphics ? graphics : new createjs.Graphics();
|
|
}
|
|
var p = createjs.extend(Shape, createjs.DisplayObject);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the Shape would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
var hasContent = this.cacheCanvas || this.graphics && !this.graphics.isEmpty();
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the Shape into the specified context ignoring its visible, alpha, shadow, and transform. Returns true if
|
|
* the draw was handled (useful for overriding functionality).
|
|
*
|
|
* <i>NOTE: This method is mainly for internal use, though it may be useful for advanced uses.</i>
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
|
|
* used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
|
|
* @return {Boolean}
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) {
|
|
return true;
|
|
}
|
|
this.graphics.draw(ctx, this);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to
|
|
* their defaults (for example .parent).
|
|
* @method clone
|
|
* @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be
|
|
* cloned. If false, the Graphics instance will be shared with the new Shape.
|
|
**/
|
|
p.clone = function (recursive) {
|
|
var g = recursive && this.graphics ? this.graphics.clone() : this.graphics;
|
|
return this._cloneProps(new Shape(g));
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Shape (name=" + this.name + ")]";
|
|
};
|
|
|
|
createjs.Shape = createjs.promote(Shape, "DisplayObject");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Text.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the
|
|
* lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML
|
|
* text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
|
|
* method, or using {{#crossLink "DOMElement"}}{{/crossLink}}.
|
|
*
|
|
* <b>Please note that Text does not support HTML text, and can only display one font style at a time.</b> To use
|
|
* multiple font styles, you will need to create multiple text instances, and position them manually.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
|
|
* text.x = 100;
|
|
* text.textBaseline = "alphabetic";
|
|
*
|
|
* CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser
|
|
* before it can be displayed.
|
|
*
|
|
* <strong>Note:</strong> Text can be expensive to generate, so cache instances where possible. Be aware that not all
|
|
* browsers will render Text exactly the same.
|
|
* @class Text
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {String} [text] The text to display.
|
|
* @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold
|
|
* 36px Arial").
|
|
* @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
|
|
* "#F00", "red", or "#FF0000").
|
|
**/
|
|
|
|
function Text(text, font, color) {
|
|
this.DisplayObject_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The text to display.
|
|
* @property text
|
|
* @type String
|
|
**/
|
|
this.text = text;
|
|
|
|
/**
|
|
* The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial").
|
|
* @property font
|
|
* @type String
|
|
**/
|
|
this.font = font;
|
|
|
|
/**
|
|
* The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000".
|
|
* It will also accept valid canvas fillStyle values.
|
|
* @property color
|
|
* @type String
|
|
**/
|
|
this.color = color;
|
|
|
|
/**
|
|
* The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed
|
|
* information view the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
|
|
* whatwg spec</a>. Default is "left".
|
|
* @property textAlign
|
|
* @type String
|
|
**/
|
|
this.textAlign = "left";
|
|
|
|
/**
|
|
* The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or
|
|
* "bottom". For detailed information view the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
|
|
* whatwg spec</a>. Default is "top".
|
|
* @property textBaseline
|
|
* @type String
|
|
*/
|
|
this.textBaseline = "top";
|
|
|
|
/**
|
|
* The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or
|
|
* shrunk to make it fit in this width. For detailed information view the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
|
|
* whatwg spec</a>.
|
|
* @property maxWidth
|
|
* @type Number
|
|
*/
|
|
this.maxWidth = null;
|
|
|
|
/**
|
|
* If greater than 0, the text will be drawn as a stroke (outline) of the specified width.
|
|
* @property outline
|
|
* @type Number
|
|
**/
|
|
this.outline = 0;
|
|
|
|
/**
|
|
* Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0,
|
|
* the value of getMeasuredLineHeight is used.
|
|
* @property lineHeight
|
|
* @type Number
|
|
**/
|
|
this.lineHeight = 0;
|
|
|
|
/**
|
|
* Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null,
|
|
* the text will not be wrapped.
|
|
* @property lineWidth
|
|
* @type Number
|
|
**/
|
|
this.lineWidth = null;
|
|
}
|
|
var p = createjs.extend(Text, createjs.DisplayObject);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// static properties:
|
|
/**
|
|
* @property _workingContext
|
|
* @type CanvasRenderingContext2D
|
|
* @private
|
|
**/
|
|
var canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
if (canvas.getContext) {
|
|
Text._workingContext = canvas.getContext("2d");canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
// constants:
|
|
/**
|
|
* Lookup table for the ratio to offset bounds x calculations based on the textAlign property.
|
|
* @property H_OFFSETS
|
|
* @type Object
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Text.H_OFFSETS = { start: 0, left: 0, center: -0.5, end: -1, right: -1 };
|
|
|
|
/**
|
|
* Lookup table for the ratio to offset bounds y calculations based on the textBaseline property.
|
|
* @property H_OFFSETS
|
|
* @type Object
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Text.V_OFFSETS = { top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1 };
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
var hasContent = this.cacheCanvas || this.text != null && this.text !== "";
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the Text into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) {
|
|
return true;
|
|
}
|
|
|
|
var col = this.color || "#000";
|
|
if (this.outline) {
|
|
ctx.strokeStyle = col;ctx.lineWidth = this.outline * 1;
|
|
} else {
|
|
ctx.fillStyle = col;
|
|
}
|
|
|
|
this._drawText(this._prepContext(ctx));
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value.
|
|
* @method getMeasuredWidth
|
|
* @return {Number} The measured, untransformed width of the text.
|
|
**/
|
|
p.getMeasuredWidth = function () {
|
|
return this._getMeasuredWidth(this.text);
|
|
};
|
|
|
|
/**
|
|
* Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured
|
|
* width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts.
|
|
* @method getMeasuredLineHeight
|
|
* @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is
|
|
* based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts.
|
|
**/
|
|
p.getMeasuredLineHeight = function () {
|
|
return this._getMeasuredWidth("M") * 1.2;
|
|
};
|
|
|
|
/**
|
|
* Returns the approximate height of multi-line text by multiplying the number of lines against either the
|
|
* <code>lineHeight</code> (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
|
|
* this operation requires the text flowing logic to run, which has an associated CPU cost.
|
|
* @method getMeasuredHeight
|
|
* @return {Number} The approximate height of the untransformed multi-line text.
|
|
**/
|
|
p.getMeasuredHeight = function () {
|
|
return this._drawText(null, {}).height;
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getBounds = function () {
|
|
var rect = this.DisplayObject_getBounds();
|
|
if (rect) {
|
|
return rect;
|
|
}
|
|
if (this.text == null || this.text === "") {
|
|
return null;
|
|
}
|
|
var o = this._drawText(null, {});
|
|
var w = this.maxWidth && this.maxWidth < o.width ? this.maxWidth : o.width;
|
|
var x = w * Text.H_OFFSETS[this.textAlign || "left"];
|
|
var lineHeight = this.lineHeight || this.getMeasuredLineHeight();
|
|
var y = lineHeight * Text.V_OFFSETS[this.textBaseline || "top"];
|
|
return this._rectangle.setValues(x, y, w, o.height);
|
|
};
|
|
|
|
/**
|
|
* Returns an object with width, height, and lines properties. The width and height are the visual width and height
|
|
* of the drawn text. The lines property contains an array of strings, one for
|
|
* each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing
|
|
* whitespace removed.
|
|
* @method getMetrics
|
|
* @return {Object} An object with width, height, and lines properties.
|
|
**/
|
|
p.getMetrics = function () {
|
|
var o = { lines: [] };
|
|
o.lineHeight = this.lineHeight || this.getMeasuredLineHeight();
|
|
o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline || "top"];
|
|
return this._drawText(null, o, o.lines);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Text instance.
|
|
* @method clone
|
|
* @return {Text} a clone of the Text instance.
|
|
**/
|
|
p.clone = function () {
|
|
return this._cloneProps(new Text(this.text, this.font, this.color));
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Text (text=" + (this.text.length > 20 ? this.text.substr(0, 17) + "..." : this.text) + ")]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {Text} o
|
|
* @protected
|
|
* @return {Text} o
|
|
**/
|
|
p._cloneProps = function (o) {
|
|
this.DisplayObject__cloneProps(o);
|
|
o.textAlign = this.textAlign;
|
|
o.textBaseline = this.textBaseline;
|
|
o.maxWidth = this.maxWidth;
|
|
o.outline = this.outline;
|
|
o.lineHeight = this.lineHeight;
|
|
o.lineWidth = this.lineWidth;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _getWorkingContext
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @return {CanvasRenderingContext2D}
|
|
* @protected
|
|
**/
|
|
p._prepContext = function (ctx) {
|
|
ctx.font = this.font || "10px sans-serif";
|
|
ctx.textAlign = this.textAlign || "left";
|
|
ctx.textBaseline = this.textBaseline || "top";
|
|
return ctx;
|
|
};
|
|
|
|
/**
|
|
* Draws multiline text.
|
|
* @method _drawText
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @param {Object} o
|
|
* @param {Array} lines
|
|
* @return {Object}
|
|
* @protected
|
|
**/
|
|
p._drawText = function (ctx, o, lines) {
|
|
var paint = !!ctx;
|
|
if (!paint) {
|
|
ctx = Text._workingContext;
|
|
ctx.save();
|
|
this._prepContext(ctx);
|
|
}
|
|
var lineHeight = this.lineHeight || this.getMeasuredLineHeight();
|
|
|
|
var maxW = 0,
|
|
count = 0;
|
|
var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);
|
|
for (var i = 0, l = hardLines.length; i < l; i++) {
|
|
var str = hardLines[i];
|
|
var w = null;
|
|
|
|
if (this.lineWidth != null && (w = ctx.measureText(str).width) > this.lineWidth) {
|
|
// text wrapping:
|
|
var words = str.split(/(\s)/);
|
|
str = words[0];
|
|
w = ctx.measureText(str).width;
|
|
|
|
for (var j = 1, jl = words.length; j < jl; j += 2) {
|
|
// Line needs to wrap:
|
|
var wordW = ctx.measureText(words[j] + words[j + 1]).width;
|
|
if (w + wordW > this.lineWidth) {
|
|
if (paint) {
|
|
this._drawTextLine(ctx, str, count * lineHeight);
|
|
}
|
|
if (lines) {
|
|
lines.push(str);
|
|
}
|
|
if (w > maxW) {
|
|
maxW = w;
|
|
}
|
|
str = words[j + 1];
|
|
w = ctx.measureText(str).width;
|
|
count++;
|
|
} else {
|
|
str += words[j] + words[j + 1];
|
|
w += wordW;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (paint) {
|
|
this._drawTextLine(ctx, str, count * lineHeight);
|
|
}
|
|
if (lines) {
|
|
lines.push(str);
|
|
}
|
|
if (o && w == null) {
|
|
w = ctx.measureText(str).width;
|
|
}
|
|
if (w > maxW) {
|
|
maxW = w;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
if (o) {
|
|
o.width = maxW;
|
|
o.height = count * lineHeight;
|
|
}
|
|
if (!paint) {
|
|
ctx.restore();
|
|
}
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _drawTextLine
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @param {String} text
|
|
* @param {Number} y
|
|
* @protected
|
|
**/
|
|
p._drawTextLine = function (ctx, text, y) {
|
|
// Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
|
|
if (this.outline) {
|
|
ctx.strokeText(text, 0, y, this.maxWidth || 0xFFFF);
|
|
} else {
|
|
ctx.fillText(text, 0, y, this.maxWidth || 0xFFFF);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _getMeasuredWidth
|
|
* @param {String} text
|
|
* @protected
|
|
**/
|
|
p._getMeasuredWidth = function (text) {
|
|
var ctx = Text._workingContext;
|
|
ctx.save();
|
|
var w = this._prepContext(ctx).measureText(text).width;
|
|
ctx.restore();
|
|
return w;
|
|
};
|
|
|
|
createjs.Text = createjs.promote(Text, "DisplayObject");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// BitmapText.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported
|
|
* using new line characters, but automatic wrapping is not supported. See the
|
|
* {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}}
|
|
* property for more information on defining glyphs.
|
|
*
|
|
* <strong>Important:</strong> BitmapText extends Container, but is not designed to be used as one.
|
|
* As such, methods like addChild and removeChild are disabled.
|
|
* @class BitmapText
|
|
* @extends DisplayObject
|
|
* @param {String} [text=""] The text to display.
|
|
* @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs.
|
|
* @constructor
|
|
**/
|
|
|
|
function BitmapText(text, spriteSheet) {
|
|
this.Container_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The text to display.
|
|
* @property text
|
|
* @type String
|
|
* @default ""
|
|
**/
|
|
this.text = text || "";
|
|
|
|
/**
|
|
* A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character
|
|
* should have a single frame animation defined in the sprite sheet named the same as
|
|
* corresponding character. For example, the following animation definition:
|
|
*
|
|
* "A": {frames: [0]}
|
|
*
|
|
* would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form
|
|
* is also acceptable:
|
|
*
|
|
* "A": 0
|
|
*
|
|
* Note that if a character in the text is not found in the sprite sheet, it will also
|
|
* try to use the alternate case (upper or lower).
|
|
*
|
|
* See SpriteSheet for more information on defining sprite sheet data.
|
|
* @property spriteSheet
|
|
* @type SpriteSheet
|
|
* @default null
|
|
**/
|
|
this.spriteSheet = spriteSheet;
|
|
|
|
/**
|
|
* The height of each line of text. If 0, then it will use a line height calculated
|
|
* by checking for the height of the "1", "T", or "L" character (in that order). If
|
|
* those characters are not defined, it will use the height of the first frame of the
|
|
* sprite sheet.
|
|
* @property lineHeight
|
|
* @type Number
|
|
* @default 0
|
|
**/
|
|
this.lineHeight = 0;
|
|
|
|
/**
|
|
* This spacing (in pixels) will be added after each character in the output.
|
|
* @property letterSpacing
|
|
* @type Number
|
|
* @default 0
|
|
**/
|
|
this.letterSpacing = 0;
|
|
|
|
/**
|
|
* If a space character is not defined in the sprite sheet, then empty pixels equal to
|
|
* spaceWidth will be inserted instead. If 0, then it will use a value calculated
|
|
* by checking for the width of the "1", "l", "E", or "A" character (in that order). If
|
|
* those characters are not defined, it will use the width of the first frame of the
|
|
* sprite sheet.
|
|
* @property spaceWidth
|
|
* @type Number
|
|
* @default 0
|
|
**/
|
|
this.spaceWidth = 0;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _oldProps
|
|
* @type Object
|
|
* @protected
|
|
**/
|
|
this._oldProps = { text: 0, spriteSheet: 0, lineHeight: 0, letterSpacing: 0, spaceWidth: 0 };
|
|
}
|
|
var p = createjs.extend(BitmapText, createjs.Container);
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
// static properties:
|
|
/**
|
|
* BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains
|
|
* an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be
|
|
* retained, slightly increasing memory use, but reducing instantiation.
|
|
* @property maxPoolSize
|
|
* @type Number
|
|
* @static
|
|
* @default 100
|
|
**/
|
|
BitmapText.maxPoolSize = 100;
|
|
|
|
/**
|
|
* Sprite object pool.
|
|
* @type {Array}
|
|
* @static
|
|
* @private
|
|
*/
|
|
BitmapText._spritePool = [];
|
|
|
|
// public methods:
|
|
/**
|
|
* Docced in superclass.
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) {
|
|
return;
|
|
}
|
|
this._updateText();
|
|
this.Container_draw(ctx, ignoreCache);
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
**/
|
|
p.getBounds = function () {
|
|
this._updateText();
|
|
return this.Container_getBounds();
|
|
};
|
|
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
var hasContent = this.cacheCanvas || this.spriteSheet && this.spriteSheet.complete && this.text;
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0 && hasContent);
|
|
};
|
|
|
|
p.clone = function () {
|
|
return this._cloneProps(new BitmapText(this.text, this.spriteSheet));
|
|
};
|
|
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method addChild
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method addChildAt
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method removeChild
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method removeChildAt
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method removeAllChildren
|
|
**/
|
|
p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function () {};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {BitmapText} o
|
|
* @return {BitmapText} o
|
|
* @protected
|
|
**/
|
|
p._cloneProps = function (o) {
|
|
this.Container__cloneProps(o);
|
|
o.lineHeight = this.lineHeight;
|
|
o.letterSpacing = this.letterSpacing;
|
|
o.spaceWidth = this.spaceWidth;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _getFrameIndex
|
|
* @param {String} character
|
|
* @param {SpriteSheet} spriteSheet
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p._getFrameIndex = function (character, spriteSheet) {
|
|
var c,
|
|
o = spriteSheet.getAnimation(character);
|
|
if (!o) {
|
|
character != (c = character.toUpperCase()) || character != (c = character.toLowerCase()) || (c = null);
|
|
if (c) {
|
|
o = spriteSheet.getAnimation(c);
|
|
}
|
|
}
|
|
return o && o.frames[0];
|
|
};
|
|
|
|
/**
|
|
* @method _getFrame
|
|
* @param {String} character
|
|
* @param {SpriteSheet} spriteSheet
|
|
* @return {Object}
|
|
* @protected
|
|
**/
|
|
p._getFrame = function (character, spriteSheet) {
|
|
var index = this._getFrameIndex(character, spriteSheet);
|
|
return index == null ? index : spriteSheet.getFrame(index);
|
|
};
|
|
|
|
/**
|
|
* @method _getLineHeight
|
|
* @param {SpriteSheet} ss
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p._getLineHeight = function (ss) {
|
|
var frame = this._getFrame("1", ss) || this._getFrame("T", ss) || this._getFrame("L", ss) || ss.getFrame(0);
|
|
return frame ? frame.rect.height : 1;
|
|
};
|
|
/**
|
|
* @method _getSpaceWidth
|
|
* @param {SpriteSheet} ss
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p._getSpaceWidth = function (ss) {
|
|
var frame = this._getFrame("1", ss) || this._getFrame("l", ss) || this._getFrame("e", ss) || this._getFrame("a", ss) || ss.getFrame(0);
|
|
return frame ? frame.rect.width : 1;
|
|
};
|
|
|
|
/**
|
|
* @method _drawText
|
|
* @protected
|
|
**/
|
|
p._updateText = function () {
|
|
var x = 0,
|
|
y = 0,
|
|
o = this._oldProps,
|
|
change = false,
|
|
spaceW = this.spaceWidth,
|
|
lineH = this.lineHeight,
|
|
ss = this.spriteSheet;
|
|
var pool = BitmapText._spritePool,
|
|
kids = this.children,
|
|
childIndex = 0,
|
|
numKids = kids.length,
|
|
sprite;
|
|
|
|
for (var n in o) {
|
|
if (o[n] != this[n]) {
|
|
o[n] = this[n];
|
|
change = true;
|
|
}
|
|
}
|
|
if (!change) {
|
|
return;
|
|
}
|
|
|
|
var hasSpace = !!this._getFrame(" ", ss);
|
|
if (!hasSpace && !spaceW) {
|
|
spaceW = this._getSpaceWidth(ss);
|
|
}
|
|
if (!lineH) {
|
|
lineH = this._getLineHeight(ss);
|
|
}
|
|
|
|
for (var i = 0, l = this.text.length; i < l; i++) {
|
|
var character = this.text.charAt(i);
|
|
if (character == " " && !hasSpace) {
|
|
x += spaceW;
|
|
continue;
|
|
} else if (character == "\n" || character == "\r") {
|
|
if (character == "\r" && this.text.charAt(i + 1) == "\n") {
|
|
i++;
|
|
} // crlf
|
|
x = 0;
|
|
y += lineH;
|
|
continue;
|
|
}
|
|
|
|
var index = this._getFrameIndex(character, ss);
|
|
if (index == null) {
|
|
continue;
|
|
}
|
|
|
|
if (childIndex < numKids) {
|
|
sprite = kids[childIndex];
|
|
} else {
|
|
kids.push(sprite = pool.length ? pool.pop() : new createjs.Sprite());
|
|
sprite.parent = this;
|
|
numKids++;
|
|
}
|
|
sprite.spriteSheet = ss;
|
|
sprite.gotoAndStop(index);
|
|
sprite.x = x;
|
|
sprite.y = y;
|
|
childIndex++;
|
|
|
|
x += sprite.getBounds().width + this.letterSpacing;
|
|
}
|
|
while (numKids > childIndex) {
|
|
// faster than removeChild.
|
|
pool.push(sprite = kids.pop());
|
|
sprite.parent = null;
|
|
numKids--;
|
|
}
|
|
if (pool.length > BitmapText.maxPoolSize) {
|
|
pool.length = BitmapText.maxPoolSize;
|
|
}
|
|
};
|
|
|
|
createjs.BitmapText = createjs.promote(BitmapText, "Container");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// MovieClip.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* The MovieClip class associates a TweenJS Timeline with an EaselJS {{#crossLink "Container"}}{{/crossLink}}. It allows
|
|
* you to create objects which encapsulate timeline animations, state changes, and synched actions. Due to the
|
|
* complexities inherent in correctly setting up a MovieClip, it is largely intended for tool output and is not included
|
|
* in the main EaselJS library.
|
|
*
|
|
* Currently MovieClip only works properly if it is tick based (as opposed to time based) though some concessions have
|
|
* been made to support time-based timelines in the future.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example animates two shapes back and forth. The grey shape starts on the left, but we jump to a mid-point in
|
|
* the animation using {{#crossLink "MovieClip/gotoAndPlay"}}{{/crossLink}}.
|
|
*
|
|
* var stage = new createjs.Stage("canvas");
|
|
* createjs.Ticker.addEventListener("tick", stage);
|
|
*
|
|
* var mc = new createjs.MovieClip(null, 0, true, {start:20});
|
|
* stage.addChild(mc);
|
|
*
|
|
* var child1 = new createjs.Shape(
|
|
* new createjs.Graphics().beginFill("#999999")
|
|
* .drawCircle(30,30,30));
|
|
* var child2 = new createjs.Shape(
|
|
* new createjs.Graphics().beginFill("#5a9cfb")
|
|
* .drawCircle(30,30,30));
|
|
*
|
|
* mc.timeline.addTween(
|
|
* createjs.Tween.get(child1)
|
|
* .to({x:0}).to({x:60}, 50).to({x:0}, 50));
|
|
* mc.timeline.addTween(
|
|
* createjs.Tween.get(child2)
|
|
* .to({x:60}).to({x:0}, 50).to({x:60}, 50));
|
|
*
|
|
* mc.gotoAndPlay("start");
|
|
*
|
|
* It is recommended to use <code>tween.to()</code> to animate and set properties (use no duration to have it set
|
|
* immediately), and the <code>tween.wait()</code> method to create delays between animations. Note that using the
|
|
* <code>tween.set()</code> method to affect properties will likely not provide the desired result.
|
|
*
|
|
* @class MovieClip
|
|
* @main MovieClip
|
|
* @extends Container
|
|
* @constructor
|
|
* @param {String} [mode=independent] Initial value for the mode property. One of {{#crossLink "MovieClip/INDEPENDENT:property"}}{{/crossLink}},
|
|
* {{#crossLink "MovieClip/SINGLE_FRAME:property"}}{{/crossLink}}, or {{#crossLink "MovieClip/SYNCHED:property"}}{{/crossLink}}.
|
|
* The default is {{#crossLink "MovieClip/INDEPENDENT:property"}}{{/crossLink}}.
|
|
* @param {Number} [startPosition=0] Initial value for the {{#crossLink "MovieClip/startPosition:property"}}{{/crossLink}}
|
|
* property.
|
|
* @param {Boolean} [loop=true] Initial value for the {{#crossLink "MovieClip/loop:property"}}{{/crossLink}}
|
|
* property. The default is `true`.
|
|
* @param {Object} [labels=null] A hash of labels to pass to the {{#crossLink "MovieClip/timeline:property"}}{{/crossLink}}
|
|
* instance associated with this MovieClip. Labels only need to be passed if they need to be used.
|
|
**/
|
|
|
|
function MovieClip(mode, startPosition, loop, labels) {
|
|
this.Container_constructor();
|
|
!MovieClip.inited && MovieClip.init(); // static init
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED).
|
|
* See each constant for a description of the behaviour.
|
|
* @property mode
|
|
* @type String
|
|
* @default null
|
|
**/
|
|
this.mode = mode || MovieClip.INDEPENDENT;
|
|
|
|
/**
|
|
* Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME.
|
|
* @property startPosition
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.startPosition = startPosition || 0;
|
|
|
|
/**
|
|
* Indicates whether this MovieClip should loop when it reaches the end of its timeline.
|
|
* @property loop
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.loop = loop;
|
|
|
|
/**
|
|
* The current frame of the movieclip.
|
|
* @property currentFrame
|
|
* @type Number
|
|
* @default 0
|
|
* @readonly
|
|
*/
|
|
this.currentFrame = 0;
|
|
|
|
/**
|
|
* The TweenJS Timeline that is associated with this MovieClip. This is created automatically when the MovieClip
|
|
* instance is initialized. Animations are created by adding <a href="http://tweenjs.com">TweenJS</a> Tween
|
|
* instances to the timeline.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var tween = createjs.Tween.get(target).to({x:0}).to({x:100}, 30);
|
|
* var mc = new createjs.MovieClip();
|
|
* mc.timeline.addTween(tween);
|
|
*
|
|
* Elements can be added and removed from the timeline by toggling an "_off" property
|
|
* using the <code>tweenInstance.to()</code> method. Note that using <code>Tween.set</code> is not recommended to
|
|
* create MovieClip animations. The following example will toggle the target off on frame 0, and then back on for
|
|
* frame 1. You can use the "visible" property to achieve the same effect.
|
|
*
|
|
* var tween = createjs.Tween.get(target).to({_off:false})
|
|
* .wait(1).to({_off:true})
|
|
* .wait(1).to({_off:false});
|
|
*
|
|
* @property timeline
|
|
* @type Timeline
|
|
* @default null
|
|
*/
|
|
this.timeline = new createjs.Timeline(null, labels, { paused: true, position: startPosition, useTicks: true });
|
|
|
|
/**
|
|
* If true, the MovieClip's position will not advance when ticked.
|
|
* @property paused
|
|
* @type Boolean
|
|
* @default false
|
|
*/
|
|
this.paused = false;
|
|
|
|
/**
|
|
* If true, actions in this MovieClip's tweens will be run when the playhead advances.
|
|
* @property actionsEnabled
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.actionsEnabled = true;
|
|
|
|
/**
|
|
* If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds
|
|
* it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT.
|
|
* <br><br>
|
|
* For example, if you had a character animation with a "body" child MovieClip instance
|
|
* with different costumes on each frame, you could set body.autoReset = false, so that
|
|
* you can manually change the frame it is on, without worrying that it will be reset
|
|
* automatically.
|
|
* @property autoReset
|
|
* @type Boolean
|
|
* @default true
|
|
*/
|
|
this.autoReset = true;
|
|
|
|
/**
|
|
* An array of bounds for each frame in the MovieClip. This is mainly intended for tool output.
|
|
* @property frameBounds
|
|
* @type Array
|
|
* @default null
|
|
*/
|
|
this.frameBounds = this.frameBounds || null; // TODO: Deprecated. This is for backwards support of FlashCC
|
|
|
|
/**
|
|
* By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip
|
|
* will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
|
|
* framerate.
|
|
*
|
|
* For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will
|
|
* advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
|
|
* vary slightly between frames.
|
|
*
|
|
* This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
|
|
* passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
|
|
* @property framerate
|
|
* @type {Number}
|
|
* @default null
|
|
**/
|
|
this.framerate = null;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _synchOffset
|
|
* @type Number
|
|
* @default 0
|
|
* @private
|
|
*/
|
|
this._synchOffset = 0;
|
|
|
|
/**
|
|
* @property _prevPos
|
|
* @type Number
|
|
* @default -1
|
|
* @private
|
|
*/
|
|
this._prevPos = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1.
|
|
|
|
/**
|
|
* @property _prevPosition
|
|
* @type Number
|
|
* @default 0
|
|
* @private
|
|
*/
|
|
this._prevPosition = 0;
|
|
|
|
/**
|
|
* The time remaining from the previous tick, only applicable when .framerate is set.
|
|
* @property _t
|
|
* @type Number
|
|
* @private
|
|
*/
|
|
this._t = 0;
|
|
|
|
/**
|
|
* List of display objects that are actively being managed by the MovieClip.
|
|
* @property _managed
|
|
* @type Object
|
|
* @private
|
|
*/
|
|
this._managed = {};
|
|
}
|
|
var p = createjs.extend(MovieClip, createjs.Container);
|
|
|
|
// constants:
|
|
/**
|
|
* The MovieClip will advance independently of its parent, even if its parent is paused.
|
|
* This is the default mode.
|
|
* @property INDEPENDENT
|
|
* @static
|
|
* @type String
|
|
* @default "independent"
|
|
* @readonly
|
|
**/
|
|
MovieClip.INDEPENDENT = "independent";
|
|
|
|
/**
|
|
* The MovieClip will only display a single frame (as determined by the startPosition property).
|
|
* @property SINGLE_FRAME
|
|
* @static
|
|
* @type String
|
|
* @default "single"
|
|
* @readonly
|
|
**/
|
|
MovieClip.SINGLE_FRAME = "single";
|
|
|
|
/**
|
|
* The MovieClip will be advanced only when its parent advances and will be synched to the position of
|
|
* the parent MovieClip.
|
|
* @property SYNCHED
|
|
* @static
|
|
* @type String
|
|
* @default "synched"
|
|
* @readonly
|
|
**/
|
|
MovieClip.SYNCHED = "synched";
|
|
|
|
// static properties:
|
|
MovieClip.inited = false;
|
|
|
|
// static methods:
|
|
MovieClip.init = function () {
|
|
if (MovieClip.inited) {
|
|
return;
|
|
}
|
|
// plugins introduce some overhead to Tween, so we only install this if an MC is instantiated.
|
|
MovieClipPlugin.install();
|
|
MovieClip.inited = true;
|
|
};
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Use the {{#crossLink "MovieClip/labels:property"}}{{/crossLink}} property instead.
|
|
* @method getLabels
|
|
* @return {Array}
|
|
* @deprecated
|
|
**/
|
|
p.getLabels = function () {
|
|
return this.timeline.getLabels();
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "MovieClip/currentLabel:property"}}{{/crossLink}} property instead.
|
|
* @method getCurrentLabel
|
|
* @return {String}
|
|
* @deprecated
|
|
**/
|
|
p.getCurrentLabel = function () {
|
|
this._updateTimeline();
|
|
return this.timeline.getCurrentLabel();
|
|
};
|
|
|
|
/**
|
|
* Use the {{#crossLink "MovieClip/duration:property"}}{{/crossLink}} property instead.
|
|
* @method getDuration
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p.getDuration = function () {
|
|
return this.timeline.duration;
|
|
};
|
|
|
|
/**
|
|
* Returns an array of objects with label and position (aka frame) properties, sorted by position.
|
|
* Shortcut to TweenJS: Timeline.getLabels();
|
|
* @property labels
|
|
* @type {Array}
|
|
* @readonly
|
|
**/
|
|
|
|
/**
|
|
* Returns the name of the label on or immediately before the current frame. See TweenJS: Timeline.getCurrentLabel()
|
|
* for more information.
|
|
* @property currentLabel
|
|
* @type {String}
|
|
* @readonly
|
|
**/
|
|
|
|
/**
|
|
* Returns the duration of this MovieClip in seconds or ticks. Identical to {{#crossLink "MovieClip/duration:property"}}{{/crossLink}}
|
|
* and provided for Flash API compatibility.
|
|
* @property totalFrames
|
|
* @type {Number}
|
|
* @readonly
|
|
**/
|
|
|
|
/**
|
|
* Returns the duration of this MovieClip in seconds or ticks.
|
|
* @property duration
|
|
* @type {Number}
|
|
* @readonly
|
|
**/
|
|
try {
|
|
Object.defineProperties(p, {
|
|
labels: { get: p.getLabels },
|
|
currentLabel: { get: p.getCurrentLabel },
|
|
totalFrames: { get: p.getDuration },
|
|
duration: { get: p.getDuration }
|
|
});
|
|
} catch (e) {}
|
|
|
|
// public methods:
|
|
/**
|
|
* Constructor alias for backwards compatibility. This method will be removed in future versions.
|
|
* Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
|
|
* @method initialize
|
|
* @deprecated in favour of `createjs.promote()`
|
|
**/
|
|
p.initialize = MovieClip; // TODO: Deprecated. This is for backwards support of FlashCC
|
|
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function () {
|
|
// children are placed in draw, so we can't determine if we have content.
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
// draw to cache first:
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) {
|
|
return true;
|
|
}
|
|
this._updateTimeline();
|
|
this.Container_draw(ctx, ignoreCache);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Sets paused to false.
|
|
* @method play
|
|
**/
|
|
p.play = function () {
|
|
this.paused = false;
|
|
};
|
|
|
|
/**
|
|
* Sets paused to true.
|
|
* @method stop
|
|
**/
|
|
p.stop = function () {
|
|
this.paused = true;
|
|
};
|
|
|
|
/**
|
|
* Advances this movie clip to the specified position or label and sets paused to false.
|
|
* @method gotoAndPlay
|
|
* @param {String|Number} positionOrLabel The animation name or frame number to go to.
|
|
**/
|
|
p.gotoAndPlay = function (positionOrLabel) {
|
|
this.paused = false;
|
|
this._goto(positionOrLabel);
|
|
};
|
|
|
|
/**
|
|
* Advances this movie clip to the specified position or label and sets paused to true.
|
|
* @method gotoAndStop
|
|
* @param {String|Number} positionOrLabel The animation or frame name to go to.
|
|
**/
|
|
p.gotoAndStop = function (positionOrLabel) {
|
|
this.paused = true;
|
|
this._goto(positionOrLabel);
|
|
};
|
|
|
|
/**
|
|
* Advances the playhead. This occurs automatically each tick by default.
|
|
* @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set.
|
|
* @method advance
|
|
*/
|
|
p.advance = function (time) {
|
|
// TODO: should we worry at all about clips who change their own modes via frame scripts?
|
|
var independent = MovieClip.INDEPENDENT;
|
|
if (this.mode != independent) {
|
|
return;
|
|
}
|
|
|
|
var o = this,
|
|
fps = o.framerate;
|
|
while ((o = o.parent) && fps == null) {
|
|
if (o.mode == independent) {
|
|
fps = o._framerate;
|
|
}
|
|
}
|
|
this._framerate = fps;
|
|
|
|
var t = fps != null && fps != -1 && time != null ? time / (1000 / fps) + this._t : 1;
|
|
var frames = t | 0;
|
|
this._t = t - frames; // leftover time
|
|
|
|
while (!this.paused && frames--) {
|
|
this._prevPosition = this._prevPos < 0 ? 0 : this._prevPosition + 1;
|
|
this._updateTimeline();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* MovieClip instances cannot be cloned.
|
|
* @method clone
|
|
**/
|
|
p.clone = function () {
|
|
// TODO: add support for this? Need to clone the Timeline & retarget tweens - pretty complex.
|
|
throw "MovieClip cannot be cloned.";
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[MovieClip (name=" + this.name + ")]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* function.
|
|
* @protected
|
|
**/
|
|
p._tick = function (evtObj) {
|
|
this.advance(evtObj && evtObj.delta);
|
|
this.Container__tick(evtObj);
|
|
};
|
|
|
|
/**
|
|
* @method _goto
|
|
* @param {String|Number} positionOrLabel The animation name or frame number to go to.
|
|
* @protected
|
|
**/
|
|
p._goto = function (positionOrLabel) {
|
|
var pos = this.timeline.resolve(positionOrLabel);
|
|
if (pos == null) {
|
|
return;
|
|
}
|
|
// prevent _updateTimeline from overwriting the new position because of a reset:
|
|
if (this._prevPos == -1) {
|
|
this._prevPos = NaN;
|
|
}
|
|
this._prevPosition = pos;
|
|
this._t = 0;
|
|
this._updateTimeline();
|
|
};
|
|
|
|
/**
|
|
* @method _reset
|
|
* @private
|
|
**/
|
|
p._reset = function () {
|
|
this._prevPos = -1;
|
|
this._t = this.currentFrame = 0;
|
|
this.paused = false;
|
|
};
|
|
|
|
/**
|
|
* @method _updateTimeline
|
|
* @protected
|
|
**/
|
|
p._updateTimeline = function () {
|
|
var tl = this.timeline;
|
|
var synched = this.mode != MovieClip.INDEPENDENT;
|
|
tl.loop = this.loop == null ? true : this.loop;
|
|
|
|
var pos = synched ? this.startPosition + (this.mode == MovieClip.SINGLE_FRAME ? 0 : this._synchOffset) : this._prevPos < 0 ? 0 : this._prevPosition;
|
|
var mode = synched || !this.actionsEnabled ? createjs.Tween.NONE : null;
|
|
|
|
// pre-assign currentFrame so it is available to frame scripts:
|
|
this.currentFrame = tl._calcPosition(pos);
|
|
|
|
// update timeline position, ignoring actions if this is a graphic.
|
|
tl.setPosition(pos, mode);
|
|
|
|
this._prevPosition = tl._prevPosition;
|
|
if (this._prevPos == tl._prevPos) {
|
|
return;
|
|
}
|
|
this.currentFrame = this._prevPos = tl._prevPos;
|
|
|
|
for (var n in this._managed) {
|
|
this._managed[n] = 1;
|
|
}
|
|
|
|
var tweens = tl._tweens;
|
|
for (var i = 0, l = tweens.length; i < l; i++) {
|
|
var tween = tweens[i];
|
|
var target = tween._target;
|
|
if (target == this || tween.passive) {
|
|
continue;
|
|
} // TODO: this assumes actions tween has this as the target. Valid?
|
|
var offset = tween._stepPosition;
|
|
|
|
if (target instanceof createjs.DisplayObject) {
|
|
// motion tween.
|
|
this._addManagedChild(target, offset);
|
|
} else {
|
|
// state tween.
|
|
this._setState(target.state, offset);
|
|
}
|
|
}
|
|
|
|
var kids = this.children;
|
|
for (i = kids.length - 1; i >= 0; i--) {
|
|
var id = kids[i].id;
|
|
if (this._managed[id] == 1) {
|
|
this.removeChildAt(i);
|
|
delete this._managed[id];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _setState
|
|
* @param {Array} state
|
|
* @param {Number} offset
|
|
* @protected
|
|
**/
|
|
p._setState = function (state, offset) {
|
|
if (!state) {
|
|
return;
|
|
}
|
|
for (var i = state.length - 1; i >= 0; i--) {
|
|
var o = state[i];
|
|
var target = o.t;
|
|
var props = o.p;
|
|
for (var n in props) {
|
|
target[n] = props[n];
|
|
}
|
|
this._addManagedChild(target, offset);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds a child to the timeline, and sets it up as a managed child.
|
|
* @method _addManagedChild
|
|
* @param {MovieClip} child The child MovieClip to manage
|
|
* @param {Number} offset
|
|
* @private
|
|
**/
|
|
p._addManagedChild = function (child, offset) {
|
|
if (child._off) {
|
|
return;
|
|
}
|
|
this.addChildAt(child, 0);
|
|
|
|
if (child instanceof MovieClip) {
|
|
child._synchOffset = offset;
|
|
// TODO: this does not precisely match Flash. Flash loses track of the clip if it is renamed or removed from the timeline, which causes it to reset.
|
|
if (child.mode == MovieClip.INDEPENDENT && child.autoReset && !this._managed[child.id]) {
|
|
child._reset();
|
|
}
|
|
}
|
|
this._managed[child.id] = 2;
|
|
};
|
|
|
|
/**
|
|
* @method _getBounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._getBounds = function (matrix, ignoreTransform) {
|
|
var bounds = this.DisplayObject_getBounds();
|
|
if (!bounds) {
|
|
this._updateTimeline();
|
|
if (this.frameBounds) {
|
|
bounds = this._rectangle.copy(this.frameBounds[this.currentFrame]);
|
|
}
|
|
}
|
|
if (bounds) {
|
|
return this._transformBounds(bounds, matrix, ignoreTransform);
|
|
}
|
|
return this.Container__getBounds(matrix, ignoreTransform);
|
|
};
|
|
|
|
createjs.MovieClip = createjs.promote(MovieClip, "Container");
|
|
|
|
// MovieClipPlugin for TweenJS:
|
|
/**
|
|
* This plugin works with <a href="http://tweenjs.com" target="_blank">TweenJS</a> to prevent the startPosition
|
|
* property from tweening.
|
|
* @private
|
|
* @class MovieClipPlugin
|
|
* @constructor
|
|
**/
|
|
function MovieClipPlugin() {
|
|
throw "MovieClipPlugin cannot be instantiated.";
|
|
}
|
|
|
|
/**
|
|
* @method priority
|
|
* @private
|
|
**/
|
|
MovieClipPlugin.priority = 100; // very high priority, should run first
|
|
|
|
/**
|
|
* @method install
|
|
* @private
|
|
**/
|
|
MovieClipPlugin.install = function () {
|
|
createjs.Tween.installPlugin(MovieClipPlugin, ["startPosition"]);
|
|
};
|
|
|
|
/**
|
|
* @method init
|
|
* @param {Tween} tween
|
|
* @param {String} prop
|
|
* @param {String|Number|Boolean} value
|
|
* @private
|
|
**/
|
|
MovieClipPlugin.init = function (tween, prop, value) {
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* @method step
|
|
* @private
|
|
**/
|
|
MovieClipPlugin.step = function () {
|
|
// unused.
|
|
};
|
|
|
|
/**
|
|
* @method tween
|
|
* @param {Tween} tween
|
|
* @param {String} prop
|
|
* @param {String | Number | Boolean} value
|
|
* @param {Array} startValues
|
|
* @param {Array} endValues
|
|
* @param {Number} ratio
|
|
* @param {Object} wait
|
|
* @param {Object} end
|
|
* @return {*}
|
|
*/
|
|
MovieClipPlugin.tween = function (tween, prop, value, startValues, endValues, ratio, wait, end) {
|
|
if (!(tween.target instanceof MovieClip)) {
|
|
return value;
|
|
}
|
|
return ratio == 1 ? endValues[prop] : startValues[prop];
|
|
};
|
|
})();
|
|
|
|
//##############################################################################
|
|
// SpriteSheetUtils.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s.
|
|
* A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For
|
|
* example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across
|
|
* by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated.
|
|
* @class SpriteSheetUtils
|
|
* @static
|
|
**/
|
|
|
|
function SpriteSheetUtils() {
|
|
throw "SpriteSheetUtils cannot be instantiated";
|
|
}
|
|
|
|
// private static properties:
|
|
/**
|
|
* @property _workingCanvas
|
|
* @static
|
|
* @type HTMLCanvasElement | Object
|
|
* @protected
|
|
*/
|
|
/**
|
|
* @property _workingContext
|
|
* @static
|
|
* @type CanvasRenderingContext2D
|
|
* @protected
|
|
*/
|
|
var canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
if (canvas.getContext) {
|
|
SpriteSheetUtils._workingCanvas = canvas;
|
|
SpriteSheetUtils._workingContext = canvas.getContext("2d");
|
|
canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
// public static methods:
|
|
/**
|
|
* <b>This is an experimental method, and may be buggy. Please report issues.</b><br/><br/>
|
|
* Extends the existing sprite sheet by flipping the original frames horizontally, vertically, or both,
|
|
* and adding appropriate animation & frame data. The flipped animations will have a suffix added to their names
|
|
* (_h, _v, _hv as appropriate). Make sure the sprite sheet images are fully loaded before using this method.
|
|
* <br/><br/>
|
|
* For example:<br/>
|
|
* SpriteSheetUtils.addFlippedFrames(mySpriteSheet, true, true);
|
|
* The above would add frames that are flipped horizontally AND frames that are flipped vertically.
|
|
* <br/><br/>
|
|
* Note that you can also flip any display object by setting its scaleX or scaleY to a negative value. On some
|
|
* browsers (especially those without hardware accelerated canvas) this can result in slightly degraded performance,
|
|
* which is why addFlippedFrames is available.
|
|
* @method addFlippedFrames
|
|
* @static
|
|
* @param {SpriteSheet} spriteSheet
|
|
* @param {Boolean} horizontal If true, horizontally flipped frames will be added.
|
|
* @param {Boolean} vertical If true, vertically flipped frames will be added.
|
|
* @param {Boolean} both If true, frames that are flipped both horizontally and vertically will be added.
|
|
* @deprecated Modern browsers perform better when flipping via a transform (ex. scaleX=-1) rendering this obsolete.
|
|
**/
|
|
SpriteSheetUtils.addFlippedFrames = function (spriteSheet, horizontal, vertical, both) {
|
|
if (!horizontal && !vertical && !both) {
|
|
return;
|
|
}
|
|
|
|
var count = 0;
|
|
if (horizontal) {
|
|
SpriteSheetUtils._flip(spriteSheet, ++count, true, false);
|
|
}
|
|
if (vertical) {
|
|
SpriteSheetUtils._flip(spriteSheet, ++count, false, true);
|
|
}
|
|
if (both) {
|
|
SpriteSheetUtils._flip(spriteSheet, ++count, true, true);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is
|
|
* to use a spritesheet frame as the source for a bitmap fill.
|
|
*
|
|
* <strong>WARNING:</strong> In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}}
|
|
* with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this
|
|
* method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}}
|
|
* property of {{#crossLink "Bitmap"}}{{/crossLink}}.
|
|
*
|
|
* The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas.
|
|
* @method extractFrame
|
|
* @static
|
|
* @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from.
|
|
* @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation
|
|
* name is specified, only the first frame of the animation will be extracted.
|
|
* @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image.
|
|
*/
|
|
SpriteSheetUtils.extractFrame = function (spriteSheet, frameOrAnimation) {
|
|
if (isNaN(frameOrAnimation)) {
|
|
frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0];
|
|
}
|
|
var data = spriteSheet.getFrame(frameOrAnimation);
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
var r = data.rect;
|
|
var canvas = SpriteSheetUtils._workingCanvas;
|
|
canvas.width = r.width;
|
|
canvas.height = r.height;
|
|
SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
|
|
var img = document.createElement("img");
|
|
img.src = canvas.toDataURL("image/png");
|
|
return img;
|
|
};
|
|
|
|
/**
|
|
* Merges the rgb channels of one image with the alpha channel of another. This can be used to combine a compressed
|
|
* JPEG image containing color data with a PNG32 monochromatic image containing alpha data. With certain types of
|
|
* images (those with detail that lend itself to JPEG compression) this can provide significant file size savings
|
|
* versus a single RGBA PNG32. This method is very fast (generally on the order of 1-2 ms to run).
|
|
* @method mergeAlpha
|
|
* @static
|
|
* @param {HTMLImageElement} rbgImage The image (or canvas) containing the RGB channels to use.
|
|
* @param {HTMLImageElement} alphaImage The image (or canvas) containing the alpha channel to use.
|
|
* @param {HTMLCanvasElement} canvas Optional. If specified, this canvas will be used and returned. If not, a new canvas will be created.
|
|
* @return {HTMLCanvasElement} A canvas with the combined image data. This can be used as a source for Bitmap or SpriteSheet.
|
|
* @deprecated Tools such as ImageAlpha generally provide better results. This will be moved to sandbox in the future.
|
|
*/
|
|
SpriteSheetUtils.mergeAlpha = function (rgbImage, alphaImage, canvas) {
|
|
if (!canvas) {
|
|
canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
}
|
|
canvas.width = Math.max(alphaImage.width, rgbImage.width);
|
|
canvas.height = Math.max(alphaImage.height, rgbImage.height);
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.save();
|
|
ctx.drawImage(rgbImage, 0, 0);
|
|
ctx.globalCompositeOperation = "destination-in";
|
|
ctx.drawImage(alphaImage, 0, 0);
|
|
ctx.restore();
|
|
return canvas;
|
|
};
|
|
|
|
// private static methods:
|
|
SpriteSheetUtils._flip = function (spriteSheet, count, h, v) {
|
|
var imgs = spriteSheet._images;
|
|
var canvas = SpriteSheetUtils._workingCanvas;
|
|
var ctx = SpriteSheetUtils._workingContext;
|
|
var il = imgs.length / count;
|
|
for (var i = 0; i < il; i++) {
|
|
var src = imgs[i];
|
|
src.__tmp = i; // a bit hacky, but faster than doing indexOf below.
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, canvas.width + 1, canvas.height + 1);
|
|
canvas.width = src.width;
|
|
canvas.height = src.height;
|
|
ctx.setTransform(h ? -1 : 1, 0, 0, v ? -1 : 1, h ? src.width : 0, v ? src.height : 0);
|
|
ctx.drawImage(src, 0, 0);
|
|
var img = document.createElement("img");
|
|
img.src = canvas.toDataURL("image/png");
|
|
// work around a strange bug in Safari:
|
|
img.width = src.width;
|
|
img.height = src.height;
|
|
imgs.push(img);
|
|
}
|
|
|
|
var frames = spriteSheet._frames;
|
|
var fl = frames.length / count;
|
|
for (i = 0; i < fl; i++) {
|
|
src = frames[i];
|
|
var rect = src.rect.clone();
|
|
img = imgs[src.image.__tmp + il * count];
|
|
|
|
var frame = { image: img, rect: rect, regX: src.regX, regY: src.regY };
|
|
if (h) {
|
|
rect.x = img.width - rect.x - rect.width; // update rect
|
|
frame.regX = rect.width - src.regX; // update registration point
|
|
}
|
|
if (v) {
|
|
rect.y = img.height - rect.y - rect.height; // update rect
|
|
frame.regY = rect.height - src.regY; // update registration point
|
|
}
|
|
frames.push(frame);
|
|
}
|
|
|
|
var sfx = "_" + (h ? "h" : "") + (v ? "v" : "");
|
|
var names = spriteSheet._animations;
|
|
var data = spriteSheet._data;
|
|
var al = names.length / count;
|
|
for (i = 0; i < al; i++) {
|
|
var name = names[i];
|
|
src = data[name];
|
|
var anim = { name: name + sfx, speed: src.speed, next: src.next, frames: [] };
|
|
if (src.next) {
|
|
anim.next += sfx;
|
|
}
|
|
frames = src.frames;
|
|
for (var j = 0, l = frames.length; j < l; j++) {
|
|
anim.frames.push(frames[j] + fl * count);
|
|
}
|
|
data[anim.name] = anim;
|
|
names.push(anim.name);
|
|
}
|
|
};
|
|
|
|
createjs.SpriteSheetUtils = SpriteSheetUtils;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// SpriteSheetBuilder.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* The SpriteSheetBuilder allows you to generate {{#crossLink "SpriteSheet"}}{{/crossLink}} instances at run time
|
|
* from any display object. This can allow you to maintain your assets as vector graphics (for low file size), and
|
|
* render them at run time as SpriteSheets for better performance.
|
|
*
|
|
* SpriteSheets can be built either synchronously, or asynchronously, so that large SpriteSheets can be generated
|
|
* without locking the UI.
|
|
*
|
|
* Note that the "images" used in the generated SpriteSheet are actually canvas elements, and that they will be
|
|
* sized to the nearest power of 2 up to the value of {{#crossLink "SpriteSheetBuilder/maxWidth:property"}}{{/crossLink}}
|
|
* or {{#crossLink "SpriteSheetBuilder/maxHeight:property"}}{{/crossLink}}.
|
|
* @class SpriteSheetBuilder
|
|
* @param {Number} [framerate=0] The {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} of
|
|
* {{#crossLink "SpriteSheet"}}{{/crossLink}} instances that are created.
|
|
* @extends EventDispatcher
|
|
* @constructor
|
|
**/
|
|
|
|
function SpriteSheetBuilder(framerate) {
|
|
this.EventDispatcher_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The maximum width for the images (not individual frames) in the generated SpriteSheet. It is recommended to
|
|
* use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max
|
|
* dimensions, then additional images will be created as needed.
|
|
* @property maxWidth
|
|
* @type Number
|
|
* @default 2048
|
|
*/
|
|
this.maxWidth = 2048;
|
|
|
|
/**
|
|
* The maximum height for the images (not individual frames) in the generated SpriteSheet. It is recommended to
|
|
* use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max
|
|
* dimensions, then additional images will be created as needed.
|
|
* @property maxHeight
|
|
* @type Number
|
|
* @default 2048
|
|
**/
|
|
this.maxHeight = 2048;
|
|
|
|
/**
|
|
* The SpriteSheet that was generated. This will be null before a build is completed successfully.
|
|
* @property spriteSheet
|
|
* @type SpriteSheet
|
|
**/
|
|
this.spriteSheet = null;
|
|
|
|
/**
|
|
* The scale to apply when drawing all frames to the SpriteSheet. This is multiplied against any scale specified
|
|
* in the addFrame call. This can be used, for example, to generate a SpriteSheet at run time that is tailored
|
|
* to the a specific device resolution (ex. tablet vs mobile).
|
|
* @property scale
|
|
* @type Number
|
|
* @default 1
|
|
**/
|
|
this.scale = 1;
|
|
|
|
/**
|
|
* The padding to use between frames. This is helpful to preserve antialiasing on drawn vector content.
|
|
* @property padding
|
|
* @type Number
|
|
* @default 1
|
|
**/
|
|
this.padding = 1;
|
|
|
|
/**
|
|
* A number from 0.01 to 0.99 that indicates what percentage of time the builder can use. This can be
|
|
* thought of as the number of seconds per second the builder will use. For example, with a timeSlice value of 0.3,
|
|
* the builder will run 20 times per second, using approximately 15ms per build (30% of available time, or 0.3s per second).
|
|
* Defaults to 0.3.
|
|
* @property timeSlice
|
|
* @type Number
|
|
* @default 0.3
|
|
**/
|
|
this.timeSlice = 0.3;
|
|
|
|
/**
|
|
* A value between 0 and 1 that indicates the progress of a build, or -1 if a build has not
|
|
* been initiated.
|
|
* @property progress
|
|
* @type Number
|
|
* @default -1
|
|
* @readonly
|
|
*/
|
|
this.progress = -1;
|
|
|
|
/**
|
|
* A {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} value that will be passed to new {{#crossLink "SpriteSheet"}}{{/crossLink}} instances that are
|
|
* created. If no framerate is specified (or it is 0), then SpriteSheets will use the {{#crossLink "Ticker"}}{{/crossLink}}
|
|
* framerate.
|
|
* @property framerate
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.framerate = framerate || 0;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _frames
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._frames = [];
|
|
|
|
/**
|
|
* @property _animations
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._animations = {};
|
|
|
|
/**
|
|
* @property _data
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._data = null;
|
|
|
|
/**
|
|
* @property _nextFrameIndex
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._nextFrameIndex = 0;
|
|
|
|
/**
|
|
* @property _index
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._index = 0;
|
|
|
|
/**
|
|
* @property _timerID
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._timerID = null;
|
|
|
|
/**
|
|
* @property _scale
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._scale = 1;
|
|
}
|
|
var p = createjs.extend(SpriteSheetBuilder, createjs.EventDispatcher);
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// constants:
|
|
SpriteSheetBuilder.ERR_DIMENSIONS = "frame dimensions exceed max spritesheet dimensions";
|
|
SpriteSheetBuilder.ERR_RUNNING = "a build is already running";
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when a build completes.
|
|
* @event complete
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when an asynchronous build has progress.
|
|
* @event progress
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {Number} progress The current progress value (0-1).
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
// public methods:
|
|
/**
|
|
* Adds a frame to the {{#crossLink "SpriteSheet"}}{{/crossLink}}. Note that the frame will not be drawn until you
|
|
* call {{#crossLink "SpriteSheetBuilder/build"}}{{/crossLink}} method. The optional setup params allow you to have
|
|
* a function run immediately before the draw occurs. For example, this allows you to add a single source multiple
|
|
* times, but manipulate it or its children to change it to generate different frames.
|
|
*
|
|
* Note that the source's transformations (x, y, scale, rotate, alpha) will be ignored, except for regX/Y. To apply
|
|
* transforms to a source object and have them captured in the SpriteSheet, simply place it into a {{#crossLink "Container"}}{{/crossLink}}
|
|
* and pass in the Container as the source.
|
|
* @method addFrame
|
|
* @param {DisplayObject} source The source {{#crossLink "DisplayObject"}}{{/crossLink}} to draw as the frame.
|
|
* @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the
|
|
* source to draw to the frame. If not specified, it will look for a `getBounds` method, bounds property, or
|
|
* `nominalBounds` property on the source to use. If one is not found, the frame will be skipped.
|
|
* @param {Number} [scale=1] Optional. The scale to draw this frame at. Default is 1.
|
|
* @param {Function} [setupFunction] A function to call immediately before drawing this frame. It will be called with two parameters: the source, and setupData.
|
|
* @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
|
|
* @return {Number} The index of the frame that was just added, or null if a sourceRect could not be determined.
|
|
**/
|
|
p.addFrame = function (source, sourceRect, scale, setupFunction, setupData) {
|
|
if (this._data) {
|
|
throw SpriteSheetBuilder.ERR_RUNNING;
|
|
}
|
|
var rect = sourceRect || source.bounds || source.nominalBounds;
|
|
if (!rect && source.getBounds) {
|
|
rect = source.getBounds();
|
|
}
|
|
if (!rect) {
|
|
return null;
|
|
}
|
|
scale = scale || 1;
|
|
return this._frames.push({ source: source, sourceRect: rect, scale: scale, funct: setupFunction, data: setupData, index: this._frames.length, height: rect.height * scale }) - 1;
|
|
};
|
|
|
|
/**
|
|
* Adds an animation that will be included in the created {{#crossLink "SpriteSheet"}}{{/crossLink}}.
|
|
* @method addAnimation
|
|
* @param {String} name The name for the animation.
|
|
* @param {Array} frames An array of frame indexes that comprise the animation. Ex. [3,6,5] would describe an animation
|
|
* that played frame indexes 3, 6, and 5 in that order.
|
|
* @param {String} [next] Specifies the name of the animation to continue to after this animation ends. You can
|
|
* also pass false to have the animation stop when it ends. By default it will loop to the start of the same animation.
|
|
* @param {Number} [speed] Specifies a frame advance speed for this animation. For example, a value of 0.5 would
|
|
* cause the animation to advance every second tick. Note that earlier versions used `frequency` instead, which had
|
|
* the opposite effect.
|
|
**/
|
|
p.addAnimation = function (name, frames, next, speed) {
|
|
if (this._data) {
|
|
throw SpriteSheetBuilder.ERR_RUNNING;
|
|
}
|
|
this._animations[name] = { frames: frames, next: next, speed: speed };
|
|
};
|
|
|
|
/**
|
|
* This will take a {{#crossLink "MovieClip"}}{{/crossLink}} instance, and add its frames and labels to this
|
|
* builder. Labels will be added as an animation running from the label index to the next label. For example, if
|
|
* there is a label named "foo" at frame 0 and a label named "bar" at frame 10, in a MovieClip with 15 frames, it
|
|
* will add an animation named "foo" that runs from frame index 0 to 9, and an animation named "bar" that runs from
|
|
* frame index 10 to 14.
|
|
*
|
|
* Note that this will iterate through the full MovieClip with {{#crossLink "MovieClip/actionsEnabled:property"}}{{/crossLink}}
|
|
* set to `false`, ending on the last frame.
|
|
* @method addMovieClip
|
|
* @param {MovieClip} source The source MovieClip instance to add to the SpriteSheet.
|
|
* @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the source to
|
|
* draw to the frame. If not specified, it will look for a {{#crossLink "DisplayObject/getBounds"}}{{/crossLink}}
|
|
* method, `frameBounds` Array, `bounds` property, or `nominalBounds` property on the source to use. If one is not
|
|
* found, the MovieClip will be skipped.
|
|
* @param {Number} [scale=1] The scale to draw the movie clip at.
|
|
* @param {Function} [setupFunction] A function to call immediately before drawing each frame. It will be called
|
|
* with three parameters: the source, setupData, and the frame index.
|
|
* @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
|
|
* @param {Function} [labelFunction] This method will be called for each MovieClip label that is added with four
|
|
* parameters: the label name, the source MovieClip instance, the starting frame index (in the movieclip timeline)
|
|
* and the end index. It must return a new name for the label/animation, or `false` to exclude the label.
|
|
**/
|
|
p.addMovieClip = function (source, sourceRect, scale, setupFunction, setupData, labelFunction) {
|
|
if (this._data) {
|
|
throw SpriteSheetBuilder.ERR_RUNNING;
|
|
}
|
|
var rects = source.frameBounds;
|
|
var rect = sourceRect || source.bounds || source.nominalBounds;
|
|
if (!rect && source.getBounds) {
|
|
rect = source.getBounds();
|
|
}
|
|
if (!rect && !rects) {
|
|
return;
|
|
}
|
|
|
|
var i,
|
|
l,
|
|
baseFrameIndex = this._frames.length;
|
|
var duration = source.timeline.duration;
|
|
for (i = 0; i < duration; i++) {
|
|
var r = rects && rects[i] ? rects[i] : rect;
|
|
this.addFrame(source, r, scale, this._setupMovieClipFrame, { i: i, f: setupFunction, d: setupData });
|
|
}
|
|
var labels = source.timeline._labels;
|
|
var lbls = [];
|
|
for (var n in labels) {
|
|
lbls.push({ index: labels[n], label: n });
|
|
}
|
|
if (lbls.length) {
|
|
lbls.sort(function (a, b) {
|
|
return a.index - b.index;
|
|
});
|
|
for (i = 0, l = lbls.length; i < l; i++) {
|
|
var label = lbls[i].label;
|
|
var start = baseFrameIndex + lbls[i].index;
|
|
var end = baseFrameIndex + (i == l - 1 ? duration : lbls[i + 1].index);
|
|
var frames = [];
|
|
for (var j = start; j < end; j++) {
|
|
frames.push(j);
|
|
}
|
|
if (labelFunction) {
|
|
label = labelFunction(label, source, start, end);
|
|
if (!label) {
|
|
continue;
|
|
}
|
|
}
|
|
this.addAnimation(label, frames, true); // for now, this loops all animations.
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Builds a {{#crossLink "SpriteSheet"}}{{/crossLink}} instance based on the current frames.
|
|
* @method build
|
|
* @return {SpriteSheet} The created SpriteSheet instance, or null if a build is already running or an error
|
|
* occurred.
|
|
**/
|
|
p.build = function () {
|
|
if (this._data) {
|
|
throw SpriteSheetBuilder.ERR_RUNNING;
|
|
}
|
|
this._startBuild();
|
|
while (this._drawNext()) {}
|
|
this._endBuild();
|
|
return this.spriteSheet;
|
|
};
|
|
|
|
/**
|
|
* Asynchronously builds a {{#crossLink "SpriteSheet"}}{{/crossLink}} instance based on the current frames. It will
|
|
* run 20 times per second, using an amount of time defined by `timeSlice`. When it is complete it will call the
|
|
* specified callback.
|
|
* @method buildAsync
|
|
* @param {Number} [timeSlice] Sets the timeSlice property on this instance.
|
|
**/
|
|
p.buildAsync = function (timeSlice) {
|
|
if (this._data) {
|
|
throw SpriteSheetBuilder.ERR_RUNNING;
|
|
}
|
|
this.timeSlice = timeSlice;
|
|
this._startBuild();
|
|
var _this = this;
|
|
this._timerID = setTimeout(function () {
|
|
_this._run();
|
|
}, 50 - Math.max(0.01, Math.min(0.99, this.timeSlice || 0.3)) * 50);
|
|
};
|
|
|
|
/**
|
|
* Stops the current asynchronous build.
|
|
* @method stopAsync
|
|
**/
|
|
p.stopAsync = function () {
|
|
clearTimeout(this._timerID);
|
|
this._data = null;
|
|
};
|
|
|
|
/**
|
|
* SpriteSheetBuilder instances cannot be cloned.
|
|
* @method clone
|
|
**/
|
|
p.clone = function () {
|
|
throw "SpriteSheetBuilder cannot be cloned.";
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[SpriteSheetBuilder]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _startBuild
|
|
* @protected
|
|
**/
|
|
p._startBuild = function () {
|
|
var pad = this.padding || 0;
|
|
this.progress = 0;
|
|
this.spriteSheet = null;
|
|
this._index = 0;
|
|
this._scale = this.scale;
|
|
var dataFrames = [];
|
|
this._data = {
|
|
images: [],
|
|
frames: dataFrames,
|
|
framerate: this.framerate,
|
|
animations: this._animations // TODO: should we "clone" _animations in case someone adds more animations after a build?
|
|
};
|
|
|
|
var frames = this._frames.slice();
|
|
frames.sort(function (a, b) {
|
|
return a.height <= b.height ? -1 : 1;
|
|
});
|
|
|
|
if (frames[frames.length - 1].height + pad * 2 > this.maxHeight) {
|
|
throw SpriteSheetBuilder.ERR_DIMENSIONS;
|
|
}
|
|
var y = 0,
|
|
x = 0;
|
|
var img = 0;
|
|
while (frames.length) {
|
|
var o = this._fillRow(frames, y, img, dataFrames, pad);
|
|
if (o.w > x) {
|
|
x = o.w;
|
|
}
|
|
y += o.h;
|
|
if (!o.h || !frames.length) {
|
|
var canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
canvas.width = this._getSize(x, this.maxWidth);
|
|
canvas.height = this._getSize(y, this.maxHeight);
|
|
this._data.images[img] = canvas;
|
|
if (!o.h) {
|
|
x = y = 0;
|
|
img++;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _setupMovieClipFrame
|
|
* @protected
|
|
* @return {Number} The width & height of the row.
|
|
**/
|
|
p._setupMovieClipFrame = function (source, data) {
|
|
var ae = source.actionsEnabled;
|
|
source.actionsEnabled = false;
|
|
source.gotoAndStop(data.i);
|
|
source.actionsEnabled = ae;
|
|
data.f && data.f(source, data.d, data.i);
|
|
};
|
|
|
|
/**
|
|
* @method _getSize
|
|
* @protected
|
|
* @return {Number} The width & height of the row.
|
|
**/
|
|
p._getSize = function (size, max) {
|
|
var pow = 4;
|
|
while (Math.pow(2, ++pow) < size) {}
|
|
return Math.min(max, Math.pow(2, pow));
|
|
};
|
|
|
|
/**
|
|
* @method _fillRow
|
|
* @param {Array} frames
|
|
* @param {Number} y
|
|
* @param {HTMLImageElement} img
|
|
* @param {Object} dataFrames
|
|
* @param {Number} pad
|
|
* @protected
|
|
* @return {Number} The width & height of the row.
|
|
**/
|
|
p._fillRow = function (frames, y, img, dataFrames, pad) {
|
|
var w = this.maxWidth;
|
|
var maxH = this.maxHeight;
|
|
y += pad;
|
|
var h = maxH - y;
|
|
var x = pad;
|
|
var height = 0;
|
|
for (var i = frames.length - 1; i >= 0; i--) {
|
|
var frame = frames[i];
|
|
var sc = this._scale * frame.scale;
|
|
var rect = frame.sourceRect;
|
|
var source = frame.source;
|
|
var rx = Math.floor(sc * rect.x - pad);
|
|
var ry = Math.floor(sc * rect.y - pad);
|
|
var rh = Math.ceil(sc * rect.height + pad * 2);
|
|
var rw = Math.ceil(sc * rect.width + pad * 2);
|
|
if (rw > w) {
|
|
throw SpriteSheetBuilder.ERR_DIMENSIONS;
|
|
}
|
|
if (rh > h || x + rw > w) {
|
|
continue;
|
|
}
|
|
frame.img = img;
|
|
frame.rect = new createjs.Rectangle(x, y, rw, rh);
|
|
height = height || rh;
|
|
frames.splice(i, 1);
|
|
dataFrames[frame.index] = [x, y, rw, rh, img, Math.round(-rx + sc * source.regX - pad), Math.round(-ry + sc * source.regY - pad)];
|
|
x += rw;
|
|
}
|
|
return { w: x, h: height };
|
|
};
|
|
|
|
/**
|
|
* @method _endBuild
|
|
* @protected
|
|
**/
|
|
p._endBuild = function () {
|
|
this.spriteSheet = new createjs.SpriteSheet(this._data);
|
|
this._data = null;
|
|
this.progress = 1;
|
|
this.dispatchEvent("complete");
|
|
};
|
|
|
|
/**
|
|
* @method _run
|
|
* @protected
|
|
**/
|
|
p._run = function () {
|
|
var ts = Math.max(0.01, Math.min(0.99, this.timeSlice || 0.3)) * 50;
|
|
var t = new Date().getTime() + ts;
|
|
var complete = false;
|
|
while (t > new Date().getTime()) {
|
|
if (!this._drawNext()) {
|
|
complete = true;break;
|
|
}
|
|
}
|
|
if (complete) {
|
|
this._endBuild();
|
|
} else {
|
|
var _this = this;
|
|
this._timerID = setTimeout(function () {
|
|
_this._run();
|
|
}, 50 - ts);
|
|
}
|
|
var p = this.progress = this._index / this._frames.length;
|
|
if (this.hasEventListener("progress")) {
|
|
var evt = new createjs.Event("progress");
|
|
evt.progress = p;
|
|
this.dispatchEvent(evt);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _drawNext
|
|
* @protected
|
|
* @return Boolean Returns false if this is the last draw.
|
|
**/
|
|
p._drawNext = function () {
|
|
var frame = this._frames[this._index];
|
|
var sc = frame.scale * this._scale;
|
|
var rect = frame.rect;
|
|
var sourceRect = frame.sourceRect;
|
|
var canvas = this._data.images[frame.img];
|
|
var ctx = canvas.getContext("2d");
|
|
frame.funct && frame.funct(frame.source, frame.data);
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
ctx.clip();
|
|
ctx.translate(Math.ceil(rect.x - sourceRect.x * sc), Math.ceil(rect.y - sourceRect.y * sc));
|
|
ctx.scale(sc, sc);
|
|
frame.source.draw(ctx); // display object will draw itself.
|
|
ctx.restore();
|
|
return ++this._index < this._frames.length;
|
|
};
|
|
|
|
createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// DOMElement.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* <b>This class is still experimental, and more advanced use is likely to be buggy. Please report bugs.</b>
|
|
*
|
|
* A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed
|
|
* within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is
|
|
* not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be
|
|
* drawn in front of or behind the canvas).
|
|
*
|
|
* The position of a DOMElement is relative to their parent node in the DOM. It is recommended that
|
|
* the DOM Object be added to a div that also contains the canvas so that they share the same position
|
|
* on the page.
|
|
*
|
|
* DOMElement is useful for positioning HTML elements over top of canvas content, and for elements
|
|
* that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML
|
|
* content.
|
|
*
|
|
* <h4>Mouse Interaction</h4>
|
|
*
|
|
* DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse
|
|
* events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to
|
|
* the htmlElement (note, this does not support EventDispatcher)
|
|
*
|
|
* var domElement = new createjs.DOMElement(htmlElement);
|
|
* domElement.htmlElement.onclick = function() {
|
|
* console.log("clicked");
|
|
* }
|
|
*
|
|
* @class DOMElement
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {HTMLElement} htmlElement A reference or id for the DOM element to manage.
|
|
*/
|
|
|
|
function DOMElement(htmlElement) {
|
|
this.DisplayObject_constructor();
|
|
|
|
if (typeof htmlElement == "string") {
|
|
htmlElement = document.getElementById(htmlElement);
|
|
}
|
|
this.mouseEnabled = false;
|
|
|
|
var style = htmlElement.style;
|
|
style.position = "absolute";
|
|
style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%";
|
|
|
|
// public properties:
|
|
/**
|
|
* The DOM object to manage.
|
|
* @property htmlElement
|
|
* @type HTMLElement
|
|
*/
|
|
this.htmlElement = htmlElement;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _oldMtx
|
|
* @type Matrix2D
|
|
* @protected
|
|
*/
|
|
this._oldProps = null;
|
|
}
|
|
var p = createjs.extend(DOMElement, createjs.DisplayObject);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
*/
|
|
p.isVisible = function () {
|
|
return this.htmlElement != null;
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
* @return {Boolean}
|
|
*/
|
|
p.draw = function (ctx, ignoreCache) {
|
|
// this relies on the _tick method because draw isn't called if the parent is not visible.
|
|
// the actual update happens in _handleDrawEnd
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method cache
|
|
*/
|
|
p.cache = function () {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method uncache
|
|
*/
|
|
p.uncache = function () {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method updateCache
|
|
*/
|
|
p.updateCache = function () {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method hitTest
|
|
*/
|
|
p.hitTest = function () {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method localToGlobal
|
|
*/
|
|
p.localToGlobal = function () {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method globalToLocal
|
|
*/
|
|
p.globalToLocal = function () {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method localToLocal
|
|
*/
|
|
p.localToLocal = function () {};
|
|
|
|
/**
|
|
* DOMElement cannot be cloned. Throws an error.
|
|
* @method clone
|
|
*/
|
|
p.clone = function () {
|
|
throw "DOMElement cannot be cloned.";
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
*/
|
|
p.toString = function () {
|
|
return "[DOMElement (name=" + this.name + ")]";
|
|
};
|
|
|
|
/**
|
|
* Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
|
|
* are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event click
|
|
*/
|
|
|
|
/**
|
|
* Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
|
|
* are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event dblClick
|
|
*/
|
|
|
|
/**
|
|
* Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
|
|
* are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event mousedown
|
|
*/
|
|
|
|
/**
|
|
* The HTMLElement can listen for the mouseover event, not the DOMElement instance.
|
|
* Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event mouseover
|
|
*/
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @event tick
|
|
*/
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* function.
|
|
* @protected
|
|
*/
|
|
p._tick = function (evtObj) {
|
|
var stage = this.getStage();
|
|
stage && stage.on("drawend", this._handleDrawEnd, this, true);
|
|
this.DisplayObject__tick(evtObj);
|
|
};
|
|
|
|
/**
|
|
* @method _handleDrawEnd
|
|
* @param {Event} evt
|
|
* @protected
|
|
*/
|
|
p._handleDrawEnd = function (evt) {
|
|
var o = this.htmlElement;
|
|
if (!o) {
|
|
return;
|
|
}
|
|
var style = o.style;
|
|
|
|
var props = this.getConcatenatedDisplayProps(this._props),
|
|
mtx = props.matrix;
|
|
|
|
var visibility = props.visible ? "visible" : "hidden";
|
|
if (visibility != style.visibility) {
|
|
style.visibility = visibility;
|
|
}
|
|
if (!props.visible) {
|
|
return;
|
|
}
|
|
|
|
var oldProps = this._oldProps,
|
|
oldMtx = oldProps && oldProps.matrix;
|
|
var n = 10000; // precision
|
|
|
|
if (!oldMtx || !oldMtx.equals(mtx)) {
|
|
var str = "matrix(" + (mtx.a * n | 0) / n + "," + (mtx.b * n | 0) / n + "," + (mtx.c * n | 0) / n + "," + (mtx.d * n | 0) / n + "," + (mtx.tx + 0.5 | 0);
|
|
style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str + "," + (mtx.ty + 0.5 | 0) + ")";
|
|
style.MozTransform = str + "px," + (mtx.ty + 0.5 | 0) + "px)";
|
|
if (!oldProps) {
|
|
oldProps = this._oldProps = new createjs.DisplayProps(true, NaN);
|
|
}
|
|
oldProps.matrix.copy(mtx);
|
|
}
|
|
|
|
if (oldProps.alpha != props.alpha) {
|
|
style.opacity = "" + (props.alpha * n | 0) / n;
|
|
oldProps.alpha = props.alpha;
|
|
}
|
|
};
|
|
|
|
createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Filter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Base class that all filters should inherit from. Filters need to be applied to objects that have been cached using
|
|
* the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method. If an object changes, please cache it again, or use
|
|
* {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* myInstance.filters = [
|
|
* new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0),
|
|
* new createjs.BlurFilter(5, 5, 10)
|
|
* ];
|
|
* myInstance.cache(0,0, 100, 100);
|
|
*
|
|
* Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the
|
|
* margins that need to be applied in order to fully display the filter. For example, the {{#crossLink "BlurFilter"}}{{/crossLink}}
|
|
* will cause an object to feather outwards, resulting in a margin around the shape.
|
|
*
|
|
* <h4>EaselJS Filters</h4>
|
|
* EaselJS comes with a number of pre-built filters:
|
|
* <ul><li>{{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object</li>
|
|
* <li>{{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object</li>
|
|
* <li>{{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object</li>
|
|
* <li>{{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object</li>
|
|
* <li>{{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}</li>
|
|
* </ul>
|
|
*
|
|
* @class Filter
|
|
* @constructor
|
|
**/
|
|
|
|
function Filter() {}
|
|
var p = Filter.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Provides padding values for this filter. That is, how much the filter will extend the visual bounds of an object it is applied to.
|
|
* @method getBounds
|
|
* @param {Rectangle} [rect] If specified, the provided Rectangle instance will be expanded by the padding amounts and returned.
|
|
* @return {Rectangle} If a `rect` param was provided, it is returned. If not, either a new rectangle with the padding values, or null if no padding is required for this filter.
|
|
**/
|
|
p.getBounds = function (rect) {
|
|
return rect;
|
|
};
|
|
|
|
/**
|
|
* Applies the filter to the specified context.
|
|
* @method applyFilter
|
|
* @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
|
|
* @param {Number} x The x position to use for the source rect.
|
|
* @param {Number} y The y position to use for the source rect.
|
|
* @param {Number} width The width to use for the source rect.
|
|
* @param {Number} height The height to use for the source rect.
|
|
* @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
|
|
* @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
|
|
* @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
|
|
* @return {Boolean} If the filter was applied successfully.
|
|
**/
|
|
p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
|
|
// this is the default behaviour because most filters access pixel data. It is overridden when not needed.
|
|
targetCtx = targetCtx || ctx;
|
|
if (targetX == null) {
|
|
targetX = x;
|
|
}
|
|
if (targetY == null) {
|
|
targetY = y;
|
|
}
|
|
try {
|
|
var imageData = ctx.getImageData(x, y, width, height);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
if (this._applyFilter(imageData)) {
|
|
targetCtx.putImageData(imageData, targetX, targetY);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[Filter]";
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Filter instance.
|
|
* @method clone
|
|
* @return {Filter} A clone of the current Filter instance.
|
|
**/
|
|
p.clone = function () {
|
|
return new Filter();
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _applyFilter
|
|
* @param {ImageData} imageData Target ImageData instance.
|
|
* @return {Boolean}
|
|
**/
|
|
p._applyFilter = function (imageData) {
|
|
return true;
|
|
};
|
|
|
|
createjs.Filter = Filter;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// BlurFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies a box blur to DisplayObjects. Note that this filter is fairly CPU intensive, particularly if the quality is
|
|
* set higher than 1.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}}
|
|
* method to account for the spread that the blur causes.
|
|
*
|
|
* var shape = new createjs.Shape().set({x:100,y:100});
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
|
|
*
|
|
* var blurFilter = new createjs.BlurFilter(5, 5, 1);
|
|
* shape.filters = [blurFilter];
|
|
* var bounds = blurFilter.getBounds();
|
|
*
|
|
* shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
|
|
* @class BlurFilter
|
|
* @extends Filter
|
|
* @constructor
|
|
* @param {Number} [blurX=0] The horizontal blur radius in pixels.
|
|
* @param {Number} [blurY=0] The vertical blur radius in pixels.
|
|
* @param {Number} [quality=1] The number of blur iterations.
|
|
**/
|
|
|
|
function BlurFilter(blurX, blurY, quality) {
|
|
if (isNaN(blurX) || blurX < 0) blurX = 0;
|
|
if (isNaN(blurY) || blurY < 0) blurY = 0;
|
|
if (isNaN(quality) || quality < 1) quality = 1;
|
|
|
|
// public properties:
|
|
/**
|
|
* Horizontal blur radius in pixels
|
|
* @property blurX
|
|
* @default 0
|
|
* @type Number
|
|
**/
|
|
this.blurX = blurX | 0;
|
|
|
|
/**
|
|
* Vertical blur radius in pixels
|
|
* @property blurY
|
|
* @default 0
|
|
* @type Number
|
|
**/
|
|
this.blurY = blurY | 0;
|
|
|
|
/**
|
|
* Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a
|
|
* smoother blur, but take twice as long to run.
|
|
* @property quality
|
|
* @default 1
|
|
* @type Number
|
|
**/
|
|
this.quality = quality | 0;
|
|
}
|
|
var p = createjs.extend(BlurFilter, createjs.Filter);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Array of multiply values for blur calculations.
|
|
* @property MUL_TABLE
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
BlurFilter.MUL_TABLE = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1];
|
|
|
|
/**
|
|
* Array of shift values for blur calculations.
|
|
* @property SHG_TABLE
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
BlurFilter.SHG_TABLE = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9];
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.getBounds = function (rect) {
|
|
var x = this.blurX | 0,
|
|
y = this.blurY | 0;
|
|
if (x <= 0 && y <= 0) {
|
|
return rect;
|
|
}
|
|
var q = Math.pow(this.quality, 0.2);
|
|
return (rect || new createjs.Rectangle()).pad(x * q + 1, y * q + 1, x * q + 1, y * q + 1);
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
return new BlurFilter(this.blurX, this.blurY, this.quality);
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[BlurFilter]";
|
|
};
|
|
|
|
// private methods:
|
|
|
|
/** docced in super class **/
|
|
p._applyFilter = function (imageData) {
|
|
|
|
var radiusX = this.blurX >> 1;
|
|
if (isNaN(radiusX) || radiusX < 0) return false;
|
|
var radiusY = this.blurY >> 1;
|
|
if (isNaN(radiusY) || radiusY < 0) return false;
|
|
if (radiusX == 0 && radiusY == 0) return false;
|
|
|
|
var iterations = this.quality;
|
|
if (isNaN(iterations) || iterations < 1) iterations = 1;
|
|
iterations |= 0;
|
|
if (iterations > 3) iterations = 3;
|
|
if (iterations < 1) iterations = 1;
|
|
|
|
var px = imageData.data;
|
|
var x = 0,
|
|
y = 0,
|
|
i = 0,
|
|
p = 0,
|
|
yp = 0,
|
|
yi = 0,
|
|
yw = 0,
|
|
r = 0,
|
|
g = 0,
|
|
b = 0,
|
|
a = 0,
|
|
pr = 0,
|
|
pg = 0,
|
|
pb = 0,
|
|
pa = 0;
|
|
|
|
var divx = radiusX + radiusX + 1 | 0;
|
|
var divy = radiusY + radiusY + 1 | 0;
|
|
var w = imageData.width | 0;
|
|
var h = imageData.height | 0;
|
|
|
|
var w1 = w - 1 | 0;
|
|
var h1 = h - 1 | 0;
|
|
var rxp1 = radiusX + 1 | 0;
|
|
var ryp1 = radiusY + 1 | 0;
|
|
|
|
var ssx = { r: 0, b: 0, g: 0, a: 0 };
|
|
var sx = ssx;
|
|
for (i = 1; i < divx; i++) {
|
|
sx = sx.n = { r: 0, b: 0, g: 0, a: 0 };
|
|
}
|
|
sx.n = ssx;
|
|
|
|
var ssy = { r: 0, b: 0, g: 0, a: 0 };
|
|
var sy = ssy;
|
|
for (i = 1; i < divy; i++) {
|
|
sy = sy.n = { r: 0, b: 0, g: 0, a: 0 };
|
|
}
|
|
sy.n = ssy;
|
|
|
|
var si = null;
|
|
|
|
var mtx = BlurFilter.MUL_TABLE[radiusX] | 0;
|
|
var stx = BlurFilter.SHG_TABLE[radiusX] | 0;
|
|
var mty = BlurFilter.MUL_TABLE[radiusY] | 0;
|
|
var sty = BlurFilter.SHG_TABLE[radiusY] | 0;
|
|
|
|
while (iterations-- > 0) {
|
|
|
|
yw = yi = 0;
|
|
var ms = mtx;
|
|
var ss = stx;
|
|
for (y = h; --y > -1;) {
|
|
r = rxp1 * (pr = px[yi | 0]);
|
|
g = rxp1 * (pg = px[yi + 1 | 0]);
|
|
b = rxp1 * (pb = px[yi + 2 | 0]);
|
|
a = rxp1 * (pa = px[yi + 3 | 0]);
|
|
|
|
sx = ssx;
|
|
|
|
for (i = rxp1; --i > -1;) {
|
|
sx.r = pr;
|
|
sx.g = pg;
|
|
sx.b = pb;
|
|
sx.a = pa;
|
|
sx = sx.n;
|
|
}
|
|
|
|
for (i = 1; i < rxp1; i++) {
|
|
p = yi + ((w1 < i ? w1 : i) << 2) | 0;
|
|
r += sx.r = px[p];
|
|
g += sx.g = px[p + 1];
|
|
b += sx.b = px[p + 2];
|
|
a += sx.a = px[p + 3];
|
|
|
|
sx = sx.n;
|
|
}
|
|
|
|
si = ssx;
|
|
for (x = 0; x < w; x++) {
|
|
px[yi++] = r * ms >>> ss;
|
|
px[yi++] = g * ms >>> ss;
|
|
px[yi++] = b * ms >>> ss;
|
|
px[yi++] = a * ms >>> ss;
|
|
|
|
p = yw + ((p = x + radiusX + 1) < w1 ? p : w1) << 2;
|
|
|
|
r -= si.r - (si.r = px[p]);
|
|
g -= si.g - (si.g = px[p + 1]);
|
|
b -= si.b - (si.b = px[p + 2]);
|
|
a -= si.a - (si.a = px[p + 3]);
|
|
|
|
si = si.n;
|
|
}
|
|
yw += w;
|
|
}
|
|
|
|
ms = mty;
|
|
ss = sty;
|
|
for (x = 0; x < w; x++) {
|
|
yi = x << 2 | 0;
|
|
|
|
r = ryp1 * (pr = px[yi]) | 0;
|
|
g = ryp1 * (pg = px[yi + 1 | 0]) | 0;
|
|
b = ryp1 * (pb = px[yi + 2 | 0]) | 0;
|
|
a = ryp1 * (pa = px[yi + 3 | 0]) | 0;
|
|
|
|
sy = ssy;
|
|
for (i = 0; i < ryp1; i++) {
|
|
sy.r = pr;
|
|
sy.g = pg;
|
|
sy.b = pb;
|
|
sy.a = pa;
|
|
sy = sy.n;
|
|
}
|
|
|
|
yp = w;
|
|
|
|
for (i = 1; i <= radiusY; i++) {
|
|
yi = yp + x << 2;
|
|
|
|
r += sy.r = px[yi];
|
|
g += sy.g = px[yi + 1];
|
|
b += sy.b = px[yi + 2];
|
|
a += sy.a = px[yi + 3];
|
|
|
|
sy = sy.n;
|
|
|
|
if (i < h1) {
|
|
yp += w;
|
|
}
|
|
}
|
|
|
|
yi = x;
|
|
si = ssy;
|
|
if (iterations > 0) {
|
|
for (y = 0; y < h; y++) {
|
|
p = yi << 2;
|
|
px[p + 3] = pa = a * ms >>> ss;
|
|
if (pa > 0) {
|
|
px[p] = r * ms >>> ss;
|
|
px[p + 1] = g * ms >>> ss;
|
|
px[p + 2] = b * ms >>> ss;
|
|
} else {
|
|
px[p] = px[p + 1] = px[p + 2] = 0;
|
|
}
|
|
|
|
p = x + ((p = y + ryp1) < h1 ? p : h1) * w << 2;
|
|
|
|
r -= si.r - (si.r = px[p]);
|
|
g -= si.g - (si.g = px[p + 1]);
|
|
b -= si.b - (si.b = px[p + 2]);
|
|
a -= si.a - (si.a = px[p + 3]);
|
|
|
|
si = si.n;
|
|
|
|
yi += w;
|
|
}
|
|
} else {
|
|
for (y = 0; y < h; y++) {
|
|
p = yi << 2;
|
|
px[p + 3] = pa = a * ms >>> ss;
|
|
if (pa > 0) {
|
|
pa = 255 / pa;
|
|
px[p] = (r * ms >>> ss) * pa;
|
|
px[p + 1] = (g * ms >>> ss) * pa;
|
|
px[p + 2] = (b * ms >>> ss) * pa;
|
|
} else {
|
|
px[p] = px[p + 1] = px[p + 2] = 0;
|
|
}
|
|
|
|
p = x + ((p = y + ryp1) < h1 ? p : h1) * w << 2;
|
|
|
|
r -= si.r - (si.r = px[p]);
|
|
g -= si.g - (si.g = px[p + 1]);
|
|
b -= si.b - (si.b = px[p + 2]);
|
|
a -= si.a - (si.a = px[p + 3]);
|
|
|
|
si = si.n;
|
|
|
|
yi += w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
createjs.BlurFilter = createjs.promote(BlurFilter, "Filter");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// AlphaMapFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will
|
|
* be copied from the red channel of the map, and the RGB channels will be copied from the target.
|
|
*
|
|
* Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much
|
|
* better performance.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image.
|
|
*
|
|
* var box = new createjs.Shape();
|
|
* box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100)
|
|
* box.graphics.drawRect(0, 0, 100, 100);
|
|
* box.cache(0, 0, 100, 100);
|
|
*
|
|
* var bmp = new createjs.Bitmap("path/to/image.jpg");
|
|
* bmp.filters = [
|
|
* new createjs.AlphaMapFilter(box.cacheCanvas)
|
|
* ];
|
|
* bmp.cache(0, 0, 100, 100);
|
|
* stage.addChild(bmp);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
|
|
* @class AlphaMapFilter
|
|
* @extends Filter
|
|
* @constructor
|
|
* @param {HTMLImageElement|HTMLCanvasElement} alphaMap The greyscale image (or canvas) to use as the alpha value for the
|
|
* result. This should be exactly the same dimensions as the target.
|
|
**/
|
|
|
|
function AlphaMapFilter(alphaMap) {
|
|
|
|
// public properties:
|
|
/**
|
|
* The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same
|
|
* dimensions as the target.
|
|
* @property alphaMap
|
|
* @type HTMLImageElement|HTMLCanvasElement
|
|
**/
|
|
this.alphaMap = alphaMap;
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _alphaMap
|
|
* @protected
|
|
* @type HTMLImageElement|HTMLCanvasElement
|
|
**/
|
|
this._alphaMap = null;
|
|
|
|
/**
|
|
* @property _mapData
|
|
* @protected
|
|
* @type Uint8ClampedArray
|
|
**/
|
|
this._mapData = null;
|
|
}
|
|
var p = createjs.extend(AlphaMapFilter, createjs.Filter);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
var o = new AlphaMapFilter(this.alphaMap);
|
|
o._alphaMap = this._alphaMap;
|
|
o._mapData = this._mapData;
|
|
return o;
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[AlphaMapFilter]";
|
|
};
|
|
|
|
// private methods:
|
|
/** docced in super class **/
|
|
p._applyFilter = function (imageData) {
|
|
if (!this.alphaMap) {
|
|
return true;
|
|
}
|
|
if (!this._prepAlphaMap()) {
|
|
return false;
|
|
}
|
|
|
|
// TODO: update to support scenarios where the target has different dimensions.
|
|
var data = imageData.data;
|
|
var map = this._mapData;
|
|
for (var i = 0, l = data.length; i < l; i += 4) {
|
|
data[i + 3] = map[i] || 0;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @method _prepAlphaMap
|
|
* @protected
|
|
**/
|
|
p._prepAlphaMap = function () {
|
|
if (!this.alphaMap) {
|
|
return false;
|
|
}
|
|
if (this.alphaMap == this._alphaMap && this._mapData) {
|
|
return true;
|
|
}
|
|
|
|
this._mapData = null;
|
|
var map = this._alphaMap = this.alphaMap;
|
|
var canvas = map;
|
|
var ctx;
|
|
if (map instanceof HTMLCanvasElement) {
|
|
ctx = canvas.getContext("2d");
|
|
} else {
|
|
canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
canvas.width = map.width;
|
|
canvas.height = map.height;
|
|
ctx = canvas.getContext("2d");
|
|
ctx.drawImage(map, 0, 0);
|
|
}
|
|
|
|
try {
|
|
var imgData = ctx.getImageData(0, 0, map.width, map.height);
|
|
} catch (e) {
|
|
//if (!this.suppressCrossDomainErrors) throw new Error("unable to access local image data: " + e);
|
|
return false;
|
|
}
|
|
|
|
this._mapData = imgData.data;
|
|
return true;
|
|
};
|
|
|
|
createjs.AlphaMapFilter = createjs.promote(AlphaMapFilter, "Filter");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// AlphaMaskFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies the alpha from the mask image (or canvas) to the target, such that the alpha channel of the result will
|
|
* be derived from the mask, and the RGB channels will be copied from the target. This can be used, for example, to
|
|
* apply an alpha mask to a display object. This can also be used to combine a JPG compressed RGB image with a PNG32
|
|
* alpha mask, which can result in a much smaller file size than a single PNG32 containing ARGB.
|
|
*
|
|
* <b>IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly.</b>
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image.
|
|
*
|
|
* var box = new createjs.Shape();
|
|
* box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100)
|
|
* box.graphics.drawRect(0, 0, 100, 100);
|
|
* box.cache(0, 0, 100, 100);
|
|
*
|
|
* var bmp = new createjs.Bitmap("path/to/image.jpg");
|
|
* bmp.filters = [
|
|
* new createjs.AlphaMaskFilter(box.cacheCanvas)
|
|
* ];
|
|
* bmp.cache(0, 0, 100, 100);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
|
|
* @class AlphaMaskFilter
|
|
* @extends Filter
|
|
* @constructor
|
|
* @param {HTMLImageElement|HTMLCanvasElement} mask
|
|
**/
|
|
|
|
function AlphaMaskFilter(mask) {
|
|
|
|
// public properties:
|
|
/**
|
|
* The image (or canvas) to use as the mask.
|
|
* @property mask
|
|
* @type HTMLImageElement|HTMLCanvasElement
|
|
**/
|
|
this.mask = mask;
|
|
}
|
|
var p = createjs.extend(AlphaMaskFilter, createjs.Filter);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Applies the filter to the specified context.
|
|
*
|
|
* <strong>IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters
|
|
* correctly.</strong>
|
|
* @method applyFilter
|
|
* @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
|
|
* @param {Number} x The x position to use for the source rect.
|
|
* @param {Number} y The y position to use for the source rect.
|
|
* @param {Number} width The width to use for the source rect.
|
|
* @param {Number} height The height to use for the source rect.
|
|
* @param {CanvasRenderingContext2D} [targetCtx] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx.
|
|
* @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x.
|
|
* @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y.
|
|
* @return {Boolean} If the filter was applied successfully.
|
|
**/
|
|
p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
|
|
if (!this.mask) {
|
|
return true;
|
|
}
|
|
targetCtx = targetCtx || ctx;
|
|
if (targetX == null) {
|
|
targetX = x;
|
|
}
|
|
if (targetY == null) {
|
|
targetY = y;
|
|
}
|
|
|
|
targetCtx.save();
|
|
if (ctx != targetCtx) {
|
|
// TODO: support targetCtx and targetX/Y
|
|
// clearRect, then draw the ctx in?
|
|
return false;
|
|
}
|
|
|
|
targetCtx.globalCompositeOperation = "destination-in";
|
|
targetCtx.drawImage(this.mask, targetX, targetY);
|
|
targetCtx.restore();
|
|
return true;
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
return new AlphaMaskFilter(this.mask);
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[AlphaMaskFilter]";
|
|
};
|
|
|
|
createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// ColorFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies a color transform to DisplayObjects.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels
|
|
* to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel.
|
|
*
|
|
* var shape = new createjs.Shape().set({x:100,y:100});
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
|
|
*
|
|
* shape.filters = [
|
|
* new createjs.ColorFilter(0,0,0,1, 0,0,255,0)
|
|
* ];
|
|
* shape.cache(-50, -50, 100, 100);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
|
|
* @class ColorFilter
|
|
* @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1.
|
|
* @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1.
|
|
* @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1.
|
|
* @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1.
|
|
* @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @constructor
|
|
* @extends Filter
|
|
**/
|
|
|
|
function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) {
|
|
|
|
// public properties:
|
|
/**
|
|
* Red channel multiplier.
|
|
* @property redMultiplier
|
|
* @type Number
|
|
**/
|
|
this.redMultiplier = redMultiplier != null ? redMultiplier : 1;
|
|
|
|
/**
|
|
* Green channel multiplier.
|
|
* @property greenMultiplier
|
|
* @type Number
|
|
**/
|
|
this.greenMultiplier = greenMultiplier != null ? greenMultiplier : 1;
|
|
|
|
/**
|
|
* Blue channel multiplier.
|
|
* @property blueMultiplier
|
|
* @type Number
|
|
**/
|
|
this.blueMultiplier = blueMultiplier != null ? blueMultiplier : 1;
|
|
|
|
/**
|
|
* Alpha channel multiplier.
|
|
* @property alphaMultiplier
|
|
* @type Number
|
|
**/
|
|
this.alphaMultiplier = alphaMultiplier != null ? alphaMultiplier : 1;
|
|
|
|
/**
|
|
* Red channel offset (added to value).
|
|
* @property redOffset
|
|
* @type Number
|
|
**/
|
|
this.redOffset = redOffset || 0;
|
|
|
|
/**
|
|
* Green channel offset (added to value).
|
|
* @property greenOffset
|
|
* @type Number
|
|
**/
|
|
this.greenOffset = greenOffset || 0;
|
|
|
|
/**
|
|
* Blue channel offset (added to value).
|
|
* @property blueOffset
|
|
* @type Number
|
|
**/
|
|
this.blueOffset = blueOffset || 0;
|
|
|
|
/**
|
|
* Alpha channel offset (added to value).
|
|
* @property alphaOffset
|
|
* @type Number
|
|
**/
|
|
this.alphaOffset = alphaOffset || 0;
|
|
}
|
|
var p = createjs.extend(ColorFilter, createjs.Filter);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[ColorFilter]";
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
return new ColorFilter(this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset);
|
|
};
|
|
|
|
// private methods:
|
|
/** docced in super class **/
|
|
p._applyFilter = function (imageData) {
|
|
var data = imageData.data;
|
|
var l = data.length;
|
|
for (var i = 0; i < l; i += 4) {
|
|
data[i] = data[i] * this.redMultiplier + this.redOffset;
|
|
data[i + 1] = data[i + 1] * this.greenMultiplier + this.greenOffset;
|
|
data[i + 2] = data[i + 2] * this.blueMultiplier + this.blueOffset;
|
|
data[i + 3] = data[i + 3] * this.alphaMultiplier + this.alphaOffset;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
createjs.ColorFilter = createjs.promote(ColorFilter, "Filter");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// ColorMatrix.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Provides helper functions for assembling a matrix for use with the {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}.
|
|
* Most methods return the instance to facilitate chained calls.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* myColorMatrix.adjustHue(20).adjustBrightness(50);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}
|
|
* for an example of how to use ColorMatrix to change a DisplayObject's color.
|
|
* @class ColorMatrix
|
|
* @param {Number} brightness
|
|
* @param {Number} contrast
|
|
* @param {Number} saturation
|
|
* @param {Number} hue
|
|
* @constructor
|
|
**/
|
|
|
|
function ColorMatrix(brightness, contrast, saturation, hue) {
|
|
this.setColor(brightness, contrast, saturation, hue);
|
|
}
|
|
var p = ColorMatrix.prototype;
|
|
|
|
/**
|
|
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
|
|
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
|
|
* for details.
|
|
*
|
|
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
|
|
*
|
|
* @method initialize
|
|
* @protected
|
|
* @deprecated
|
|
*/
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is.
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Array of delta values for contrast calculations.
|
|
* @property DELTA_INDEX
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
ColorMatrix.DELTA_INDEX = [0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, 10.0];
|
|
|
|
/**
|
|
* Identity matrix values.
|
|
* @property IDENTITY_MATRIX
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
ColorMatrix.IDENTITY_MATRIX = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1];
|
|
|
|
/**
|
|
* The constant length of a color matrix.
|
|
* @property LENGTH
|
|
* @type Number
|
|
* @protected
|
|
* @static
|
|
**/
|
|
ColorMatrix.LENGTH = ColorMatrix.IDENTITY_MATRIX.length;
|
|
|
|
// public methods:
|
|
/**
|
|
* Resets the instance with the specified values.
|
|
* @method setColor
|
|
* @param {Number} brightness
|
|
* @param {Number} contrast
|
|
* @param {Number} saturation
|
|
* @param {Number} hue
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
*/
|
|
p.setColor = function (brightness, contrast, saturation, hue) {
|
|
return this.reset().adjustColor(brightness, contrast, saturation, hue);
|
|
};
|
|
|
|
/**
|
|
* Resets the matrix to identity values.
|
|
* @method reset
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
*/
|
|
p.reset = function () {
|
|
return this.copy(ColorMatrix.IDENTITY_MATRIX);
|
|
};
|
|
|
|
/**
|
|
* Shortcut method to adjust brightness, contrast, saturation and hue.
|
|
* Equivalent to calling adjustHue(hue), adjustContrast(contrast),
|
|
* adjustBrightness(brightness), adjustSaturation(saturation), in that order.
|
|
* @method adjustColor
|
|
* @param {Number} brightness
|
|
* @param {Number} contrast
|
|
* @param {Number} saturation
|
|
* @param {Number} hue
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.adjustColor = function (brightness, contrast, saturation, hue) {
|
|
this.adjustHue(hue);
|
|
this.adjustContrast(contrast);
|
|
this.adjustBrightness(brightness);
|
|
return this.adjustSaturation(saturation);
|
|
};
|
|
|
|
/**
|
|
* Adjusts the brightness of pixel color by adding the specified value to the red, green and blue channels.
|
|
* Positive values will make the image brighter, negative values will make it darker.
|
|
* @method adjustBrightness
|
|
* @param {Number} value A value between -255 & 255 that will be added to the RGB channels.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.adjustBrightness = function (value) {
|
|
if (value == 0 || isNaN(value)) {
|
|
return this;
|
|
}
|
|
value = this._cleanValue(value, 255);
|
|
this._multiplyMatrix([1, 0, 0, 0, value, 0, 1, 0, 0, value, 0, 0, 1, 0, value, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adjusts the contrast of pixel color.
|
|
* Positive values will increase contrast, negative values will decrease contrast.
|
|
* @method adjustContrast
|
|
* @param {Number} value A value between -100 & 100.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.adjustContrast = function (value) {
|
|
if (value == 0 || isNaN(value)) {
|
|
return this;
|
|
}
|
|
value = this._cleanValue(value, 100);
|
|
var x;
|
|
if (value < 0) {
|
|
x = 127 + value / 100 * 127;
|
|
} else {
|
|
x = value % 1;
|
|
if (x == 0) {
|
|
x = ColorMatrix.DELTA_INDEX[value];
|
|
} else {
|
|
x = ColorMatrix.DELTA_INDEX[value << 0] * (1 - x) + ColorMatrix.DELTA_INDEX[(value << 0) + 1] * x; // use linear interpolation for more granularity.
|
|
}
|
|
x = x * 127 + 127;
|
|
}
|
|
this._multiplyMatrix([x / 127, 0, 0, 0, 0.5 * (127 - x), 0, x / 127, 0, 0, 0.5 * (127 - x), 0, 0, x / 127, 0, 0.5 * (127 - x), 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adjusts the color saturation of the pixel.
|
|
* Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale).
|
|
* @method adjustSaturation
|
|
* @param {Number} value A value between -100 & 100.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.adjustSaturation = function (value) {
|
|
if (value == 0 || isNaN(value)) {
|
|
return this;
|
|
}
|
|
value = this._cleanValue(value, 100);
|
|
var x = 1 + (value > 0 ? 3 * value / 100 : value / 100);
|
|
var lumR = 0.3086;
|
|
var lumG = 0.6094;
|
|
var lumB = 0.0820;
|
|
this._multiplyMatrix([lumR * (1 - x) + x, lumG * (1 - x), lumB * (1 - x), 0, 0, lumR * (1 - x), lumG * (1 - x) + x, lumB * (1 - x), 0, 0, lumR * (1 - x), lumG * (1 - x), lumB * (1 - x) + x, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adjusts the hue of the pixel color.
|
|
* @method adjustHue
|
|
* @param {Number} value A value between -180 & 180.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.adjustHue = function (value) {
|
|
if (value == 0 || isNaN(value)) {
|
|
return this;
|
|
}
|
|
value = this._cleanValue(value, 180) / 180 * Math.PI;
|
|
var cosVal = Math.cos(value);
|
|
var sinVal = Math.sin(value);
|
|
var lumR = 0.213;
|
|
var lumG = 0.715;
|
|
var lumB = 0.072;
|
|
this._multiplyMatrix([lumR + cosVal * (1 - lumR) + sinVal * -lumR, lumG + cosVal * -lumG + sinVal * -lumG, lumB + cosVal * -lumB + sinVal * (1 - lumB), 0, 0, lumR + cosVal * -lumR + sinVal * 0.143, lumG + cosVal * (1 - lumG) + sinVal * 0.140, lumB + cosVal * -lumB + sinVal * -0.283, 0, 0, lumR + cosVal * -lumR + sinVal * -(1 - lumR), lumG + cosVal * -lumG + sinVal * lumG, lumB + cosVal * (1 - lumB) + sinVal * lumB, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Concatenates (multiplies) the specified matrix with this one.
|
|
* @method concat
|
|
* @param {Array} matrix An array or ColorMatrix instance.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.concat = function (matrix) {
|
|
matrix = this._fixMatrix(matrix);
|
|
if (matrix.length != ColorMatrix.LENGTH) {
|
|
return this;
|
|
}
|
|
this._multiplyMatrix(matrix);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this ColorMatrix.
|
|
* @method clone
|
|
* @return {ColorMatrix} A clone of this ColorMatrix.
|
|
**/
|
|
p.clone = function () {
|
|
return new ColorMatrix().copy(this);
|
|
};
|
|
|
|
/**
|
|
* Return a length 25 (5x5) array instance containing this matrix's values.
|
|
* @method toArray
|
|
* @return {Array} An array holding this matrix's values.
|
|
**/
|
|
p.toArray = function () {
|
|
var arr = [];
|
|
for (var i = 0, l = ColorMatrix.LENGTH; i < l; i++) {
|
|
arr[i] = this[i];
|
|
}
|
|
return arr;
|
|
};
|
|
|
|
/**
|
|
* Copy the specified matrix's values to this matrix.
|
|
* @method copy
|
|
* @param {Array} matrix An array or ColorMatrix instance.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
* @chainable
|
|
**/
|
|
p.copy = function (matrix) {
|
|
var l = ColorMatrix.LENGTH;
|
|
for (var i = 0; i < l; i++) {
|
|
this[i] = matrix[i];
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function () {
|
|
return "[ColorMatrix]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _multiplyMatrix
|
|
* @param {Array} matrix
|
|
* @protected
|
|
**/
|
|
p._multiplyMatrix = function (matrix) {
|
|
var i,
|
|
j,
|
|
k,
|
|
col = [];
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
for (j = 0; j < 5; j++) {
|
|
col[j] = this[j + i * 5];
|
|
}
|
|
for (j = 0; j < 5; j++) {
|
|
var val = 0;
|
|
for (k = 0; k < 5; k++) {
|
|
val += matrix[j + k * 5] * col[k];
|
|
}
|
|
this[j + i * 5] = val;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Make sure values are within the specified range, hue has a limit of 180, brightness is 255, others are 100.
|
|
* @method _cleanValue
|
|
* @param {Number} value The raw number
|
|
* @param {Number} limit The maximum that the number can be. The minimum is the limit * -1.
|
|
* @protected
|
|
**/
|
|
p._cleanValue = function (value, limit) {
|
|
return Math.min(limit, Math.max(-limit, value));
|
|
};
|
|
|
|
/**
|
|
* Makes sure matrixes are 5x5 (25 long).
|
|
* @method _fixMatrix
|
|
* @param {Array} matrix
|
|
* @protected
|
|
**/
|
|
p._fixMatrix = function (matrix) {
|
|
if (matrix instanceof ColorMatrix) {
|
|
matrix = matrix.toArray();
|
|
}
|
|
if (matrix.length < ColorMatrix.LENGTH) {
|
|
matrix = matrix.slice(0, matrix.length).concat(ColorMatrix.IDENTITY_MATRIX.slice(matrix.length, ColorMatrix.LENGTH));
|
|
} else if (matrix.length > ColorMatrix.LENGTH) {
|
|
matrix = matrix.slice(0, ColorMatrix.LENGTH);
|
|
}
|
|
return matrix;
|
|
};
|
|
|
|
createjs.ColorMatrix = ColorMatrix;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// ColorMatrixFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the
|
|
* {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform,
|
|
* consider the {{#crossLink "ColorFilter"}}{{/crossLink}}.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example creates a red circle, inverts its hue, and then saturates it to brighten it up.
|
|
*
|
|
* var shape = new createjs.Shape().set({x:100,y:100});
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
|
|
*
|
|
* var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100);
|
|
* shape.filters = [
|
|
* new createjs.ColorMatrixFilter(matrix)
|
|
* ];
|
|
*
|
|
* shape.cache(-50, -50, 100, 100);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
|
|
* @class ColorMatrixFilter
|
|
* @constructor
|
|
* @extends Filter
|
|
* @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
|
|
* class.
|
|
**/
|
|
|
|
function ColorMatrixFilter(matrix) {
|
|
|
|
// public properties:
|
|
/**
|
|
* A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
|
|
* @property matrix
|
|
* @type Array | ColorMatrix
|
|
**/
|
|
this.matrix = matrix;
|
|
}
|
|
var p = createjs.extend(ColorMatrixFilter, createjs.Filter);
|
|
|
|
// TODO: deprecated
|
|
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
|
|
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[ColorMatrixFilter]";
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
return new ColorMatrixFilter(this.matrix);
|
|
};
|
|
|
|
// private methods:
|
|
/** docced in super class **/
|
|
p._applyFilter = function (imageData) {
|
|
var data = imageData.data;
|
|
var l = data.length;
|
|
var r, g, b, a;
|
|
var mtx = this.matrix;
|
|
var m0 = mtx[0],
|
|
m1 = mtx[1],
|
|
m2 = mtx[2],
|
|
m3 = mtx[3],
|
|
m4 = mtx[4];
|
|
var m5 = mtx[5],
|
|
m6 = mtx[6],
|
|
m7 = mtx[7],
|
|
m8 = mtx[8],
|
|
m9 = mtx[9];
|
|
var m10 = mtx[10],
|
|
m11 = mtx[11],
|
|
m12 = mtx[12],
|
|
m13 = mtx[13],
|
|
m14 = mtx[14];
|
|
var m15 = mtx[15],
|
|
m16 = mtx[16],
|
|
m17 = mtx[17],
|
|
m18 = mtx[18],
|
|
m19 = mtx[19];
|
|
|
|
for (var i = 0; i < l; i += 4) {
|
|
r = data[i];
|
|
g = data[i + 1];
|
|
b = data[i + 2];
|
|
a = data[i + 3];
|
|
data[i] = r * m0 + g * m1 + b * m2 + a * m3 + m4; // red
|
|
data[i + 1] = r * m5 + g * m6 + b * m7 + a * m8 + m9; // green
|
|
data[i + 2] = r * m10 + g * m11 + b * m12 + a * m13 + m14; // blue
|
|
data[i + 3] = r * m15 + g * m16 + b * m17 + a * m18 + m19; // alpha
|
|
}
|
|
return true;
|
|
};
|
|
|
|
createjs.ColorMatrixFilter = createjs.promote(ColorMatrixFilter, "Filter");
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Touch.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* Global utility for working with multi-touch enabled devices in EaselJS. Currently supports W3C Touch API (iOS and
|
|
* modern Android browser) and the Pointer API (IE), including ms-prefixed events in IE10, and unprefixed in IE11.
|
|
*
|
|
* Ensure that you {{#crossLink "Touch/disable"}}{{/crossLink}} touch when cleaning up your application. You do not have
|
|
* to check if touch is supported to enable it, as it will fail gracefully if it is not supported.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var stage = new createjs.Stage("canvasId");
|
|
* createjs.Touch.enable(stage);
|
|
*
|
|
* <strong>Note:</strong> It is important to disable Touch on a stage that you are no longer using:
|
|
*
|
|
* createjs.Touch.disable(stage);
|
|
*
|
|
* @class Touch
|
|
* @static
|
|
**/
|
|
|
|
function Touch() {
|
|
throw "Touch cannot be instantiated";
|
|
}
|
|
|
|
// public static methods:
|
|
/**
|
|
* Returns `true` if touch is supported in the current browser.
|
|
* @method isSupported
|
|
* @return {Boolean} Indicates whether touch is supported in the current browser.
|
|
* @static
|
|
**/
|
|
Touch.isSupported = function () {
|
|
return !!('ontouchstart' in window || // iOS & Android
|
|
window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0 // IE10
|
|
|| window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0); // IE11+
|
|
};
|
|
|
|
/**
|
|
* Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
|
|
* (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
|
|
* multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
|
|
* double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
|
|
* for more information.
|
|
* @method enable
|
|
* @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
|
|
* @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
|
|
* @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
|
|
* allowed when the user is interacting with the target canvas.
|
|
* @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
|
|
* @static
|
|
**/
|
|
Touch.enable = function (stage, singleTouch, allowDefault) {
|
|
if (!stage || !stage.canvas || !Touch.isSupported()) {
|
|
return false;
|
|
}
|
|
if (stage.__touch) {
|
|
return true;
|
|
}
|
|
|
|
// inject required properties on stage:
|
|
stage.__touch = { pointers: {}, multitouch: !singleTouch, preventDefault: !allowDefault, count: 0 };
|
|
|
|
// note that in the future we may need to disable the standard mouse event model before adding
|
|
// these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
|
|
if ('ontouchstart' in window) {
|
|
Touch._IOS_enable(stage);
|
|
} else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) {
|
|
Touch._IE_enable(stage);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Removes all listeners that were set up when calling `Touch.enable()` on a stage.
|
|
* @method disable
|
|
* @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on.
|
|
* @static
|
|
**/
|
|
Touch.disable = function (stage) {
|
|
if (!stage) {
|
|
return;
|
|
}
|
|
if ('ontouchstart' in window) {
|
|
Touch._IOS_disable(stage);
|
|
} else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) {
|
|
Touch._IE_disable(stage);
|
|
}
|
|
|
|
delete stage.__touch;
|
|
};
|
|
|
|
// Private static methods:
|
|
/**
|
|
* @method _IOS_enable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IOS_enable = function (stage) {
|
|
var canvas = stage.canvas;
|
|
var f = stage.__touch.f = function (e) {
|
|
Touch._IOS_handleEvent(stage, e);
|
|
};
|
|
canvas.addEventListener("touchstart", f, false);
|
|
canvas.addEventListener("touchmove", f, false);
|
|
canvas.addEventListener("touchend", f, false);
|
|
canvas.addEventListener("touchcancel", f, false);
|
|
};
|
|
|
|
/**
|
|
* @method _IOS_disable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IOS_disable = function (stage) {
|
|
var canvas = stage.canvas;
|
|
if (!canvas) {
|
|
return;
|
|
}
|
|
var f = stage.__touch.f;
|
|
canvas.removeEventListener("touchstart", f, false);
|
|
canvas.removeEventListener("touchmove", f, false);
|
|
canvas.removeEventListener("touchend", f, false);
|
|
canvas.removeEventListener("touchcancel", f, false);
|
|
};
|
|
|
|
/**
|
|
* @method _IOS_handleEvent
|
|
* @param {Stage} stage
|
|
* @param {Object} e The event to handle
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Touch._IOS_handleEvent = function (stage, e) {
|
|
if (!stage) {
|
|
return;
|
|
}
|
|
if (stage.__touch.preventDefault) {
|
|
e.preventDefault && e.preventDefault();
|
|
}
|
|
var touches = e.changedTouches;
|
|
var type = e.type;
|
|
for (var i = 0, l = touches.length; i < l; i++) {
|
|
var touch = touches[i];
|
|
var id = touch.identifier;
|
|
if (touch.target != stage.canvas) {
|
|
continue;
|
|
}
|
|
|
|
if (type == "touchstart") {
|
|
this._handleStart(stage, id, e, touch.pageX, touch.pageY);
|
|
} else if (type == "touchmove") {
|
|
this._handleMove(stage, id, e, touch.pageX, touch.pageY);
|
|
} else if (type == "touchend" || type == "touchcancel") {
|
|
this._handleEnd(stage, id, e);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _IE_enable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IE_enable = function (stage) {
|
|
var canvas = stage.canvas;
|
|
var f = stage.__touch.f = function (e) {
|
|
Touch._IE_handleEvent(stage, e);
|
|
};
|
|
|
|
if (window.navigator["pointerEnabled"] === undefined) {
|
|
canvas.addEventListener("MSPointerDown", f, false);
|
|
window.addEventListener("MSPointerMove", f, false);
|
|
window.addEventListener("MSPointerUp", f, false);
|
|
window.addEventListener("MSPointerCancel", f, false);
|
|
if (stage.__touch.preventDefault) {
|
|
canvas.style.msTouchAction = "none";
|
|
}
|
|
} else {
|
|
canvas.addEventListener("pointerdown", f, false);
|
|
window.addEventListener("pointermove", f, false);
|
|
window.addEventListener("pointerup", f, false);
|
|
window.addEventListener("pointercancel", f, false);
|
|
if (stage.__touch.preventDefault) {
|
|
canvas.style.touchAction = "none";
|
|
}
|
|
}
|
|
stage.__touch.activeIDs = {};
|
|
};
|
|
|
|
/**
|
|
* @method _IE_disable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IE_disable = function (stage) {
|
|
var f = stage.__touch.f;
|
|
|
|
if (window.navigator["pointerEnabled"] === undefined) {
|
|
window.removeEventListener("MSPointerMove", f, false);
|
|
window.removeEventListener("MSPointerUp", f, false);
|
|
window.removeEventListener("MSPointerCancel", f, false);
|
|
if (stage.canvas) {
|
|
stage.canvas.removeEventListener("MSPointerDown", f, false);
|
|
}
|
|
} else {
|
|
window.removeEventListener("pointermove", f, false);
|
|
window.removeEventListener("pointerup", f, false);
|
|
window.removeEventListener("pointercancel", f, false);
|
|
if (stage.canvas) {
|
|
stage.canvas.removeEventListener("pointerdown", f, false);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _IE_handleEvent
|
|
* @param {Stage} stage
|
|
* @param {Object} e The event to handle.
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Touch._IE_handleEvent = function (stage, e) {
|
|
if (!stage) {
|
|
return;
|
|
}
|
|
if (stage.__touch.preventDefault) {
|
|
e.preventDefault && e.preventDefault();
|
|
}
|
|
var type = e.type;
|
|
var id = e.pointerId;
|
|
var ids = stage.__touch.activeIDs;
|
|
|
|
if (type == "MSPointerDown" || type == "pointerdown") {
|
|
if (e.srcElement != stage.canvas) {
|
|
return;
|
|
}
|
|
ids[id] = true;
|
|
this._handleStart(stage, id, e, e.pageX, e.pageY);
|
|
} else if (ids[id]) {
|
|
// it's an id we're watching
|
|
if (type == "MSPointerMove" || type == "pointermove") {
|
|
this._handleMove(stage, id, e, e.pageX, e.pageY);
|
|
} else if (type == "MSPointerUp" || type == "MSPointerCancel" || type == "pointerup" || type == "pointercancel") {
|
|
delete ids[id];
|
|
this._handleEnd(stage, id, e);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleStart
|
|
* @param {Stage} stage
|
|
* @param {String|Number} id
|
|
* @param {Object} e
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @protected
|
|
**/
|
|
Touch._handleStart = function (stage, id, e, x, y) {
|
|
var props = stage.__touch;
|
|
if (!props.multitouch && props.count) {
|
|
return;
|
|
}
|
|
var ids = props.pointers;
|
|
if (ids[id]) {
|
|
return;
|
|
}
|
|
ids[id] = true;
|
|
props.count++;
|
|
stage._handlePointerDown(id, e, x, y);
|
|
};
|
|
|
|
/**
|
|
* @method _handleMove
|
|
* @param {Stage} stage
|
|
* @param {String|Number} id
|
|
* @param {Object} e
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @protected
|
|
**/
|
|
Touch._handleMove = function (stage, id, e, x, y) {
|
|
if (!stage.__touch.pointers[id]) {
|
|
return;
|
|
}
|
|
stage._handlePointerMove(id, e, x, y);
|
|
};
|
|
|
|
/**
|
|
* @method _handleEnd
|
|
* @param {Stage} stage
|
|
* @param {String|Number} id
|
|
* @param {Object} e
|
|
* @protected
|
|
**/
|
|
Touch._handleEnd = function (stage, id, e) {
|
|
// TODO: cancel should be handled differently for proper UI (ex. an up would trigger a click, a cancel would more closely resemble an out).
|
|
var props = stage.__touch;
|
|
var ids = props.pointers;
|
|
if (!ids[id]) {
|
|
return;
|
|
}
|
|
props.count--;
|
|
stage._handlePointerUp(id, e, true);
|
|
delete ids[id];
|
|
};
|
|
|
|
createjs.Touch = Touch;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// version.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
/**
|
|
* Static class holding library specific information such as the version and buildDate of
|
|
* the library.
|
|
* @class EaselJS
|
|
**/
|
|
|
|
var s = createjs.EaselJS = createjs.EaselJS || {};
|
|
|
|
/**
|
|
* The version string for this release.
|
|
* @property version
|
|
* @type String
|
|
* @static
|
|
**/
|
|
s.version = /*=version*/"0.8.2"; // injected by build process
|
|
|
|
/**
|
|
* The build date for this release in UTC format.
|
|
* @property buildDate
|
|
* @type String
|
|
* @static
|
|
**/
|
|
s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:34 GMT"; // injected by build process
|
|
})();
|
|
|
|
/*** EXPORTS FROM exports-loader ***/
|
|
module.exports = window.createjs;
|
|
}).call(window);
|
|
|
|
/***/ }),
|
|
/* 3 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
|
|
* jQuery JavaScript Library v2.2.4
|
|
* http://jquery.com/
|
|
*
|
|
* Includes Sizzle.js
|
|
* http://sizzlejs.com/
|
|
*
|
|
* Copyright jQuery Foundation and other contributors
|
|
* Released under the MIT license
|
|
* http://jquery.org/license
|
|
*
|
|
* Date: 2016-05-20T17:23Z
|
|
*/
|
|
|
|
(function( global, factory ) {
|
|
|
|
if ( typeof module === "object" && typeof module.exports === "object" ) {
|
|
// For CommonJS and CommonJS-like environments where a proper `window`
|
|
// is present, execute the factory and get jQuery.
|
|
// For environments that do not have a `window` with a `document`
|
|
// (such as Node.js), expose a 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.document ?
|
|
factory( global, true ) :
|
|
function( w ) {
|
|
if ( !w.document ) {
|
|
throw new Error( "jQuery requires a window with a document" );
|
|
}
|
|
return factory( w );
|
|
};
|
|
} else {
|
|
factory( global );
|
|
}
|
|
|
|
// Pass this if window is not defined yet
|
|
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
|
|
|
|
// Support: Firefox 18+
|
|
// Can't be in strict mode, several libs including ASP.NET trace
|
|
// the stack via arguments.caller.callee and Firefox dies if
|
|
// you try to trace through "use strict" call chains. (#13335)
|
|
//"use strict";
|
|
var arr = [];
|
|
|
|
var document = window.document;
|
|
|
|
var slice = arr.slice;
|
|
|
|
var concat = arr.concat;
|
|
|
|
var push = arr.push;
|
|
|
|
var indexOf = arr.indexOf;
|
|
|
|
var class2type = {};
|
|
|
|
var toString = class2type.toString;
|
|
|
|
var hasOwn = class2type.hasOwnProperty;
|
|
|
|
var support = {};
|
|
|
|
|
|
|
|
var
|
|
version = "2.2.4",
|
|
|
|
// Define a local copy of jQuery
|
|
jQuery = function( selector, context ) {
|
|
|
|
// The jQuery object is actually just the init constructor 'enhanced'
|
|
// Need init if jQuery is called (just allow error to be thrown if not included)
|
|
return new jQuery.fn.init( selector, context );
|
|
},
|
|
|
|
// Support: Android<4.1
|
|
// Make sure we trim BOM and NBSP
|
|
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
|
|
|
|
// Matches dashed string for camelizing
|
|
rmsPrefix = /^-ms-/,
|
|
rdashAlpha = /-([\da-z])/gi,
|
|
|
|
// Used by jQuery.camelCase as callback to replace()
|
|
fcamelCase = function( all, letter ) {
|
|
return letter.toUpperCase();
|
|
};
|
|
|
|
jQuery.fn = jQuery.prototype = {
|
|
|
|
// The current version of jQuery being used
|
|
jquery: version,
|
|
|
|
constructor: jQuery,
|
|
|
|
// Start with an empty selector
|
|
selector: "",
|
|
|
|
// The default length of a jQuery object is 0
|
|
length: 0,
|
|
|
|
toArray: function() {
|
|
return slice.call( this );
|
|
},
|
|
|
|
// Get the Nth element in the matched element set OR
|
|
// Get the whole matched element set as a clean array
|
|
get: function( num ) {
|
|
return num != null ?
|
|
|
|
// Return just the one element from the set
|
|
( num < 0 ? this[ num + this.length ] : this[ num ] ) :
|
|
|
|
// Return all the elements in a clean array
|
|
slice.call( this );
|
|
},
|
|
|
|
// Take an array of elements and push it onto the stack
|
|
// (returning the new matched element set)
|
|
pushStack: function( elems ) {
|
|
|
|
// Build a new jQuery matched element set
|
|
var ret = jQuery.merge( this.constructor(), elems );
|
|
|
|
// Add the old object onto the stack (as a reference)
|
|
ret.prevObject = this;
|
|
ret.context = this.context;
|
|
|
|
// Return the newly-formed element set
|
|
return ret;
|
|
},
|
|
|
|
// Execute a callback for every element in the matched set.
|
|
each: function( callback ) {
|
|
return jQuery.each( this, callback );
|
|
},
|
|
|
|
map: function( callback ) {
|
|
return this.pushStack( jQuery.map( this, function( elem, i ) {
|
|
return callback.call( elem, i, elem );
|
|
} ) );
|
|
},
|
|
|
|
slice: function() {
|
|
return this.pushStack( slice.apply( this, arguments ) );
|
|
},
|
|
|
|
first: function() {
|
|
return this.eq( 0 );
|
|
},
|
|
|
|
last: function() {
|
|
return this.eq( -1 );
|
|
},
|
|
|
|
eq: function( i ) {
|
|
var len = this.length,
|
|
j = +i + ( i < 0 ? len : 0 );
|
|
return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
|
|
},
|
|
|
|
end: function() {
|
|
return this.prevObject || this.constructor();
|
|
},
|
|
|
|
// For internal use only.
|
|
// Behaves like an Array's method, not like a jQuery method.
|
|
push: push,
|
|
sort: arr.sort,
|
|
splice: arr.splice
|
|
};
|
|
|
|
jQuery.extend = jQuery.fn.extend = function() {
|
|
var options, name, src, copy, copyIsArray, clone,
|
|
target = arguments[ 0 ] || {},
|
|
i = 1,
|
|
length = arguments.length,
|
|
deep = false;
|
|
|
|
// Handle a deep copy situation
|
|
if ( typeof target === "boolean" ) {
|
|
deep = target;
|
|
|
|
// Skip the boolean and the target
|
|
target = arguments[ i ] || {};
|
|
i++;
|
|
}
|
|
|
|
// Handle case when target is a string or something (possible in deep copy)
|
|
if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
|
|
target = {};
|
|
}
|
|
|
|
// Extend jQuery itself if only one argument is passed
|
|
if ( i === length ) {
|
|
target = this;
|
|
i--;
|
|
}
|
|
|
|
for ( ; i < length; i++ ) {
|
|
|
|
// Only deal with non-null/undefined values
|
|
if ( ( options = arguments[ i ] ) != null ) {
|
|
|
|
// Extend the base object
|
|
for ( name in options ) {
|
|
src = target[ name ];
|
|
copy = options[ name ];
|
|
|
|
// Prevent never-ending loop
|
|
if ( target === copy ) {
|
|
continue;
|
|
}
|
|
|
|
// Recurse if we're merging plain objects or arrays
|
|
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
|
|
( copyIsArray = jQuery.isArray( copy ) ) ) ) {
|
|
|
|
if ( copyIsArray ) {
|
|
copyIsArray = false;
|
|
clone = src && jQuery.isArray( src ) ? src : [];
|
|
|
|
} else {
|
|
clone = src && jQuery.isPlainObject( src ) ? src : {};
|
|
}
|
|
|
|
// Never move original objects, clone them
|
|
target[ name ] = jQuery.extend( deep, clone, copy );
|
|
|
|
// Don't bring in undefined values
|
|
} else if ( copy !== undefined ) {
|
|
target[ name ] = copy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the modified object
|
|
return target;
|
|
};
|
|
|
|
jQuery.extend( {
|
|
|
|
// Unique for each copy of jQuery on the page
|
|
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
|
|
|
|
// Assume jQuery is ready without the ready module
|
|
isReady: true,
|
|
|
|
error: function( msg ) {
|
|
throw new Error( msg );
|
|
},
|
|
|
|
noop: function() {},
|
|
|
|
isFunction: function( obj ) {
|
|
return jQuery.type( obj ) === "function";
|
|
},
|
|
|
|
isArray: Array.isArray,
|
|
|
|
isWindow: function( obj ) {
|
|
return obj != null && obj === obj.window;
|
|
},
|
|
|
|
isNumeric: function( obj ) {
|
|
|
|
// parseFloat NaNs numeric-cast false positives (null|true|false|"")
|
|
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
|
|
// subtraction forces infinities to NaN
|
|
// adding 1 corrects loss of precision from parseFloat (#15100)
|
|
var realStringObj = obj && obj.toString();
|
|
return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;
|
|
},
|
|
|
|
isPlainObject: function( obj ) {
|
|
var key;
|
|
|
|
// Not plain objects:
|
|
// - Any object or value whose internal [[Class]] property is not "[object Object]"
|
|
// - DOM nodes
|
|
// - window
|
|
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Not own constructor property must be Object
|
|
if ( obj.constructor &&
|
|
!hasOwn.call( obj, "constructor" ) &&
|
|
!hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Own properties are enumerated firstly, so to speed up,
|
|
// if last one is own, then all properties are own
|
|
for ( key in obj ) {}
|
|
|
|
return key === undefined || hasOwn.call( obj, key );
|
|
},
|
|
|
|
isEmptyObject: function( obj ) {
|
|
var name;
|
|
for ( name in obj ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
type: function( obj ) {
|
|
if ( obj == null ) {
|
|
return obj + "";
|
|
}
|
|
|
|
// Support: Android<4.0, iOS<6 (functionish RegExp)
|
|
return typeof obj === "object" || typeof obj === "function" ?
|
|
class2type[ toString.call( obj ) ] || "object" :
|
|
typeof obj;
|
|
},
|
|
|
|
// Evaluates a script in a global context
|
|
globalEval: function( code ) {
|
|
var script,
|
|
indirect = eval;
|
|
|
|
code = jQuery.trim( code );
|
|
|
|
if ( code ) {
|
|
|
|
// If the code includes a valid, prologue position
|
|
// strict mode pragma, execute code by injecting a
|
|
// script tag into the document.
|
|
if ( code.indexOf( "use strict" ) === 1 ) {
|
|
script = document.createElement( "script" );
|
|
script.text = code;
|
|
document.head.appendChild( script ).parentNode.removeChild( script );
|
|
} else {
|
|
|
|
// Otherwise, avoid the DOM node creation, insertion
|
|
// and removal by using an indirect global eval
|
|
|
|
indirect( code );
|
|
}
|
|
}
|
|
},
|
|
|
|
// Convert dashed to camelCase; used by the css and data modules
|
|
// Support: IE9-11+
|
|
// Microsoft forgot to hump their vendor prefix (#9572)
|
|
camelCase: function( string ) {
|
|
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
|
|
},
|
|
|
|
nodeName: function( elem, name ) {
|
|
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
|
|
},
|
|
|
|
each: function( obj, callback ) {
|
|
var length, i = 0;
|
|
|
|
if ( isArrayLike( obj ) ) {
|
|
length = obj.length;
|
|
for ( ; i < length; i++ ) {
|
|
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for ( i in obj ) {
|
|
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
},
|
|
|
|
// Support: Android<4.1
|
|
trim: function( text ) {
|
|
return text == null ?
|
|
"" :
|
|
( text + "" ).replace( rtrim, "" );
|
|
},
|
|
|
|
// results is for internal usage only
|
|
makeArray: function( arr, results ) {
|
|
var ret = results || [];
|
|
|
|
if ( arr != null ) {
|
|
if ( isArrayLike( Object( arr ) ) ) {
|
|
jQuery.merge( ret,
|
|
typeof arr === "string" ?
|
|
[ arr ] : arr
|
|
);
|
|
} else {
|
|
push.call( ret, arr );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
|
|
inArray: function( elem, arr, i ) {
|
|
return arr == null ? -1 : indexOf.call( arr, elem, i );
|
|
},
|
|
|
|
merge: function( first, second ) {
|
|
var len = +second.length,
|
|
j = 0,
|
|
i = first.length;
|
|
|
|
for ( ; j < len; j++ ) {
|
|
first[ i++ ] = second[ j ];
|
|
}
|
|
|
|
first.length = i;
|
|
|
|
return first;
|
|
},
|
|
|
|
grep: function( elems, callback, invert ) {
|
|
var callbackInverse,
|
|
matches = [],
|
|
i = 0,
|
|
length = elems.length,
|
|
callbackExpect = !invert;
|
|
|
|
// Go through the array, only saving the items
|
|
// that pass the validator function
|
|
for ( ; i < length; i++ ) {
|
|
callbackInverse = !callback( elems[ i ], i );
|
|
if ( callbackInverse !== callbackExpect ) {
|
|
matches.push( elems[ i ] );
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
},
|
|
|
|
// arg is for internal usage only
|
|
map: function( elems, callback, arg ) {
|
|
var length, value,
|
|
i = 0,
|
|
ret = [];
|
|
|
|
// Go through the array, translating each of the items to their new values
|
|
if ( isArrayLike( elems ) ) {
|
|
length = elems.length;
|
|
for ( ; i < length; i++ ) {
|
|
value = callback( elems[ i ], i, arg );
|
|
|
|
if ( value != null ) {
|
|
ret.push( value );
|
|
}
|
|
}
|
|
|
|
// Go through every key on the object,
|
|
} else {
|
|
for ( i in elems ) {
|
|
value = callback( elems[ i ], i, arg );
|
|
|
|
if ( value != null ) {
|
|
ret.push( value );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flatten any nested arrays
|
|
return concat.apply( [], ret );
|
|
},
|
|
|
|
// A global GUID counter for objects
|
|
guid: 1,
|
|
|
|
// Bind a function to a context, optionally partially applying any
|
|
// arguments.
|
|
proxy: function( fn, context ) {
|
|
var tmp, args, proxy;
|
|
|
|
if ( typeof context === "string" ) {
|
|
tmp = fn[ context ];
|
|
context = fn;
|
|
fn = tmp;
|
|
}
|
|
|
|
// Quick check to determine if target is callable, in the spec
|
|
// this throws a TypeError, but we will just return undefined.
|
|
if ( !jQuery.isFunction( fn ) ) {
|
|
return undefined;
|
|
}
|
|
|
|
// Simulated bind
|
|
args = slice.call( arguments, 2 );
|
|
proxy = function() {
|
|
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
|
|
};
|
|
|
|
// Set the guid of unique handler to the same of original handler, so it can be removed
|
|
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
|
|
|
|
return proxy;
|
|
},
|
|
|
|
now: Date.now,
|
|
|
|
// jQuery.support is not used in Core but other projects attach their
|
|
// properties to it so it needs to exist.
|
|
support: support
|
|
} );
|
|
|
|
// JSHint would error on this code due to the Symbol not being defined in ES5.
|
|
// Defining this global in .jshintrc would create a danger of using the global
|
|
// unguarded in another place, it seems safer to just disable JSHint for these
|
|
// three lines.
|
|
/* jshint ignore: start */
|
|
if ( typeof Symbol === "function" ) {
|
|
jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
|
|
}
|
|
/* jshint ignore: end */
|
|
|
|
// Populate the class2type map
|
|
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
|
|
function( i, name ) {
|
|
class2type[ "[object " + name + "]" ] = name.toLowerCase();
|
|
} );
|
|
|
|
function isArrayLike( obj ) {
|
|
|
|
// Support: iOS 8.2 (not reproducible in simulator)
|
|
// `in` check used to prevent JIT error (gh-2145)
|
|
// hasOwn isn't used here due to false negatives
|
|
// regarding Nodelist length in IE
|
|
var length = !!obj && "length" in obj && obj.length,
|
|
type = jQuery.type( obj );
|
|
|
|
if ( type === "function" || jQuery.isWindow( obj ) ) {
|
|
return false;
|
|
}
|
|
|
|
return type === "array" || length === 0 ||
|
|
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
|
|
}
|
|
var Sizzle =
|
|
/*!
|
|
* Sizzle CSS Selector Engine v2.2.1
|
|
* http://sizzlejs.com/
|
|
*
|
|
* Copyright jQuery Foundation and other contributors
|
|
* Released under the MIT license
|
|
* http://jquery.org/license
|
|
*
|
|
* Date: 2015-10-17
|
|
*/
|
|
(function( window ) {
|
|
|
|
var i,
|
|
support,
|
|
Expr,
|
|
getText,
|
|
isXML,
|
|
tokenize,
|
|
compile,
|
|
select,
|
|
outermostContext,
|
|
sortInput,
|
|
hasDuplicate,
|
|
|
|
// Local document vars
|
|
setDocument,
|
|
document,
|
|
docElem,
|
|
documentIsHTML,
|
|
rbuggyQSA,
|
|
rbuggyMatches,
|
|
matches,
|
|
contains,
|
|
|
|
// Instance-specific data
|
|
expando = "sizzle" + 1 * new Date(),
|
|
preferredDoc = window.document,
|
|
dirruns = 0,
|
|
done = 0,
|
|
classCache = createCache(),
|
|
tokenCache = createCache(),
|
|
compilerCache = createCache(),
|
|
sortOrder = function( a, b ) {
|
|
if ( a === b ) {
|
|
hasDuplicate = true;
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
// General-purpose constants
|
|
MAX_NEGATIVE = 1 << 31,
|
|
|
|
// Instance methods
|
|
hasOwn = ({}).hasOwnProperty,
|
|
arr = [],
|
|
pop = arr.pop,
|
|
push_native = arr.push,
|
|
push = arr.push,
|
|
slice = arr.slice,
|
|
// Use a stripped-down indexOf as it's faster than native
|
|
// http://jsperf.com/thor-indexof-vs-for/5
|
|
indexOf = function( list, elem ) {
|
|
var i = 0,
|
|
len = list.length;
|
|
for ( ; i < len; i++ ) {
|
|
if ( list[i] === elem ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
|
|
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
|
|
|
|
// Regular expressions
|
|
|
|
// http://www.w3.org/TR/css3-selectors/#whitespace
|
|
whitespace = "[\\x20\\t\\r\\n\\f]",
|
|
|
|
// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
|
|
identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
|
|
|
|
// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
|
|
attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
|
|
// Operator (capture 2)
|
|
"*([*^$|!~]?=)" + whitespace +
|
|
// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
|
|
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
|
|
"*\\]",
|
|
|
|
pseudos = ":(" + identifier + ")(?:\\((" +
|
|
// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
|
|
// 1. quoted (capture 3; capture 4 or capture 5)
|
|
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
|
|
// 2. simple (capture 6)
|
|
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
|
|
// 3. anything else (capture 2)
|
|
".*" +
|
|
")\\)|)",
|
|
|
|
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
|
|
rwhitespace = new RegExp( whitespace + "+", "g" ),
|
|
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
|
|
|
|
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
|
|
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
|
|
|
|
rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
|
|
|
|
rpseudo = new RegExp( pseudos ),
|
|
ridentifier = new RegExp( "^" + identifier + "$" ),
|
|
|
|
matchExpr = {
|
|
"ID": new RegExp( "^#(" + identifier + ")" ),
|
|
"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
|
|
"TAG": new RegExp( "^(" + identifier + "|[*])" ),
|
|
"ATTR": new RegExp( "^" + attributes ),
|
|
"PSEUDO": new RegExp( "^" + pseudos ),
|
|
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
|
|
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
|
|
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
|
|
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
|
|
// For use in libraries implementing .is()
|
|
// We use this for POS matching in `select`
|
|
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
|
|
whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
|
|
},
|
|
|
|
rinputs = /^(?:input|select|textarea|button)$/i,
|
|
rheader = /^h\d$/i,
|
|
|
|
rnative = /^[^{]+\{\s*\[native \w/,
|
|
|
|
// Easily-parseable/retrievable ID or TAG or CLASS selectors
|
|
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
|
|
|
|
rsibling = /[+~]/,
|
|
rescape = /'|\\/g,
|
|
|
|
// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
|
|
runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
|
|
funescape = function( _, escaped, escapedWhitespace ) {
|
|
var high = "0x" + escaped - 0x10000;
|
|
// NaN means non-codepoint
|
|
// Support: Firefox<24
|
|
// Workaround erroneous numeric interpretation of +"0x"
|
|
return high !== high || escapedWhitespace ?
|
|
escaped :
|
|
high < 0 ?
|
|
// BMP codepoint
|
|
String.fromCharCode( high + 0x10000 ) :
|
|
// Supplemental Plane codepoint (surrogate pair)
|
|
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
|
|
},
|
|
|
|
// Used for iframes
|
|
// See setDocument()
|
|
// Removing the function wrapper causes a "Permission Denied"
|
|
// error in IE
|
|
unloadHandler = function() {
|
|
setDocument();
|
|
};
|
|
|
|
// Optimize for push.apply( _, NodeList )
|
|
try {
|
|
push.apply(
|
|
(arr = slice.call( preferredDoc.childNodes )),
|
|
preferredDoc.childNodes
|
|
);
|
|
// Support: Android<4.0
|
|
// Detect silently failing push.apply
|
|
arr[ preferredDoc.childNodes.length ].nodeType;
|
|
} catch ( e ) {
|
|
push = { apply: arr.length ?
|
|
|
|
// Leverage slice if possible
|
|
function( target, els ) {
|
|
push_native.apply( target, slice.call(els) );
|
|
} :
|
|
|
|
// Support: IE<9
|
|
// Otherwise append directly
|
|
function( target, els ) {
|
|
var j = target.length,
|
|
i = 0;
|
|
// Can't trust NodeList.length
|
|
while ( (target[j++] = els[i++]) ) {}
|
|
target.length = j - 1;
|
|
}
|
|
};
|
|
}
|
|
|
|
function Sizzle( selector, context, results, seed ) {
|
|
var m, i, elem, nid, nidselect, match, groups, newSelector,
|
|
newContext = context && context.ownerDocument,
|
|
|
|
// nodeType defaults to 9, since context defaults to document
|
|
nodeType = context ? context.nodeType : 9;
|
|
|
|
results = results || [];
|
|
|
|
// Return early from calls with invalid selector or context
|
|
if ( typeof selector !== "string" || !selector ||
|
|
nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
|
|
|
|
return results;
|
|
}
|
|
|
|
// Try to shortcut find operations (as opposed to filters) in HTML documents
|
|
if ( !seed ) {
|
|
|
|
if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
|
|
setDocument( context );
|
|
}
|
|
context = context || document;
|
|
|
|
if ( documentIsHTML ) {
|
|
|
|
// If the selector is sufficiently simple, try using a "get*By*" DOM method
|
|
// (excepting DocumentFragment context, where the methods don't exist)
|
|
if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
|
|
|
|
// ID selector
|
|
if ( (m = match[1]) ) {
|
|
|
|
// Document context
|
|
if ( nodeType === 9 ) {
|
|
if ( (elem = context.getElementById( m )) ) {
|
|
|
|
// Support: IE, Opera, Webkit
|
|
// TODO: identify versions
|
|
// getElementById can match elements by name instead of ID
|
|
if ( elem.id === m ) {
|
|
results.push( elem );
|
|
return results;
|
|
}
|
|
} else {
|
|
return results;
|
|
}
|
|
|
|
// Element context
|
|
} else {
|
|
|
|
// Support: IE, Opera, Webkit
|
|
// TODO: identify versions
|
|
// getElementById can match elements by name instead of ID
|
|
if ( newContext && (elem = newContext.getElementById( m )) &&
|
|
contains( context, elem ) &&
|
|
elem.id === m ) {
|
|
|
|
results.push( elem );
|
|
return results;
|
|
}
|
|
}
|
|
|
|
// Type selector
|
|
} else if ( match[2] ) {
|
|
push.apply( results, context.getElementsByTagName( selector ) );
|
|
return results;
|
|
|
|
// Class selector
|
|
} else if ( (m = match[3]) && support.getElementsByClassName &&
|
|
context.getElementsByClassName ) {
|
|
|
|
push.apply( results, context.getElementsByClassName( m ) );
|
|
return results;
|
|
}
|
|
}
|
|
|
|
// Take advantage of querySelectorAll
|
|
if ( support.qsa &&
|
|
!compilerCache[ selector + " " ] &&
|
|
(!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
|
|
|
|
if ( nodeType !== 1 ) {
|
|
newContext = context;
|
|
newSelector = selector;
|
|
|
|
// qSA looks outside Element context, which is not what we want
|
|
// Thanks to Andrew Dupont for this workaround technique
|
|
// Support: IE <=8
|
|
// Exclude object elements
|
|
} else if ( context.nodeName.toLowerCase() !== "object" ) {
|
|
|
|
// Capture the context ID, setting it first if necessary
|
|
if ( (nid = context.getAttribute( "id" )) ) {
|
|
nid = nid.replace( rescape, "\\$&" );
|
|
} else {
|
|
context.setAttribute( "id", (nid = expando) );
|
|
}
|
|
|
|
// Prefix every selector in the list
|
|
groups = tokenize( selector );
|
|
i = groups.length;
|
|
nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']";
|
|
while ( i-- ) {
|
|
groups[i] = nidselect + " " + toSelector( groups[i] );
|
|
}
|
|
newSelector = groups.join( "," );
|
|
|
|
// Expand context for sibling selectors
|
|
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
|
|
context;
|
|
}
|
|
|
|
if ( newSelector ) {
|
|
try {
|
|
push.apply( results,
|
|
newContext.querySelectorAll( newSelector )
|
|
);
|
|
return results;
|
|
} catch ( qsaError ) {
|
|
} finally {
|
|
if ( nid === expando ) {
|
|
context.removeAttribute( "id" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// All others
|
|
return select( selector.replace( rtrim, "$1" ), context, results, seed );
|
|
}
|
|
|
|
/**
|
|
* Create key-value caches of limited size
|
|
* @returns {function(string, object)} Returns the Object data after storing it on itself with
|
|
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
|
|
* deleting the oldest entry
|
|
*/
|
|
function createCache() {
|
|
var keys = [];
|
|
|
|
function cache( key, value ) {
|
|
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
|
|
if ( keys.push( key + " " ) > Expr.cacheLength ) {
|
|
// Only keep the most recent entries
|
|
delete cache[ keys.shift() ];
|
|
}
|
|
return (cache[ key + " " ] = value);
|
|
}
|
|
return cache;
|
|
}
|
|
|
|
/**
|
|
* Mark a function for special use by Sizzle
|
|
* @param {Function} fn The function to mark
|
|
*/
|
|
function markFunction( fn ) {
|
|
fn[ expando ] = true;
|
|
return fn;
|
|
}
|
|
|
|
/**
|
|
* Support testing using an element
|
|
* @param {Function} fn Passed the created div and expects a boolean result
|
|
*/
|
|
function assert( fn ) {
|
|
var div = document.createElement("div");
|
|
|
|
try {
|
|
return !!fn( div );
|
|
} catch (e) {
|
|
return false;
|
|
} finally {
|
|
// Remove from its parent by default
|
|
if ( div.parentNode ) {
|
|
div.parentNode.removeChild( div );
|
|
}
|
|
// release memory in IE
|
|
div = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds the same handler for all of the specified attrs
|
|
* @param {String} attrs Pipe-separated list of attributes
|
|
* @param {Function} handler The method that will be applied
|
|
*/
|
|
function addHandle( attrs, handler ) {
|
|
var arr = attrs.split("|"),
|
|
i = arr.length;
|
|
|
|
while ( i-- ) {
|
|
Expr.attrHandle[ arr[i] ] = handler;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks document order of two siblings
|
|
* @param {Element} a
|
|
* @param {Element} b
|
|
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
|
|
*/
|
|
function siblingCheck( a, b ) {
|
|
var cur = b && a,
|
|
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
|
|
( ~b.sourceIndex || MAX_NEGATIVE ) -
|
|
( ~a.sourceIndex || MAX_NEGATIVE );
|
|
|
|
// Use IE sourceIndex if available on both nodes
|
|
if ( diff ) {
|
|
return diff;
|
|
}
|
|
|
|
// Check if b follows a
|
|
if ( cur ) {
|
|
while ( (cur = cur.nextSibling) ) {
|
|
if ( cur === b ) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return a ? 1 : -1;
|
|
}
|
|
|
|
/**
|
|
* Returns a function to use in pseudos for input types
|
|
* @param {String} type
|
|
*/
|
|
function createInputPseudo( type ) {
|
|
return function( elem ) {
|
|
var name = elem.nodeName.toLowerCase();
|
|
return name === "input" && elem.type === type;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a function to use in pseudos for buttons
|
|
* @param {String} type
|
|
*/
|
|
function createButtonPseudo( type ) {
|
|
return function( elem ) {
|
|
var name = elem.nodeName.toLowerCase();
|
|
return (name === "input" || name === "button") && elem.type === type;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a function to use in pseudos for positionals
|
|
* @param {Function} fn
|
|
*/
|
|
function createPositionalPseudo( fn ) {
|
|
return markFunction(function( argument ) {
|
|
argument = +argument;
|
|
return markFunction(function( seed, matches ) {
|
|
var j,
|
|
matchIndexes = fn( [], seed.length, argument ),
|
|
i = matchIndexes.length;
|
|
|
|
// Match elements found at the specified indexes
|
|
while ( i-- ) {
|
|
if ( seed[ (j = matchIndexes[i]) ] ) {
|
|
seed[j] = !(matches[j] = seed[j]);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Checks a node for validity as a Sizzle context
|
|
* @param {Element|Object=} context
|
|
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
|
|
*/
|
|
function testContext( context ) {
|
|
return context && typeof context.getElementsByTagName !== "undefined" && context;
|
|
}
|
|
|
|
// Expose support vars for convenience
|
|
support = Sizzle.support = {};
|
|
|
|
/**
|
|
* Detects XML nodes
|
|
* @param {Element|Object} elem An element or a document
|
|
* @returns {Boolean} True iff elem is a non-HTML XML node
|
|
*/
|
|
isXML = Sizzle.isXML = function( elem ) {
|
|
// documentElement is verified for cases where it doesn't yet exist
|
|
// (such as loading iframes in IE - #4833)
|
|
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
|
|
return documentElement ? documentElement.nodeName !== "HTML" : false;
|
|
};
|
|
|
|
/**
|
|
* Sets document-related variables once based on the current document
|
|
* @param {Element|Object} [doc] An element or document object to use to set the document
|
|
* @returns {Object} Returns the current document
|
|
*/
|
|
setDocument = Sizzle.setDocument = function( node ) {
|
|
var hasCompare, parent,
|
|
doc = node ? node.ownerDocument || node : preferredDoc;
|
|
|
|
// Return early if doc is invalid or already selected
|
|
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
|
|
return document;
|
|
}
|
|
|
|
// Update global variables
|
|
document = doc;
|
|
docElem = document.documentElement;
|
|
documentIsHTML = !isXML( document );
|
|
|
|
// Support: IE 9-11, Edge
|
|
// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
|
|
if ( (parent = document.defaultView) && parent.top !== parent ) {
|
|
// Support: IE 11
|
|
if ( parent.addEventListener ) {
|
|
parent.addEventListener( "unload", unloadHandler, false );
|
|
|
|
// Support: IE 9 - 10 only
|
|
} else if ( parent.attachEvent ) {
|
|
parent.attachEvent( "onunload", unloadHandler );
|
|
}
|
|
}
|
|
|
|
/* Attributes
|
|
---------------------------------------------------------------------- */
|
|
|
|
// Support: IE<8
|
|
// Verify that getAttribute really returns attributes and not properties
|
|
// (excepting IE8 booleans)
|
|
support.attributes = assert(function( div ) {
|
|
div.className = "i";
|
|
return !div.getAttribute("className");
|
|
});
|
|
|
|
/* getElement(s)By*
|
|
---------------------------------------------------------------------- */
|
|
|
|
// Check if getElementsByTagName("*") returns only elements
|
|
support.getElementsByTagName = assert(function( div ) {
|
|
div.appendChild( document.createComment("") );
|
|
return !div.getElementsByTagName("*").length;
|
|
});
|
|
|
|
// Support: IE<9
|
|
support.getElementsByClassName = rnative.test( document.getElementsByClassName );
|
|
|
|
// Support: IE<10
|
|
// Check if getElementById returns elements by name
|
|
// The broken getElementById methods don't pick up programatically-set names,
|
|
// so use a roundabout getElementsByName test
|
|
support.getById = assert(function( div ) {
|
|
docElem.appendChild( div ).id = expando;
|
|
return !document.getElementsByName || !document.getElementsByName( expando ).length;
|
|
});
|
|
|
|
// ID find and filter
|
|
if ( support.getById ) {
|
|
Expr.find["ID"] = function( id, context ) {
|
|
if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
|
|
var m = context.getElementById( id );
|
|
return m ? [ m ] : [];
|
|
}
|
|
};
|
|
Expr.filter["ID"] = function( id ) {
|
|
var attrId = id.replace( runescape, funescape );
|
|
return function( elem ) {
|
|
return elem.getAttribute("id") === attrId;
|
|
};
|
|
};
|
|
} else {
|
|
// Support: IE6/7
|
|
// getElementById is not reliable as a find shortcut
|
|
delete Expr.find["ID"];
|
|
|
|
Expr.filter["ID"] = function( id ) {
|
|
var attrId = id.replace( runescape, funescape );
|
|
return function( elem ) {
|
|
var node = typeof elem.getAttributeNode !== "undefined" &&
|
|
elem.getAttributeNode("id");
|
|
return node && node.value === attrId;
|
|
};
|
|
};
|
|
}
|
|
|
|
// Tag
|
|
Expr.find["TAG"] = support.getElementsByTagName ?
|
|
function( tag, context ) {
|
|
if ( typeof context.getElementsByTagName !== "undefined" ) {
|
|
return context.getElementsByTagName( tag );
|
|
|
|
// DocumentFragment nodes don't have gEBTN
|
|
} else if ( support.qsa ) {
|
|
return context.querySelectorAll( tag );
|
|
}
|
|
} :
|
|
|
|
function( tag, context ) {
|
|
var elem,
|
|
tmp = [],
|
|
i = 0,
|
|
// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
|
|
results = context.getElementsByTagName( tag );
|
|
|
|
// Filter out possible comments
|
|
if ( tag === "*" ) {
|
|
while ( (elem = results[i++]) ) {
|
|
if ( elem.nodeType === 1 ) {
|
|
tmp.push( elem );
|
|
}
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
return results;
|
|
};
|
|
|
|
// Class
|
|
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
|
|
if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
|
|
return context.getElementsByClassName( className );
|
|
}
|
|
};
|
|
|
|
/* QSA/matchesSelector
|
|
---------------------------------------------------------------------- */
|
|
|
|
// QSA and matchesSelector support
|
|
|
|
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
|
|
rbuggyMatches = [];
|
|
|
|
// qSa(:focus) reports false when true (Chrome 21)
|
|
// We allow this because of a bug in IE8/9 that throws an error
|
|
// whenever `document.activeElement` is accessed on an iframe
|
|
// So, we allow :focus to pass through QSA all the time to avoid the IE error
|
|
// See http://bugs.jquery.com/ticket/13378
|
|
rbuggyQSA = [];
|
|
|
|
if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
|
|
// Build QSA regex
|
|
// Regex strategy adopted from Diego Perini
|
|
assert(function( div ) {
|
|
// Select is set to empty string on purpose
|
|
// This is to test IE's treatment of not explicitly
|
|
// setting a boolean content attribute,
|
|
// since its presence should be enough
|
|
// http://bugs.jquery.com/ticket/12359
|
|
docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
|
|
"<select id='" + expando + "-\r\\' msallowcapture=''>" +
|
|
"<option selected=''></option></select>";
|
|
|
|
// Support: IE8, Opera 11-12.16
|
|
// Nothing should be selected when empty strings follow ^= or $= or *=
|
|
// The test attribute must be unknown in Opera but "safe" for WinRT
|
|
// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
|
|
if ( div.querySelectorAll("[msallowcapture^='']").length ) {
|
|
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
|
|
}
|
|
|
|
// Support: IE8
|
|
// Boolean attributes and "value" are not treated correctly
|
|
if ( !div.querySelectorAll("[selected]").length ) {
|
|
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
|
|
}
|
|
|
|
// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
|
|
if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
|
|
rbuggyQSA.push("~=");
|
|
}
|
|
|
|
// Webkit/Opera - :checked should return selected option elements
|
|
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
|
|
// IE8 throws error here and will not see later tests
|
|
if ( !div.querySelectorAll(":checked").length ) {
|
|
rbuggyQSA.push(":checked");
|
|
}
|
|
|
|
// Support: Safari 8+, iOS 8+
|
|
// https://bugs.webkit.org/show_bug.cgi?id=136851
|
|
// In-page `selector#id sibing-combinator selector` fails
|
|
if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
|
|
rbuggyQSA.push(".#.+[+~]");
|
|
}
|
|
});
|
|
|
|
assert(function( div ) {
|
|
// Support: Windows 8 Native Apps
|
|
// The type and name attributes are restricted during .innerHTML assignment
|
|
var input = document.createElement("input");
|
|
input.setAttribute( "type", "hidden" );
|
|
div.appendChild( input ).setAttribute( "name", "D" );
|
|
|
|
// Support: IE8
|
|
// Enforce case-sensitivity of name attribute
|
|
if ( div.querySelectorAll("[name=d]").length ) {
|
|
rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
|
|
}
|
|
|
|
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
|
|
// IE8 throws error here and will not see later tests
|
|
if ( !div.querySelectorAll(":enabled").length ) {
|
|
rbuggyQSA.push( ":enabled", ":disabled" );
|
|
}
|
|
|
|
// Opera 10-11 does not throw on post-comma invalid pseudos
|
|
div.querySelectorAll("*,:x");
|
|
rbuggyQSA.push(",.*:");
|
|
});
|
|
}
|
|
|
|
if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
|
|
docElem.webkitMatchesSelector ||
|
|
docElem.mozMatchesSelector ||
|
|
docElem.oMatchesSelector ||
|
|
docElem.msMatchesSelector) )) ) {
|
|
|
|
assert(function( div ) {
|
|
// Check to see if it's possible to do matchesSelector
|
|
// on a disconnected node (IE 9)
|
|
support.disconnectedMatch = matches.call( div, "div" );
|
|
|
|
// This should fail with an exception
|
|
// Gecko does not error, returns false instead
|
|
matches.call( div, "[s!='']:x" );
|
|
rbuggyMatches.push( "!=", pseudos );
|
|
});
|
|
}
|
|
|
|
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
|
|
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
|
|
|
|
/* Contains
|
|
---------------------------------------------------------------------- */
|
|
hasCompare = rnative.test( docElem.compareDocumentPosition );
|
|
|
|
// Element contains another
|
|
// Purposefully self-exclusive
|
|
// As in, an element does not contain itself
|
|
contains = hasCompare || rnative.test( docElem.contains ) ?
|
|
function( a, b ) {
|
|
var adown = a.nodeType === 9 ? a.documentElement : a,
|
|
bup = b && b.parentNode;
|
|
return a === bup || !!( bup && bup.nodeType === 1 && (
|
|
adown.contains ?
|
|
adown.contains( bup ) :
|
|
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
|
|
));
|
|
} :
|
|
function( a, b ) {
|
|
if ( b ) {
|
|
while ( (b = b.parentNode) ) {
|
|
if ( b === a ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/* Sorting
|
|
---------------------------------------------------------------------- */
|
|
|
|
// Document order sorting
|
|
sortOrder = hasCompare ?
|
|
function( a, b ) {
|
|
|
|
// Flag for duplicate removal
|
|
if ( a === b ) {
|
|
hasDuplicate = true;
|
|
return 0;
|
|
}
|
|
|
|
// Sort on method existence if only one input has compareDocumentPosition
|
|
var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
|
|
if ( compare ) {
|
|
return compare;
|
|
}
|
|
|
|
// Calculate position if both inputs belong to the same document
|
|
compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
|
|
a.compareDocumentPosition( b ) :
|
|
|
|
// Otherwise we know they are disconnected
|
|
1;
|
|
|
|
// Disconnected nodes
|
|
if ( compare & 1 ||
|
|
(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
|
|
|
|
// Choose the first element that is related to our preferred document
|
|
if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
|
|
return -1;
|
|
}
|
|
if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
|
|
return 1;
|
|
}
|
|
|
|
// Maintain original order
|
|
return sortInput ?
|
|
( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
|
|
0;
|
|
}
|
|
|
|
return compare & 4 ? -1 : 1;
|
|
} :
|
|
function( a, b ) {
|
|
// Exit early if the nodes are identical
|
|
if ( a === b ) {
|
|
hasDuplicate = true;
|
|
return 0;
|
|
}
|
|
|
|
var cur,
|
|
i = 0,
|
|
aup = a.parentNode,
|
|
bup = b.parentNode,
|
|
ap = [ a ],
|
|
bp = [ b ];
|
|
|
|
// Parentless nodes are either documents or disconnected
|
|
if ( !aup || !bup ) {
|
|
return a === document ? -1 :
|
|
b === document ? 1 :
|
|
aup ? -1 :
|
|
bup ? 1 :
|
|
sortInput ?
|
|
( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
|
|
0;
|
|
|
|
// If the nodes are siblings, we can do a quick check
|
|
} else if ( aup === bup ) {
|
|
return siblingCheck( a, b );
|
|
}
|
|
|
|
// Otherwise we need full lists of their ancestors for comparison
|
|
cur = a;
|
|
while ( (cur = cur.parentNode) ) {
|
|
ap.unshift( cur );
|
|
}
|
|
cur = b;
|
|
while ( (cur = cur.parentNode) ) {
|
|
bp.unshift( cur );
|
|
}
|
|
|
|
// Walk down the tree looking for a discrepancy
|
|
while ( ap[i] === bp[i] ) {
|
|
i++;
|
|
}
|
|
|
|
return i ?
|
|
// Do a sibling check if the nodes have a common ancestor
|
|
siblingCheck( ap[i], bp[i] ) :
|
|
|
|
// Otherwise nodes in our document sort first
|
|
ap[i] === preferredDoc ? -1 :
|
|
bp[i] === preferredDoc ? 1 :
|
|
0;
|
|
};
|
|
|
|
return document;
|
|
};
|
|
|
|
Sizzle.matches = function( expr, elements ) {
|
|
return Sizzle( expr, null, null, elements );
|
|
};
|
|
|
|
Sizzle.matchesSelector = function( elem, expr ) {
|
|
// Set document vars if needed
|
|
if ( ( elem.ownerDocument || elem ) !== document ) {
|
|
setDocument( elem );
|
|
}
|
|
|
|
// Make sure that attribute selectors are quoted
|
|
expr = expr.replace( rattributeQuotes, "='$1']" );
|
|
|
|
if ( support.matchesSelector && documentIsHTML &&
|
|
!compilerCache[ expr + " " ] &&
|
|
( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
|
|
( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
|
|
|
|
try {
|
|
var ret = matches.call( elem, expr );
|
|
|
|
// IE 9's matchesSelector returns false on disconnected nodes
|
|
if ( ret || support.disconnectedMatch ||
|
|
// As well, disconnected nodes are said to be in a document
|
|
// fragment in IE 9
|
|
elem.document && elem.document.nodeType !== 11 ) {
|
|
return ret;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
return Sizzle( expr, document, null, [ elem ] ).length > 0;
|
|
};
|
|
|
|
Sizzle.contains = function( context, elem ) {
|
|
// Set document vars if needed
|
|
if ( ( context.ownerDocument || context ) !== document ) {
|
|
setDocument( context );
|
|
}
|
|
return contains( context, elem );
|
|
};
|
|
|
|
Sizzle.attr = function( elem, name ) {
|
|
// Set document vars if needed
|
|
if ( ( elem.ownerDocument || elem ) !== document ) {
|
|
setDocument( elem );
|
|
}
|
|
|
|
var fn = Expr.attrHandle[ name.toLowerCase() ],
|
|
// Don't get fooled by Object.prototype properties (jQuery #13807)
|
|
val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
|
|
fn( elem, name, !documentIsHTML ) :
|
|
undefined;
|
|
|
|
return val !== undefined ?
|
|
val :
|
|
support.attributes || !documentIsHTML ?
|
|
elem.getAttribute( name ) :
|
|
(val = elem.getAttributeNode(name)) && val.specified ?
|
|
val.value :
|
|
null;
|
|
};
|
|
|
|
Sizzle.error = function( msg ) {
|
|
throw new Error( "Syntax error, unrecognized expression: " + msg );
|
|
};
|
|
|
|
/**
|
|
* Document sorting and removing duplicates
|
|
* @param {ArrayLike} results
|
|
*/
|
|
Sizzle.uniqueSort = function( results ) {
|
|
var elem,
|
|
duplicates = [],
|
|
j = 0,
|
|
i = 0;
|
|
|
|
// Unless we *know* we can detect duplicates, assume their presence
|
|
hasDuplicate = !support.detectDuplicates;
|
|
sortInput = !support.sortStable && results.slice( 0 );
|
|
results.sort( sortOrder );
|
|
|
|
if ( hasDuplicate ) {
|
|
while ( (elem = results[i++]) ) {
|
|
if ( elem === results[ i ] ) {
|
|
j = duplicates.push( i );
|
|
}
|
|
}
|
|
while ( j-- ) {
|
|
results.splice( duplicates[ j ], 1 );
|
|
}
|
|
}
|
|
|
|
// Clear input after sorting to release objects
|
|
// See https://github.com/jquery/sizzle/pull/225
|
|
sortInput = null;
|
|
|
|
return results;
|
|
};
|
|
|
|
/**
|
|
* Utility function for retrieving the text value of an array of DOM nodes
|
|
* @param {Array|Element} elem
|
|
*/
|
|
getText = Sizzle.getText = function( elem ) {
|
|
var node,
|
|
ret = "",
|
|
i = 0,
|
|
nodeType = elem.nodeType;
|
|
|
|
if ( !nodeType ) {
|
|
// If no nodeType, this is expected to be an array
|
|
while ( (node = elem[i++]) ) {
|
|
// Do not traverse comment nodes
|
|
ret += getText( node );
|
|
}
|
|
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
|
|
// Use textContent for elements
|
|
// innerText usage removed for consistency of new lines (jQuery #11153)
|
|
if ( typeof elem.textContent === "string" ) {
|
|
return elem.textContent;
|
|
} else {
|
|
// Traverse its children
|
|
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
|
|
ret += getText( elem );
|
|
}
|
|
}
|
|
} else if ( nodeType === 3 || nodeType === 4 ) {
|
|
return elem.nodeValue;
|
|
}
|
|
// Do not include comment or processing instruction nodes
|
|
|
|
return ret;
|
|
};
|
|
|
|
Expr = Sizzle.selectors = {
|
|
|
|
// Can be adjusted by the user
|
|
cacheLength: 50,
|
|
|
|
createPseudo: markFunction,
|
|
|
|
match: matchExpr,
|
|
|
|
attrHandle: {},
|
|
|
|
find: {},
|
|
|
|
relative: {
|
|
">": { dir: "parentNode", first: true },
|
|
" ": { dir: "parentNode" },
|
|
"+": { dir: "previousSibling", first: true },
|
|
"~": { dir: "previousSibling" }
|
|
},
|
|
|
|
preFilter: {
|
|
"ATTR": function( match ) {
|
|
match[1] = match[1].replace( runescape, funescape );
|
|
|
|
// Move the given value to match[3] whether quoted or unquoted
|
|
match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
|
|
|
|
if ( match[2] === "~=" ) {
|
|
match[3] = " " + match[3] + " ";
|
|
}
|
|
|
|
return match.slice( 0, 4 );
|
|
},
|
|
|
|
"CHILD": function( match ) {
|
|
/* matches from matchExpr["CHILD"]
|
|
1 type (only|nth|...)
|
|
2 what (child|of-type)
|
|
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
|
|
4 xn-component of xn+y argument ([+-]?\d*n|)
|
|
5 sign of xn-component
|
|
6 x of xn-component
|
|
7 sign of y-component
|
|
8 y of y-component
|
|
*/
|
|
match[1] = match[1].toLowerCase();
|
|
|
|
if ( match[1].slice( 0, 3 ) === "nth" ) {
|
|
// nth-* requires argument
|
|
if ( !match[3] ) {
|
|
Sizzle.error( match[0] );
|
|
}
|
|
|
|
// numeric x and y parameters for Expr.filter.CHILD
|
|
// remember that false/true cast respectively to 0/1
|
|
match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
|
|
match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
|
|
|
|
// other types prohibit arguments
|
|
} else if ( match[3] ) {
|
|
Sizzle.error( match[0] );
|
|
}
|
|
|
|
return match;
|
|
},
|
|
|
|
"PSEUDO": function( match ) {
|
|
var excess,
|
|
unquoted = !match[6] && match[2];
|
|
|
|
if ( matchExpr["CHILD"].test( match[0] ) ) {
|
|
return null;
|
|
}
|
|
|
|
// Accept quoted arguments as-is
|
|
if ( match[3] ) {
|
|
match[2] = match[4] || match[5] || "";
|
|
|
|
// Strip excess characters from unquoted arguments
|
|
} else if ( unquoted && rpseudo.test( unquoted ) &&
|
|
// Get excess from tokenize (recursively)
|
|
(excess = tokenize( unquoted, true )) &&
|
|
// advance to the next closing parenthesis
|
|
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
|
|
|
|
// excess is a negative index
|
|
match[0] = match[0].slice( 0, excess );
|
|
match[2] = unquoted.slice( 0, excess );
|
|
}
|
|
|
|
// Return only captures needed by the pseudo filter method (type and argument)
|
|
return match.slice( 0, 3 );
|
|
}
|
|
},
|
|
|
|
filter: {
|
|
|
|
"TAG": function( nodeNameSelector ) {
|
|
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
|
|
return nodeNameSelector === "*" ?
|
|
function() { return true; } :
|
|
function( elem ) {
|
|
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
|
|
};
|
|
},
|
|
|
|
"CLASS": function( className ) {
|
|
var pattern = classCache[ className + " " ];
|
|
|
|
return pattern ||
|
|
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
|
|
classCache( className, function( elem ) {
|
|
return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
|
|
});
|
|
},
|
|
|
|
"ATTR": function( name, operator, check ) {
|
|
return function( elem ) {
|
|
var result = Sizzle.attr( elem, name );
|
|
|
|
if ( result == null ) {
|
|
return operator === "!=";
|
|
}
|
|
if ( !operator ) {
|
|
return true;
|
|
}
|
|
|
|
result += "";
|
|
|
|
return operator === "=" ? result === check :
|
|
operator === "!=" ? result !== check :
|
|
operator === "^=" ? check && result.indexOf( check ) === 0 :
|
|
operator === "*=" ? check && result.indexOf( check ) > -1 :
|
|
operator === "$=" ? check && result.slice( -check.length ) === check :
|
|
operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
|
|
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
|
|
false;
|
|
};
|
|
},
|
|
|
|
"CHILD": function( type, what, argument, first, last ) {
|
|
var simple = type.slice( 0, 3 ) !== "nth",
|
|
forward = type.slice( -4 ) !== "last",
|
|
ofType = what === "of-type";
|
|
|
|
return first === 1 && last === 0 ?
|
|
|
|
// Shortcut for :nth-*(n)
|
|
function( elem ) {
|
|
return !!elem.parentNode;
|
|
} :
|
|
|
|
function( elem, context, xml ) {
|
|
var cache, uniqueCache, outerCache, node, nodeIndex, start,
|
|
dir = simple !== forward ? "nextSibling" : "previousSibling",
|
|
parent = elem.parentNode,
|
|
name = ofType && elem.nodeName.toLowerCase(),
|
|
useCache = !xml && !ofType,
|
|
diff = false;
|
|
|
|
if ( parent ) {
|
|
|
|
// :(first|last|only)-(child|of-type)
|
|
if ( simple ) {
|
|
while ( dir ) {
|
|
node = elem;
|
|
while ( (node = node[ dir ]) ) {
|
|
if ( ofType ?
|
|
node.nodeName.toLowerCase() === name :
|
|
node.nodeType === 1 ) {
|
|
|
|
return false;
|
|
}
|
|
}
|
|
// Reverse direction for :only-* (if we haven't yet done so)
|
|
start = dir = type === "only" && !start && "nextSibling";
|
|
}
|
|
return true;
|
|
}
|
|
|
|
start = [ forward ? parent.firstChild : parent.lastChild ];
|
|
|
|
// non-xml :nth-child(...) stores cache data on `parent`
|
|
if ( forward && useCache ) {
|
|
|
|
// Seek `elem` from a previously-cached index
|
|
|
|
// ...in a gzip-friendly way
|
|
node = parent;
|
|
outerCache = node[ expando ] || (node[ expando ] = {});
|
|
|
|
// Support: IE <9 only
|
|
// Defend against cloned attroperties (jQuery gh-1709)
|
|
uniqueCache = outerCache[ node.uniqueID ] ||
|
|
(outerCache[ node.uniqueID ] = {});
|
|
|
|
cache = uniqueCache[ type ] || [];
|
|
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
|
|
diff = nodeIndex && cache[ 2 ];
|
|
node = nodeIndex && parent.childNodes[ nodeIndex ];
|
|
|
|
while ( (node = ++nodeIndex && node && node[ dir ] ||
|
|
|
|
// Fallback to seeking `elem` from the start
|
|
(diff = nodeIndex = 0) || start.pop()) ) {
|
|
|
|
// When found, cache indexes on `parent` and break
|
|
if ( node.nodeType === 1 && ++diff && node === elem ) {
|
|
uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// Use previously-cached element index if available
|
|
if ( useCache ) {
|
|
// ...in a gzip-friendly way
|
|
node = elem;
|
|
outerCache = node[ expando ] || (node[ expando ] = {});
|
|
|
|
// Support: IE <9 only
|
|
// Defend against cloned attroperties (jQuery gh-1709)
|
|
uniqueCache = outerCache[ node.uniqueID ] ||
|
|
(outerCache[ node.uniqueID ] = {});
|
|
|
|
cache = uniqueCache[ type ] || [];
|
|
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
|
|
diff = nodeIndex;
|
|
}
|
|
|
|
// xml :nth-child(...)
|
|
// or :nth-last-child(...) or :nth(-last)?-of-type(...)
|
|
if ( diff === false ) {
|
|
// Use the same loop as above to seek `elem` from the start
|
|
while ( (node = ++nodeIndex && node && node[ dir ] ||
|
|
(diff = nodeIndex = 0) || start.pop()) ) {
|
|
|
|
if ( ( ofType ?
|
|
node.nodeName.toLowerCase() === name :
|
|
node.nodeType === 1 ) &&
|
|
++diff ) {
|
|
|
|
// Cache the index of each encountered element
|
|
if ( useCache ) {
|
|
outerCache = node[ expando ] || (node[ expando ] = {});
|
|
|
|
// Support: IE <9 only
|
|
// Defend against cloned attroperties (jQuery gh-1709)
|
|
uniqueCache = outerCache[ node.uniqueID ] ||
|
|
(outerCache[ node.uniqueID ] = {});
|
|
|
|
uniqueCache[ type ] = [ dirruns, diff ];
|
|
}
|
|
|
|
if ( node === elem ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Incorporate the offset, then check against cycle size
|
|
diff -= last;
|
|
return diff === first || ( diff % first === 0 && diff / first >= 0 );
|
|
}
|
|
};
|
|
},
|
|
|
|
"PSEUDO": function( pseudo, argument ) {
|
|
// pseudo-class names are case-insensitive
|
|
// http://www.w3.org/TR/selectors/#pseudo-classes
|
|
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
|
|
// Remember that setFilters inherits from pseudos
|
|
var args,
|
|
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
|
|
Sizzle.error( "unsupported pseudo: " + pseudo );
|
|
|
|
// The user may use createPseudo to indicate that
|
|
// arguments are needed to create the filter function
|
|
// just as Sizzle does
|
|
if ( fn[ expando ] ) {
|
|
return fn( argument );
|
|
}
|
|
|
|
// But maintain support for old signatures
|
|
if ( fn.length > 1 ) {
|
|
args = [ pseudo, pseudo, "", argument ];
|
|
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
|
|
markFunction(function( seed, matches ) {
|
|
var idx,
|
|
matched = fn( seed, argument ),
|
|
i = matched.length;
|
|
while ( i-- ) {
|
|
idx = indexOf( seed, matched[i] );
|
|
seed[ idx ] = !( matches[ idx ] = matched[i] );
|
|
}
|
|
}) :
|
|
function( elem ) {
|
|
return fn( elem, 0, args );
|
|
};
|
|
}
|
|
|
|
return fn;
|
|
}
|
|
},
|
|
|
|
pseudos: {
|
|
// Potentially complex pseudos
|
|
"not": markFunction(function( selector ) {
|
|
// Trim the selector passed to compile
|
|
// to avoid treating leading and trailing
|
|
// spaces as combinators
|
|
var input = [],
|
|
results = [],
|
|
matcher = compile( selector.replace( rtrim, "$1" ) );
|
|
|
|
return matcher[ expando ] ?
|
|
markFunction(function( seed, matches, context, xml ) {
|
|
var elem,
|
|
unmatched = matcher( seed, null, xml, [] ),
|
|
i = seed.length;
|
|
|
|
// Match elements unmatched by `matcher`
|
|
while ( i-- ) {
|
|
if ( (elem = unmatched[i]) ) {
|
|
seed[i] = !(matches[i] = elem);
|
|
}
|
|
}
|
|
}) :
|
|
function( elem, context, xml ) {
|
|
input[0] = elem;
|
|
matcher( input, null, xml, results );
|
|
// Don't keep the element (issue #299)
|
|
input[0] = null;
|
|
return !results.pop();
|
|
};
|
|
}),
|
|
|
|
"has": markFunction(function( selector ) {
|
|
return function( elem ) {
|
|
return Sizzle( selector, elem ).length > 0;
|
|
};
|
|
}),
|
|
|
|
"contains": markFunction(function( text ) {
|
|
text = text.replace( runescape, funescape );
|
|
return function( elem ) {
|
|
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
|
|
};
|
|
}),
|
|
|
|
// "Whether an element is represented by a :lang() selector
|
|
// is based solely on the element's language value
|
|
// being equal to the identifier C,
|
|
// or beginning with the identifier C immediately followed by "-".
|
|
// The matching of C against the element's language value is performed case-insensitively.
|
|
// The identifier C does not have to be a valid language name."
|
|
// http://www.w3.org/TR/selectors/#lang-pseudo
|
|
"lang": markFunction( function( lang ) {
|
|
// lang value must be a valid identifier
|
|
if ( !ridentifier.test(lang || "") ) {
|
|
Sizzle.error( "unsupported lang: " + lang );
|
|
}
|
|
lang = lang.replace( runescape, funescape ).toLowerCase();
|
|
return function( elem ) {
|
|
var elemLang;
|
|
do {
|
|
if ( (elemLang = documentIsHTML ?
|
|
elem.lang :
|
|
elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
|
|
|
|
elemLang = elemLang.toLowerCase();
|
|
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
|
|
}
|
|
} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
|
|
return false;
|
|
};
|
|
}),
|
|
|
|
// Miscellaneous
|
|
"target": function( elem ) {
|
|
var hash = window.location && window.location.hash;
|
|
return hash && hash.slice( 1 ) === elem.id;
|
|
},
|
|
|
|
"root": function( elem ) {
|
|
return elem === docElem;
|
|
},
|
|
|
|
"focus": function( elem ) {
|
|
return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
|
|
},
|
|
|
|
// Boolean properties
|
|
"enabled": function( elem ) {
|
|
return elem.disabled === false;
|
|
},
|
|
|
|
"disabled": function( elem ) {
|
|
return elem.disabled === true;
|
|
},
|
|
|
|
"checked": function( elem ) {
|
|
// In CSS3, :checked should return both checked and selected elements
|
|
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
|
|
var nodeName = elem.nodeName.toLowerCase();
|
|
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
|
|
},
|
|
|
|
"selected": function( elem ) {
|
|
// Accessing this property makes selected-by-default
|
|
// options in Safari work properly
|
|
if ( elem.parentNode ) {
|
|
elem.parentNode.selectedIndex;
|
|
}
|
|
|
|
return elem.selected === true;
|
|
},
|
|
|
|
// Contents
|
|
"empty": function( elem ) {
|
|
// http://www.w3.org/TR/selectors/#empty-pseudo
|
|
// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
|
|
// but not by others (comment: 8; processing instruction: 7; etc.)
|
|
// nodeType < 6 works because attributes (2) do not appear as children
|
|
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
|
|
if ( elem.nodeType < 6 ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
"parent": function( elem ) {
|
|
return !Expr.pseudos["empty"]( elem );
|
|
},
|
|
|
|
// Element/input types
|
|
"header": function( elem ) {
|
|
return rheader.test( elem.nodeName );
|
|
},
|
|
|
|
"input": function( elem ) {
|
|
return rinputs.test( elem.nodeName );
|
|
},
|
|
|
|
"button": function( elem ) {
|
|
var name = elem.nodeName.toLowerCase();
|
|
return name === "input" && elem.type === "button" || name === "button";
|
|
},
|
|
|
|
"text": function( elem ) {
|
|
var attr;
|
|
return elem.nodeName.toLowerCase() === "input" &&
|
|
elem.type === "text" &&
|
|
|
|
// Support: IE<8
|
|
// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
|
|
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
|
|
},
|
|
|
|
// Position-in-collection
|
|
"first": createPositionalPseudo(function() {
|
|
return [ 0 ];
|
|
}),
|
|
|
|
"last": createPositionalPseudo(function( matchIndexes, length ) {
|
|
return [ length - 1 ];
|
|
}),
|
|
|
|
"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
|
|
return [ argument < 0 ? argument + length : argument ];
|
|
}),
|
|
|
|
"even": createPositionalPseudo(function( matchIndexes, length ) {
|
|
var i = 0;
|
|
for ( ; i < length; i += 2 ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
}),
|
|
|
|
"odd": createPositionalPseudo(function( matchIndexes, length ) {
|
|
var i = 1;
|
|
for ( ; i < length; i += 2 ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
}),
|
|
|
|
"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
|
|
var i = argument < 0 ? argument + length : argument;
|
|
for ( ; --i >= 0; ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
}),
|
|
|
|
"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
|
|
var i = argument < 0 ? argument + length : argument;
|
|
for ( ; ++i < length; ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
})
|
|
}
|
|
};
|
|
|
|
Expr.pseudos["nth"] = Expr.pseudos["eq"];
|
|
|
|
// Add button/input type pseudos
|
|
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
|
|
Expr.pseudos[ i ] = createInputPseudo( i );
|
|
}
|
|
for ( i in { submit: true, reset: true } ) {
|
|
Expr.pseudos[ i ] = createButtonPseudo( i );
|
|
}
|
|
|
|
// Easy API for creating new setFilters
|
|
function setFilters() {}
|
|
setFilters.prototype = Expr.filters = Expr.pseudos;
|
|
Expr.setFilters = new setFilters();
|
|
|
|
tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
|
|
var matched, match, tokens, type,
|
|
soFar, groups, preFilters,
|
|
cached = tokenCache[ selector + " " ];
|
|
|
|
if ( cached ) {
|
|
return parseOnly ? 0 : cached.slice( 0 );
|
|
}
|
|
|
|
soFar = selector;
|
|
groups = [];
|
|
preFilters = Expr.preFilter;
|
|
|
|
while ( soFar ) {
|
|
|
|
// Comma and first run
|
|
if ( !matched || (match = rcomma.exec( soFar )) ) {
|
|
if ( match ) {
|
|
// Don't consume trailing commas as valid
|
|
soFar = soFar.slice( match[0].length ) || soFar;
|
|
}
|
|
groups.push( (tokens = []) );
|
|
}
|
|
|
|
matched = false;
|
|
|
|
// Combinators
|
|
if ( (match = rcombinators.exec( soFar )) ) {
|
|
matched = match.shift();
|
|
tokens.push({
|
|
value: matched,
|
|
// Cast descendant combinators to space
|
|
type: match[0].replace( rtrim, " " )
|
|
});
|
|
soFar = soFar.slice( matched.length );
|
|
}
|
|
|
|
// Filters
|
|
for ( type in Expr.filter ) {
|
|
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
|
|
(match = preFilters[ type ]( match ))) ) {
|
|
matched = match.shift();
|
|
tokens.push({
|
|
value: matched,
|
|
type: type,
|
|
matches: match
|
|
});
|
|
soFar = soFar.slice( matched.length );
|
|
}
|
|
}
|
|
|
|
if ( !matched ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return the length of the invalid excess
|
|
// if we're just parsing
|
|
// Otherwise, throw an error or return tokens
|
|
return parseOnly ?
|
|
soFar.length :
|
|
soFar ?
|
|
Sizzle.error( selector ) :
|
|
// Cache the tokens
|
|
tokenCache( selector, groups ).slice( 0 );
|
|
};
|
|
|
|
function toSelector( tokens ) {
|
|
var i = 0,
|
|
len = tokens.length,
|
|
selector = "";
|
|
for ( ; i < len; i++ ) {
|
|
selector += tokens[i].value;
|
|
}
|
|
return selector;
|
|
}
|
|
|
|
function addCombinator( matcher, combinator, base ) {
|
|
var dir = combinator.dir,
|
|
checkNonElements = base && dir === "parentNode",
|
|
doneName = done++;
|
|
|
|
return combinator.first ?
|
|
// Check against closest ancestor/preceding element
|
|
function( elem, context, xml ) {
|
|
while ( (elem = elem[ dir ]) ) {
|
|
if ( elem.nodeType === 1 || checkNonElements ) {
|
|
return matcher( elem, context, xml );
|
|
}
|
|
}
|
|
} :
|
|
|
|
// Check against all ancestor/preceding elements
|
|
function( elem, context, xml ) {
|
|
var oldCache, uniqueCache, outerCache,
|
|
newCache = [ dirruns, doneName ];
|
|
|
|
// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
|
|
if ( xml ) {
|
|
while ( (elem = elem[ dir ]) ) {
|
|
if ( elem.nodeType === 1 || checkNonElements ) {
|
|
if ( matcher( elem, context, xml ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
while ( (elem = elem[ dir ]) ) {
|
|
if ( elem.nodeType === 1 || checkNonElements ) {
|
|
outerCache = elem[ expando ] || (elem[ expando ] = {});
|
|
|
|
// Support: IE <9 only
|
|
// Defend against cloned attroperties (jQuery gh-1709)
|
|
uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
|
|
|
|
if ( (oldCache = uniqueCache[ dir ]) &&
|
|
oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
|
|
|
|
// Assign to newCache so results back-propagate to previous elements
|
|
return (newCache[ 2 ] = oldCache[ 2 ]);
|
|
} else {
|
|
// Reuse newcache so results back-propagate to previous elements
|
|
uniqueCache[ dir ] = newCache;
|
|
|
|
// A match means we're done; a fail means we have to keep checking
|
|
if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function elementMatcher( matchers ) {
|
|
return matchers.length > 1 ?
|
|
function( elem, context, xml ) {
|
|
var i = matchers.length;
|
|
while ( i-- ) {
|
|
if ( !matchers[i]( elem, context, xml ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} :
|
|
matchers[0];
|
|
}
|
|
|
|
function multipleContexts( selector, contexts, results ) {
|
|
var i = 0,
|
|
len = contexts.length;
|
|
for ( ; i < len; i++ ) {
|
|
Sizzle( selector, contexts[i], results );
|
|
}
|
|
return results;
|
|
}
|
|
|
|
function condense( unmatched, map, filter, context, xml ) {
|
|
var elem,
|
|
newUnmatched = [],
|
|
i = 0,
|
|
len = unmatched.length,
|
|
mapped = map != null;
|
|
|
|
for ( ; i < len; i++ ) {
|
|
if ( (elem = unmatched[i]) ) {
|
|
if ( !filter || filter( elem, context, xml ) ) {
|
|
newUnmatched.push( elem );
|
|
if ( mapped ) {
|
|
map.push( i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return newUnmatched;
|
|
}
|
|
|
|
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
|
|
if ( postFilter && !postFilter[ expando ] ) {
|
|
postFilter = setMatcher( postFilter );
|
|
}
|
|
if ( postFinder && !postFinder[ expando ] ) {
|
|
postFinder = setMatcher( postFinder, postSelector );
|
|
}
|
|
return markFunction(function( seed, results, context, xml ) {
|
|
var temp, i, elem,
|
|
preMap = [],
|
|
postMap = [],
|
|
preexisting = results.length,
|
|
|
|
// Get initial elements from seed or context
|
|
elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
|
|
|
|
// Prefilter to get matcher input, preserving a map for seed-results synchronization
|
|
matcherIn = preFilter && ( seed || !selector ) ?
|
|
condense( elems, preMap, preFilter, context, xml ) :
|
|
elems,
|
|
|
|
matcherOut = matcher ?
|
|
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
|
|
postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
|
|
|
|
// ...intermediate processing is necessary
|
|
[] :
|
|
|
|
// ...otherwise use results directly
|
|
results :
|
|
matcherIn;
|
|
|
|
// Find primary matches
|
|
if ( matcher ) {
|
|
matcher( matcherIn, matcherOut, context, xml );
|
|
}
|
|
|
|
// Apply postFilter
|
|
if ( postFilter ) {
|
|
temp = condense( matcherOut, postMap );
|
|
postFilter( temp, [], context, xml );
|
|
|
|
// Un-match failing elements by moving them back to matcherIn
|
|
i = temp.length;
|
|
while ( i-- ) {
|
|
if ( (elem = temp[i]) ) {
|
|
matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( seed ) {
|
|
if ( postFinder || preFilter ) {
|
|
if ( postFinder ) {
|
|
// Get the final matcherOut by condensing this intermediate into postFinder contexts
|
|
temp = [];
|
|
i = matcherOut.length;
|
|
while ( i-- ) {
|
|
if ( (elem = matcherOut[i]) ) {
|
|
// Restore matcherIn since elem is not yet a final match
|
|
temp.push( (matcherIn[i] = elem) );
|
|
}
|
|
}
|
|
postFinder( null, (matcherOut = []), temp, xml );
|
|
}
|
|
|
|
// Move matched elements from seed to results to keep them synchronized
|
|
i = matcherOut.length;
|
|
while ( i-- ) {
|
|
if ( (elem = matcherOut[i]) &&
|
|
(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
|
|
|
|
seed[temp] = !(results[temp] = elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add elements to results, through postFinder if defined
|
|
} else {
|
|
matcherOut = condense(
|
|
matcherOut === results ?
|
|
matcherOut.splice( preexisting, matcherOut.length ) :
|
|
matcherOut
|
|
);
|
|
if ( postFinder ) {
|
|
postFinder( null, results, matcherOut, xml );
|
|
} else {
|
|
push.apply( results, matcherOut );
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function matcherFromTokens( tokens ) {
|
|
var checkContext, matcher, j,
|
|
len = tokens.length,
|
|
leadingRelative = Expr.relative[ tokens[0].type ],
|
|
implicitRelative = leadingRelative || Expr.relative[" "],
|
|
i = leadingRelative ? 1 : 0,
|
|
|
|
// The foundational matcher ensures that elements are reachable from top-level context(s)
|
|
matchContext = addCombinator( function( elem ) {
|
|
return elem === checkContext;
|
|
}, implicitRelative, true ),
|
|
matchAnyContext = addCombinator( function( elem ) {
|
|
return indexOf( checkContext, elem ) > -1;
|
|
}, implicitRelative, true ),
|
|
matchers = [ function( elem, context, xml ) {
|
|
var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
|
|
(checkContext = context).nodeType ?
|
|
matchContext( elem, context, xml ) :
|
|
matchAnyContext( elem, context, xml ) );
|
|
// Avoid hanging onto element (issue #299)
|
|
checkContext = null;
|
|
return ret;
|
|
} ];
|
|
|
|
for ( ; i < len; i++ ) {
|
|
if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
|
|
matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
|
|
} else {
|
|
matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
|
|
|
|
// Return special upon seeing a positional matcher
|
|
if ( matcher[ expando ] ) {
|
|
// Find the next relative operator (if any) for proper handling
|
|
j = ++i;
|
|
for ( ; j < len; j++ ) {
|
|
if ( Expr.relative[ tokens[j].type ] ) {
|
|
break;
|
|
}
|
|
}
|
|
return setMatcher(
|
|
i > 1 && elementMatcher( matchers ),
|
|
i > 1 && toSelector(
|
|
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
|
|
tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
|
|
).replace( rtrim, "$1" ),
|
|
matcher,
|
|
i < j && matcherFromTokens( tokens.slice( i, j ) ),
|
|
j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
|
|
j < len && toSelector( tokens )
|
|
);
|
|
}
|
|
matchers.push( matcher );
|
|
}
|
|
}
|
|
|
|
return elementMatcher( matchers );
|
|
}
|
|
|
|
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
|
|
var bySet = setMatchers.length > 0,
|
|
byElement = elementMatchers.length > 0,
|
|
superMatcher = function( seed, context, xml, results, outermost ) {
|
|
var elem, j, matcher,
|
|
matchedCount = 0,
|
|
i = "0",
|
|
unmatched = seed && [],
|
|
setMatched = [],
|
|
contextBackup = outermostContext,
|
|
// We must always have either seed elements or outermost context
|
|
elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
|
|
// Use integer dirruns iff this is the outermost matcher
|
|
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
|
|
len = elems.length;
|
|
|
|
if ( outermost ) {
|
|
outermostContext = context === document || context || outermost;
|
|
}
|
|
|
|
// Add elements passing elementMatchers directly to results
|
|
// Support: IE<9, Safari
|
|
// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
|
|
for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
|
|
if ( byElement && elem ) {
|
|
j = 0;
|
|
if ( !context && elem.ownerDocument !== document ) {
|
|
setDocument( elem );
|
|
xml = !documentIsHTML;
|
|
}
|
|
while ( (matcher = elementMatchers[j++]) ) {
|
|
if ( matcher( elem, context || document, xml) ) {
|
|
results.push( elem );
|
|
break;
|
|
}
|
|
}
|
|
if ( outermost ) {
|
|
dirruns = dirrunsUnique;
|
|
}
|
|
}
|
|
|
|
// Track unmatched elements for set filters
|
|
if ( bySet ) {
|
|
// They will have gone through all possible matchers
|
|
if ( (elem = !matcher && elem) ) {
|
|
matchedCount--;
|
|
}
|
|
|
|
// Lengthen the array for every element, matched or not
|
|
if ( seed ) {
|
|
unmatched.push( elem );
|
|
}
|
|
}
|
|
}
|
|
|
|
// `i` is now the count of elements visited above, and adding it to `matchedCount`
|
|
// makes the latter nonnegative.
|
|
matchedCount += i;
|
|
|
|
// Apply set filters to unmatched elements
|
|
// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
|
|
// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
|
|
// no element matchers and no seed.
|
|
// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
|
|
// case, which will result in a "00" `matchedCount` that differs from `i` but is also
|
|
// numerically zero.
|
|
if ( bySet && i !== matchedCount ) {
|
|
j = 0;
|
|
while ( (matcher = setMatchers[j++]) ) {
|
|
matcher( unmatched, setMatched, context, xml );
|
|
}
|
|
|
|
if ( seed ) {
|
|
// Reintegrate element matches to eliminate the need for sorting
|
|
if ( matchedCount > 0 ) {
|
|
while ( i-- ) {
|
|
if ( !(unmatched[i] || setMatched[i]) ) {
|
|
setMatched[i] = pop.call( results );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Discard index placeholder values to get only actual matches
|
|
setMatched = condense( setMatched );
|
|
}
|
|
|
|
// Add matches to results
|
|
push.apply( results, setMatched );
|
|
|
|
// Seedless set matches succeeding multiple successful matchers stipulate sorting
|
|
if ( outermost && !seed && setMatched.length > 0 &&
|
|
( matchedCount + setMatchers.length ) > 1 ) {
|
|
|
|
Sizzle.uniqueSort( results );
|
|
}
|
|
}
|
|
|
|
// Override manipulation of globals by nested matchers
|
|
if ( outermost ) {
|
|
dirruns = dirrunsUnique;
|
|
outermostContext = contextBackup;
|
|
}
|
|
|
|
return unmatched;
|
|
};
|
|
|
|
return bySet ?
|
|
markFunction( superMatcher ) :
|
|
superMatcher;
|
|
}
|
|
|
|
compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
|
|
var i,
|
|
setMatchers = [],
|
|
elementMatchers = [],
|
|
cached = compilerCache[ selector + " " ];
|
|
|
|
if ( !cached ) {
|
|
// Generate a function of recursive functions that can be used to check each element
|
|
if ( !match ) {
|
|
match = tokenize( selector );
|
|
}
|
|
i = match.length;
|
|
while ( i-- ) {
|
|
cached = matcherFromTokens( match[i] );
|
|
if ( cached[ expando ] ) {
|
|
setMatchers.push( cached );
|
|
} else {
|
|
elementMatchers.push( cached );
|
|
}
|
|
}
|
|
|
|
// Cache the compiled function
|
|
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
|
|
|
|
// Save selector and tokenization
|
|
cached.selector = selector;
|
|
}
|
|
return cached;
|
|
};
|
|
|
|
/**
|
|
* A low-level selection function that works with Sizzle's compiled
|
|
* selector functions
|
|
* @param {String|Function} selector A selector or a pre-compiled
|
|
* selector function built with Sizzle.compile
|
|
* @param {Element} context
|
|
* @param {Array} [results]
|
|
* @param {Array} [seed] A set of elements to match against
|
|
*/
|
|
select = Sizzle.select = function( selector, context, results, seed ) {
|
|
var i, tokens, token, type, find,
|
|
compiled = typeof selector === "function" && selector,
|
|
match = !seed && tokenize( (selector = compiled.selector || selector) );
|
|
|
|
results = results || [];
|
|
|
|
// Try to minimize operations if there is only one selector in the list and no seed
|
|
// (the latter of which guarantees us context)
|
|
if ( match.length === 1 ) {
|
|
|
|
// Reduce context if the leading compound selector is an ID
|
|
tokens = match[0] = match[0].slice( 0 );
|
|
if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
|
|
support.getById && context.nodeType === 9 && documentIsHTML &&
|
|
Expr.relative[ tokens[1].type ] ) {
|
|
|
|
context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
|
|
if ( !context ) {
|
|
return results;
|
|
|
|
// Precompiled matchers will still verify ancestry, so step up a level
|
|
} else if ( compiled ) {
|
|
context = context.parentNode;
|
|
}
|
|
|
|
selector = selector.slice( tokens.shift().value.length );
|
|
}
|
|
|
|
// Fetch a seed set for right-to-left matching
|
|
i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
|
|
while ( i-- ) {
|
|
token = tokens[i];
|
|
|
|
// Abort if we hit a combinator
|
|
if ( Expr.relative[ (type = token.type) ] ) {
|
|
break;
|
|
}
|
|
if ( (find = Expr.find[ type ]) ) {
|
|
// Search, expanding context for leading sibling combinators
|
|
if ( (seed = find(
|
|
token.matches[0].replace( runescape, funescape ),
|
|
rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
|
|
)) ) {
|
|
|
|
// If seed is empty or no tokens remain, we can return early
|
|
tokens.splice( i, 1 );
|
|
selector = seed.length && toSelector( tokens );
|
|
if ( !selector ) {
|
|
push.apply( results, seed );
|
|
return results;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compile and execute a filtering function if one is not provided
|
|
// Provide `match` to avoid retokenization if we modified the selector above
|
|
( compiled || compile( selector, match ) )(
|
|
seed,
|
|
context,
|
|
!documentIsHTML,
|
|
results,
|
|
!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
|
|
);
|
|
return results;
|
|
};
|
|
|
|
// One-time assignments
|
|
|
|
// Sort stability
|
|
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
|
|
|
|
// Support: Chrome 14-35+
|
|
// Always assume duplicates if they aren't passed to the comparison function
|
|
support.detectDuplicates = !!hasDuplicate;
|
|
|
|
// Initialize against the default document
|
|
setDocument();
|
|
|
|
// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
|
|
// Detached nodes confoundingly follow *each other*
|
|
support.sortDetached = assert(function( div1 ) {
|
|
// Should return 1, but returns 4 (following)
|
|
return div1.compareDocumentPosition( document.createElement("div") ) & 1;
|
|
});
|
|
|
|
// Support: IE<8
|
|
// Prevent attribute/property "interpolation"
|
|
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
|
|
if ( !assert(function( div ) {
|
|
div.innerHTML = "<a href='#'></a>";
|
|
return div.firstChild.getAttribute("href") === "#" ;
|
|
}) ) {
|
|
addHandle( "type|href|height|width", function( elem, name, isXML ) {
|
|
if ( !isXML ) {
|
|
return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
|
|
}
|
|
});
|
|
}
|
|
|
|
// Support: IE<9
|
|
// Use defaultValue in place of getAttribute("value")
|
|
if ( !support.attributes || !assert(function( div ) {
|
|
div.innerHTML = "<input/>";
|
|
div.firstChild.setAttribute( "value", "" );
|
|
return div.firstChild.getAttribute( "value" ) === "";
|
|
}) ) {
|
|
addHandle( "value", function( elem, name, isXML ) {
|
|
if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
|
|
return elem.defaultValue;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Support: IE<9
|
|
// Use getAttributeNode to fetch booleans when getAttribute lies
|
|
if ( !assert(function( div ) {
|
|
return div.getAttribute("disabled") == null;
|
|
}) ) {
|
|
addHandle( booleans, function( elem, name, isXML ) {
|
|
var val;
|
|
if ( !isXML ) {
|
|
return elem[ name ] === true ? name.toLowerCase() :
|
|
(val = elem.getAttributeNode( name )) && val.specified ?
|
|
val.value :
|
|
null;
|
|
}
|
|
});
|
|
}
|
|
|
|
return Sizzle;
|
|
|
|
})( window );
|
|
|
|
|
|
|
|
jQuery.find = Sizzle;
|
|
jQuery.expr = Sizzle.selectors;
|
|
jQuery.expr[ ":" ] = jQuery.expr.pseudos;
|
|
jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
|
|
jQuery.text = Sizzle.getText;
|
|
jQuery.isXMLDoc = Sizzle.isXML;
|
|
jQuery.contains = Sizzle.contains;
|
|
|
|
|
|
|
|
var dir = function( elem, dir, until ) {
|
|
var matched = [],
|
|
truncate = until !== undefined;
|
|
|
|
while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
|
|
if ( elem.nodeType === 1 ) {
|
|
if ( truncate && jQuery( elem ).is( until ) ) {
|
|
break;
|
|
}
|
|
matched.push( elem );
|
|
}
|
|
}
|
|
return matched;
|
|
};
|
|
|
|
|
|
var siblings = function( n, elem ) {
|
|
var matched = [];
|
|
|
|
for ( ; n; n = n.nextSibling ) {
|
|
if ( n.nodeType === 1 && n !== elem ) {
|
|
matched.push( n );
|
|
}
|
|
}
|
|
|
|
return matched;
|
|
};
|
|
|
|
|
|
var rneedsContext = jQuery.expr.match.needsContext;
|
|
|
|
var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ );
|
|
|
|
|
|
|
|
var risSimple = /^.[^:#\[\.,]*$/;
|
|
|
|
// Implement the identical functionality for filter and not
|
|
function winnow( elements, qualifier, not ) {
|
|
if ( jQuery.isFunction( qualifier ) ) {
|
|
return jQuery.grep( elements, function( elem, i ) {
|
|
/* jshint -W018 */
|
|
return !!qualifier.call( elem, i, elem ) !== not;
|
|
} );
|
|
|
|
}
|
|
|
|
if ( qualifier.nodeType ) {
|
|
return jQuery.grep( elements, function( elem ) {
|
|
return ( elem === qualifier ) !== not;
|
|
} );
|
|
|
|
}
|
|
|
|
if ( typeof qualifier === "string" ) {
|
|
if ( risSimple.test( qualifier ) ) {
|
|
return jQuery.filter( qualifier, elements, not );
|
|
}
|
|
|
|
qualifier = jQuery.filter( qualifier, elements );
|
|
}
|
|
|
|
return jQuery.grep( elements, function( elem ) {
|
|
return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
|
|
} );
|
|
}
|
|
|
|
jQuery.filter = function( expr, elems, not ) {
|
|
var elem = elems[ 0 ];
|
|
|
|
if ( not ) {
|
|
expr = ":not(" + expr + ")";
|
|
}
|
|
|
|
return elems.length === 1 && elem.nodeType === 1 ?
|
|
jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
|
|
jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
|
|
return elem.nodeType === 1;
|
|
} ) );
|
|
};
|
|
|
|
jQuery.fn.extend( {
|
|
find: function( selector ) {
|
|
var i,
|
|
len = this.length,
|
|
ret = [],
|
|
self = this;
|
|
|
|
if ( typeof selector !== "string" ) {
|
|
return this.pushStack( jQuery( selector ).filter( function() {
|
|
for ( i = 0; i < len; i++ ) {
|
|
if ( jQuery.contains( self[ i ], this ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
} ) );
|
|
}
|
|
|
|
for ( i = 0; i < len; i++ ) {
|
|
jQuery.find( selector, self[ i ], ret );
|
|
}
|
|
|
|
// Needed because $( selector, context ) becomes $( context ).find( selector )
|
|
ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
|
|
ret.selector = this.selector ? this.selector + " " + selector : selector;
|
|
return ret;
|
|
},
|
|
filter: function( selector ) {
|
|
return this.pushStack( winnow( this, selector || [], false ) );
|
|
},
|
|
not: function( selector ) {
|
|
return this.pushStack( winnow( this, selector || [], true ) );
|
|
},
|
|
is: function( selector ) {
|
|
return !!winnow(
|
|
this,
|
|
|
|
// If this is a positional/relative selector, check membership in the returned set
|
|
// so $("p:first").is("p:last") won't return true for a doc with two "p".
|
|
typeof selector === "string" && rneedsContext.test( selector ) ?
|
|
jQuery( selector ) :
|
|
selector || [],
|
|
false
|
|
).length;
|
|
}
|
|
} );
|
|
|
|
|
|
// Initialize a jQuery object
|
|
|
|
|
|
// A central reference to the root jQuery(document)
|
|
var rootjQuery,
|
|
|
|
// A simple way to check for HTML strings
|
|
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
|
|
// Strict HTML recognition (#11290: must start with <)
|
|
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
|
|
|
|
init = jQuery.fn.init = function( selector, context, root ) {
|
|
var match, elem;
|
|
|
|
// HANDLE: $(""), $(null), $(undefined), $(false)
|
|
if ( !selector ) {
|
|
return this;
|
|
}
|
|
|
|
// Method init() accepts an alternate rootjQuery
|
|
// so migrate can support jQuery.sub (gh-2101)
|
|
root = root || rootjQuery;
|
|
|
|
// Handle HTML strings
|
|
if ( typeof selector === "string" ) {
|
|
if ( selector[ 0 ] === "<" &&
|
|
selector[ selector.length - 1 ] === ">" &&
|
|
selector.length >= 3 ) {
|
|
|
|
// Assume that strings that start and end with <> are HTML and skip the regex check
|
|
match = [ null, selector, null ];
|
|
|
|
} else {
|
|
match = rquickExpr.exec( selector );
|
|
}
|
|
|
|
// Match html or make sure no context is specified for #id
|
|
if ( match && ( match[ 1 ] || !context ) ) {
|
|
|
|
// HANDLE: $(html) -> $(array)
|
|
if ( match[ 1 ] ) {
|
|
context = context instanceof jQuery ? context[ 0 ] : context;
|
|
|
|
// Option to run scripts is true for back-compat
|
|
// Intentionally let the error be thrown if parseHTML is not present
|
|
jQuery.merge( this, jQuery.parseHTML(
|
|
match[ 1 ],
|
|
context && context.nodeType ? context.ownerDocument || context : document,
|
|
true
|
|
) );
|
|
|
|
// HANDLE: $(html, props)
|
|
if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
|
|
for ( match in context ) {
|
|
|
|
// Properties of context are called as methods if possible
|
|
if ( jQuery.isFunction( this[ match ] ) ) {
|
|
this[ match ]( context[ match ] );
|
|
|
|
// ...and otherwise set as attributes
|
|
} else {
|
|
this.attr( match, context[ match ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
|
|
// HANDLE: $(#id)
|
|
} else {
|
|
elem = document.getElementById( match[ 2 ] );
|
|
|
|
// Support: Blackberry 4.6
|
|
// gEBID returns nodes no longer in the document (#6963)
|
|
if ( elem && elem.parentNode ) {
|
|
|
|
// Inject the element directly into the jQuery object
|
|
this.length = 1;
|
|
this[ 0 ] = elem;
|
|
}
|
|
|
|
this.context = document;
|
|
this.selector = selector;
|
|
return this;
|
|
}
|
|
|
|
// HANDLE: $(expr, $(...))
|
|
} else if ( !context || context.jquery ) {
|
|
return ( context || root ).find( selector );
|
|
|
|
// HANDLE: $(expr, context)
|
|
// (which is just equivalent to: $(context).find(expr)
|
|
} else {
|
|
return this.constructor( context ).find( selector );
|
|
}
|
|
|
|
// HANDLE: $(DOMElement)
|
|
} else if ( selector.nodeType ) {
|
|
this.context = this[ 0 ] = selector;
|
|
this.length = 1;
|
|
return this;
|
|
|
|
// HANDLE: $(function)
|
|
// Shortcut for document ready
|
|
} else if ( jQuery.isFunction( selector ) ) {
|
|
return root.ready !== undefined ?
|
|
root.ready( selector ) :
|
|
|
|
// Execute immediately if ready is not present
|
|
selector( jQuery );
|
|
}
|
|
|
|
if ( selector.selector !== undefined ) {
|
|
this.selector = selector.selector;
|
|
this.context = selector.context;
|
|
}
|
|
|
|
return jQuery.makeArray( selector, this );
|
|
};
|
|
|
|
// Give the init function the jQuery prototype for later instantiation
|
|
init.prototype = jQuery.fn;
|
|
|
|
// Initialize central reference
|
|
rootjQuery = jQuery( document );
|
|
|
|
|
|
var rparentsprev = /^(?:parents|prev(?:Until|All))/,
|
|
|
|
// Methods guaranteed to produce a unique set when starting from a unique set
|
|
guaranteedUnique = {
|
|
children: true,
|
|
contents: true,
|
|
next: true,
|
|
prev: true
|
|
};
|
|
|
|
jQuery.fn.extend( {
|
|
has: function( target ) {
|
|
var targets = jQuery( target, this ),
|
|
l = targets.length;
|
|
|
|
return this.filter( function() {
|
|
var i = 0;
|
|
for ( ; i < l; i++ ) {
|
|
if ( jQuery.contains( this, targets[ i ] ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
} );
|
|
},
|
|
|
|
closest: function( selectors, context ) {
|
|
var cur,
|
|
i = 0,
|
|
l = this.length,
|
|
matched = [],
|
|
pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
|
|
jQuery( selectors, context || this.context ) :
|
|
0;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
|
|
|
|
// Always skip document fragments
|
|
if ( cur.nodeType < 11 && ( pos ?
|
|
pos.index( cur ) > -1 :
|
|
|
|
// Don't pass non-elements to Sizzle
|
|
cur.nodeType === 1 &&
|
|
jQuery.find.matchesSelector( cur, selectors ) ) ) {
|
|
|
|
matched.push( cur );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
|
|
},
|
|
|
|
// Determine the position of an element within the set
|
|
index: function( elem ) {
|
|
|
|
// No argument, return index in parent
|
|
if ( !elem ) {
|
|
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
|
|
}
|
|
|
|
// Index in selector
|
|
if ( typeof elem === "string" ) {
|
|
return indexOf.call( jQuery( elem ), this[ 0 ] );
|
|
}
|
|
|
|
// Locate the position of the desired element
|
|
return indexOf.call( this,
|
|
|
|
// If it receives a jQuery object, the first element is used
|
|
elem.jquery ? elem[ 0 ] : elem
|
|
);
|
|
},
|
|
|
|
add: function( selector, context ) {
|
|
return this.pushStack(
|
|
jQuery.uniqueSort(
|
|
jQuery.merge( this.get(), jQuery( selector, context ) )
|
|
)
|
|
);
|
|
},
|
|
|
|
addBack: function( selector ) {
|
|
return this.add( selector == null ?
|
|
this.prevObject : this.prevObject.filter( selector )
|
|
);
|
|
}
|
|
} );
|
|
|
|
function sibling( cur, dir ) {
|
|
while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
|
|
return cur;
|
|
}
|
|
|
|
jQuery.each( {
|
|
parent: function( elem ) {
|
|
var parent = elem.parentNode;
|
|
return parent && parent.nodeType !== 11 ? parent : null;
|
|
},
|
|
parents: function( elem ) {
|
|
return dir( elem, "parentNode" );
|
|
},
|
|
parentsUntil: function( elem, i, until ) {
|
|
return dir( elem, "parentNode", until );
|
|
},
|
|
next: function( elem ) {
|
|
return sibling( elem, "nextSibling" );
|
|
},
|
|
prev: function( elem ) {
|
|
return sibling( elem, "previousSibling" );
|
|
},
|
|
nextAll: function( elem ) {
|
|
return dir( elem, "nextSibling" );
|
|
},
|
|
prevAll: function( elem ) {
|
|
return dir( elem, "previousSibling" );
|
|
},
|
|
nextUntil: function( elem, i, until ) {
|
|
return dir( elem, "nextSibling", until );
|
|
},
|
|
prevUntil: function( elem, i, until ) {
|
|
return dir( elem, "previousSibling", until );
|
|
},
|
|
siblings: function( elem ) {
|
|
return siblings( ( elem.parentNode || {} ).firstChild, elem );
|
|
},
|
|
children: function( elem ) {
|
|
return siblings( elem.firstChild );
|
|
},
|
|
contents: function( elem ) {
|
|
return elem.contentDocument || jQuery.merge( [], elem.childNodes );
|
|
}
|
|
}, function( name, fn ) {
|
|
jQuery.fn[ name ] = function( until, selector ) {
|
|
var matched = jQuery.map( this, fn, until );
|
|
|
|
if ( name.slice( -5 ) !== "Until" ) {
|
|
selector = until;
|
|
}
|
|
|
|
if ( selector && typeof selector === "string" ) {
|
|
matched = jQuery.filter( selector, matched );
|
|
}
|
|
|
|
if ( this.length > 1 ) {
|
|
|
|
// Remove duplicates
|
|
if ( !guaranteedUnique[ name ] ) {
|
|
jQuery.uniqueSort( matched );
|
|
}
|
|
|
|
// Reverse order for parents* and prev-derivatives
|
|
if ( rparentsprev.test( name ) ) {
|
|
matched.reverse();
|
|
}
|
|
}
|
|
|
|
return this.pushStack( matched );
|
|
};
|
|
} );
|
|
var rnotwhite = ( /\S+/g );
|
|
|
|
|
|
|
|
// Convert String-formatted options into Object-formatted ones
|
|
function createOptions( options ) {
|
|
var object = {};
|
|
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
|
|
object[ flag ] = true;
|
|
} );
|
|
return object;
|
|
}
|
|
|
|
/*
|
|
* Create a callback list using the following parameters:
|
|
*
|
|
* options: an optional list of space-separated options that will change how
|
|
* the callback list behaves or a more traditional option object
|
|
*
|
|
* By default a callback list will act like an event callback list and can be
|
|
* "fired" multiple times.
|
|
*
|
|
* Possible options:
|
|
*
|
|
* once: will ensure the callback list can only be fired once (like a Deferred)
|
|
*
|
|
* memory: will keep track of previous values and will call any callback added
|
|
* after the list has been fired right away with the latest "memorized"
|
|
* values (like a Deferred)
|
|
*
|
|
* unique: will ensure a callback can only be added once (no duplicate in the list)
|
|
*
|
|
* stopOnFalse: interrupt callings when a callback returns false
|
|
*
|
|
*/
|
|
jQuery.Callbacks = function( options ) {
|
|
|
|
// Convert options from String-formatted to Object-formatted if needed
|
|
// (we check in cache first)
|
|
options = typeof options === "string" ?
|
|
createOptions( options ) :
|
|
jQuery.extend( {}, options );
|
|
|
|
var // Flag to know if list is currently firing
|
|
firing,
|
|
|
|
// Last fire value for non-forgettable lists
|
|
memory,
|
|
|
|
// Flag to know if list was already fired
|
|
fired,
|
|
|
|
// Flag to prevent firing
|
|
locked,
|
|
|
|
// Actual callback list
|
|
list = [],
|
|
|
|
// Queue of execution data for repeatable lists
|
|
queue = [],
|
|
|
|
// Index of currently firing callback (modified by add/remove as needed)
|
|
firingIndex = -1,
|
|
|
|
// Fire callbacks
|
|
fire = function() {
|
|
|
|
// Enforce single-firing
|
|
locked = options.once;
|
|
|
|
// Execute callbacks for all pending executions,
|
|
// respecting firingIndex overrides and runtime changes
|
|
fired = firing = true;
|
|
for ( ; queue.length; firingIndex = -1 ) {
|
|
memory = queue.shift();
|
|
while ( ++firingIndex < list.length ) {
|
|
|
|
// Run callback and check for early termination
|
|
if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
|
|
options.stopOnFalse ) {
|
|
|
|
// Jump to end and forget the data so .add doesn't re-fire
|
|
firingIndex = list.length;
|
|
memory = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Forget the data if we're done with it
|
|
if ( !options.memory ) {
|
|
memory = false;
|
|
}
|
|
|
|
firing = false;
|
|
|
|
// Clean up if we're done firing for good
|
|
if ( locked ) {
|
|
|
|
// Keep an empty list if we have data for future add calls
|
|
if ( memory ) {
|
|
list = [];
|
|
|
|
// Otherwise, this object is spent
|
|
} else {
|
|
list = "";
|
|
}
|
|
}
|
|
},
|
|
|
|
// Actual Callbacks object
|
|
self = {
|
|
|
|
// Add a callback or a collection of callbacks to the list
|
|
add: function() {
|
|
if ( list ) {
|
|
|
|
// If we have memory from a past run, we should fire after adding
|
|
if ( memory && !firing ) {
|
|
firingIndex = list.length - 1;
|
|
queue.push( memory );
|
|
}
|
|
|
|
( function add( args ) {
|
|
jQuery.each( args, function( _, arg ) {
|
|
if ( jQuery.isFunction( arg ) ) {
|
|
if ( !options.unique || !self.has( arg ) ) {
|
|
list.push( arg );
|
|
}
|
|
} else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
|
|
|
|
// Inspect recursively
|
|
add( arg );
|
|
}
|
|
} );
|
|
} )( arguments );
|
|
|
|
if ( memory && !firing ) {
|
|
fire();
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Remove a callback from the list
|
|
remove: function() {
|
|
jQuery.each( arguments, function( _, arg ) {
|
|
var index;
|
|
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
|
|
list.splice( index, 1 );
|
|
|
|
// Handle firing indexes
|
|
if ( index <= firingIndex ) {
|
|
firingIndex--;
|
|
}
|
|
}
|
|
} );
|
|
return this;
|
|
},
|
|
|
|
// Check if a given callback is in the list.
|
|
// If no argument is given, return whether or not list has callbacks attached.
|
|
has: function( fn ) {
|
|
return fn ?
|
|
jQuery.inArray( fn, list ) > -1 :
|
|
list.length > 0;
|
|
},
|
|
|
|
// Remove all callbacks from the list
|
|
empty: function() {
|
|
if ( list ) {
|
|
list = [];
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Disable .fire and .add
|
|
// Abort any current/pending executions
|
|
// Clear all callbacks and values
|
|
disable: function() {
|
|
locked = queue = [];
|
|
list = memory = "";
|
|
return this;
|
|
},
|
|
disabled: function() {
|
|
return !list;
|
|
},
|
|
|
|
// Disable .fire
|
|
// Also disable .add unless we have memory (since it would have no effect)
|
|
// Abort any pending executions
|
|
lock: function() {
|
|
locked = queue = [];
|
|
if ( !memory ) {
|
|
list = memory = "";
|
|
}
|
|
return this;
|
|
},
|
|
locked: function() {
|
|
return !!locked;
|
|
},
|
|
|
|
// Call all callbacks with the given context and arguments
|
|
fireWith: function( context, args ) {
|
|
if ( !locked ) {
|
|
args = args || [];
|
|
args = [ context, args.slice ? args.slice() : args ];
|
|
queue.push( args );
|
|
if ( !firing ) {
|
|
fire();
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Call all the callbacks with the given arguments
|
|
fire: function() {
|
|
self.fireWith( this, arguments );
|
|
return this;
|
|
},
|
|
|
|
// To know if the callbacks have already been called at least once
|
|
fired: function() {
|
|
return !!fired;
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
|
|
jQuery.extend( {
|
|
|
|
Deferred: function( func ) {
|
|
var tuples = [
|
|
|
|
// action, add listener, listener list, final state
|
|
[ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ],
|
|
[ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ],
|
|
[ "notify", "progress", jQuery.Callbacks( "memory" ) ]
|
|
],
|
|
state = "pending",
|
|
promise = {
|
|
state: function() {
|
|
return state;
|
|
},
|
|
always: function() {
|
|
deferred.done( arguments ).fail( arguments );
|
|
return this;
|
|
},
|
|
then: function( /* fnDone, fnFail, fnProgress */ ) {
|
|
var fns = arguments;
|
|
return jQuery.Deferred( function( newDefer ) {
|
|
jQuery.each( tuples, function( i, tuple ) {
|
|
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
|
|
|
|
// deferred[ done | fail | progress ] for forwarding actions to newDefer
|
|
deferred[ tuple[ 1 ] ]( function() {
|
|
var returned = fn && fn.apply( this, arguments );
|
|
if ( returned && jQuery.isFunction( returned.promise ) ) {
|
|
returned.promise()
|
|
.progress( newDefer.notify )
|
|
.done( newDefer.resolve )
|
|
.fail( newDefer.reject );
|
|
} else {
|
|
newDefer[ tuple[ 0 ] + "With" ](
|
|
this === promise ? newDefer.promise() : this,
|
|
fn ? [ returned ] : arguments
|
|
);
|
|
}
|
|
} );
|
|
} );
|
|
fns = null;
|
|
} ).promise();
|
|
},
|
|
|
|
// Get a promise for this deferred
|
|
// If obj is provided, the promise aspect is added to the object
|
|
promise: function( obj ) {
|
|
return obj != null ? jQuery.extend( obj, promise ) : promise;
|
|
}
|
|
},
|
|
deferred = {};
|
|
|
|
// Keep pipe for back-compat
|
|
promise.pipe = promise.then;
|
|
|
|
// Add list-specific methods
|
|
jQuery.each( tuples, function( i, tuple ) {
|
|
var list = tuple[ 2 ],
|
|
stateString = tuple[ 3 ];
|
|
|
|
// promise[ done | fail | progress ] = list.add
|
|
promise[ tuple[ 1 ] ] = list.add;
|
|
|
|
// Handle state
|
|
if ( stateString ) {
|
|
list.add( function() {
|
|
|
|
// state = [ resolved | rejected ]
|
|
state = stateString;
|
|
|
|
// [ reject_list | resolve_list ].disable; progress_list.lock
|
|
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
|
|
}
|
|
|
|
// deferred[ resolve | reject | notify ]
|
|
deferred[ tuple[ 0 ] ] = function() {
|
|
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
|
|
return this;
|
|
};
|
|
deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
|
|
} );
|
|
|
|
// Make the deferred a promise
|
|
promise.promise( deferred );
|
|
|
|
// Call given func if any
|
|
if ( func ) {
|
|
func.call( deferred, deferred );
|
|
}
|
|
|
|
// All done!
|
|
return deferred;
|
|
},
|
|
|
|
// Deferred helper
|
|
when: function( subordinate /* , ..., subordinateN */ ) {
|
|
var i = 0,
|
|
resolveValues = slice.call( arguments ),
|
|
length = resolveValues.length,
|
|
|
|
// the count of uncompleted subordinates
|
|
remaining = length !== 1 ||
|
|
( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
|
|
|
|
// the master Deferred.
|
|
// If resolveValues consist of only a single Deferred, just use that.
|
|
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
|
|
|
|
// Update function for both resolve and progress values
|
|
updateFunc = function( i, contexts, values ) {
|
|
return function( value ) {
|
|
contexts[ i ] = this;
|
|
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
|
|
if ( values === progressValues ) {
|
|
deferred.notifyWith( contexts, values );
|
|
} else if ( !( --remaining ) ) {
|
|
deferred.resolveWith( contexts, values );
|
|
}
|
|
};
|
|
},
|
|
|
|
progressValues, progressContexts, resolveContexts;
|
|
|
|
// Add listeners to Deferred subordinates; treat others as resolved
|
|
if ( length > 1 ) {
|
|
progressValues = new Array( length );
|
|
progressContexts = new Array( length );
|
|
resolveContexts = new Array( length );
|
|
for ( ; i < length; i++ ) {
|
|
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
|
|
resolveValues[ i ].promise()
|
|
.progress( updateFunc( i, progressContexts, progressValues ) )
|
|
.done( updateFunc( i, resolveContexts, resolveValues ) )
|
|
.fail( deferred.reject );
|
|
} else {
|
|
--remaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we're not waiting on anything, resolve the master
|
|
if ( !remaining ) {
|
|
deferred.resolveWith( resolveContexts, resolveValues );
|
|
}
|
|
|
|
return deferred.promise();
|
|
}
|
|
} );
|
|
|
|
|
|
// The deferred used on DOM ready
|
|
var readyList;
|
|
|
|
jQuery.fn.ready = function( fn ) {
|
|
|
|
// Add the callback
|
|
jQuery.ready.promise().done( fn );
|
|
|
|
return this;
|
|
};
|
|
|
|
jQuery.extend( {
|
|
|
|
// Is the DOM ready to be used? Set to true once it occurs.
|
|
isReady: false,
|
|
|
|
// A counter to track how many items to wait for before
|
|
// the ready event fires. See #6781
|
|
readyWait: 1,
|
|
|
|
// Hold (or release) the ready event
|
|
holdReady: function( hold ) {
|
|
if ( hold ) {
|
|
jQuery.readyWait++;
|
|
} else {
|
|
jQuery.ready( true );
|
|
}
|
|
},
|
|
|
|
// Handle when the DOM is ready
|
|
ready: function( wait ) {
|
|
|
|
// Abort if there are pending holds or we're already ready
|
|
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
|
|
return;
|
|
}
|
|
|
|
// Remember that the DOM is ready
|
|
jQuery.isReady = true;
|
|
|
|
// If a normal DOM Ready event fired, decrement, and wait if need be
|
|
if ( wait !== true && --jQuery.readyWait > 0 ) {
|
|
return;
|
|
}
|
|
|
|
// If there are functions bound, to execute
|
|
readyList.resolveWith( document, [ jQuery ] );
|
|
|
|
// Trigger any bound ready events
|
|
if ( jQuery.fn.triggerHandler ) {
|
|
jQuery( document ).triggerHandler( "ready" );
|
|
jQuery( document ).off( "ready" );
|
|
}
|
|
}
|
|
} );
|
|
|
|
/**
|
|
* The ready event handler and self cleanup method
|
|
*/
|
|
function completed() {
|
|
document.removeEventListener( "DOMContentLoaded", completed );
|
|
window.removeEventListener( "load", completed );
|
|
jQuery.ready();
|
|
}
|
|
|
|
jQuery.ready.promise = function( obj ) {
|
|
if ( !readyList ) {
|
|
|
|
readyList = jQuery.Deferred();
|
|
|
|
// Catch cases where $(document).ready() is called
|
|
// after the browser event has already occurred.
|
|
// Support: IE9-10 only
|
|
// Older IE sometimes signals "interactive" too soon
|
|
if ( document.readyState === "complete" ||
|
|
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
|
|
|
|
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
|
window.setTimeout( jQuery.ready );
|
|
|
|
} else {
|
|
|
|
// Use the handy event callback
|
|
document.addEventListener( "DOMContentLoaded", completed );
|
|
|
|
// A fallback to window.onload, that will always work
|
|
window.addEventListener( "load", completed );
|
|
}
|
|
}
|
|
return readyList.promise( obj );
|
|
};
|
|
|
|
// Kick off the DOM ready check even if the user does not
|
|
jQuery.ready.promise();
|
|
|
|
|
|
|
|
|
|
// Multifunctional method to get and set values of a collection
|
|
// The value/s can optionally be executed if it's a function
|
|
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
|
var i = 0,
|
|
len = elems.length,
|
|
bulk = key == null;
|
|
|
|
// Sets many values
|
|
if ( jQuery.type( key ) === "object" ) {
|
|
chainable = true;
|
|
for ( i in key ) {
|
|
access( elems, fn, i, key[ i ], true, emptyGet, raw );
|
|
}
|
|
|
|
// Sets one value
|
|
} else if ( value !== undefined ) {
|
|
chainable = true;
|
|
|
|
if ( !jQuery.isFunction( value ) ) {
|
|
raw = true;
|
|
}
|
|
|
|
if ( bulk ) {
|
|
|
|
// Bulk operations run against the entire set
|
|
if ( raw ) {
|
|
fn.call( elems, value );
|
|
fn = null;
|
|
|
|
// ...except when executing function values
|
|
} else {
|
|
bulk = fn;
|
|
fn = function( elem, key, value ) {
|
|
return bulk.call( jQuery( elem ), value );
|
|
};
|
|
}
|
|
}
|
|
|
|
if ( fn ) {
|
|
for ( ; i < len; i++ ) {
|
|
fn(
|
|
elems[ i ], key, raw ?
|
|
value :
|
|
value.call( elems[ i ], i, fn( elems[ i ], key ) )
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return chainable ?
|
|
elems :
|
|
|
|
// Gets
|
|
bulk ?
|
|
fn.call( elems ) :
|
|
len ? fn( elems[ 0 ], key ) : emptyGet;
|
|
};
|
|
var acceptData = function( owner ) {
|
|
|
|
// Accepts only:
|
|
// - Node
|
|
// - Node.ELEMENT_NODE
|
|
// - Node.DOCUMENT_NODE
|
|
// - Object
|
|
// - Any
|
|
/* jshint -W018 */
|
|
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
|
|
};
|
|
|
|
|
|
|
|
|
|
function Data() {
|
|
this.expando = jQuery.expando + Data.uid++;
|
|
}
|
|
|
|
Data.uid = 1;
|
|
|
|
Data.prototype = {
|
|
|
|
register: function( owner, initial ) {
|
|
var value = initial || {};
|
|
|
|
// If it is a node unlikely to be stringify-ed or looped over
|
|
// use plain assignment
|
|
if ( owner.nodeType ) {
|
|
owner[ this.expando ] = value;
|
|
|
|
// Otherwise secure it in a non-enumerable, non-writable property
|
|
// configurability must be true to allow the property to be
|
|
// deleted with the delete operator
|
|
} else {
|
|
Object.defineProperty( owner, this.expando, {
|
|
value: value,
|
|
writable: true,
|
|
configurable: true
|
|
} );
|
|
}
|
|
return owner[ this.expando ];
|
|
},
|
|
cache: function( owner ) {
|
|
|
|
// We can accept data for non-element nodes in modern browsers,
|
|
// but we should not, see #8335.
|
|
// Always return an empty object.
|
|
if ( !acceptData( owner ) ) {
|
|
return {};
|
|
}
|
|
|
|
// Check if the owner object already has a cache
|
|
var value = owner[ this.expando ];
|
|
|
|
// If not, create one
|
|
if ( !value ) {
|
|
value = {};
|
|
|
|
// We can accept data for non-element nodes in modern browsers,
|
|
// but we should not, see #8335.
|
|
// Always return an empty object.
|
|
if ( acceptData( owner ) ) {
|
|
|
|
// If it is a node unlikely to be stringify-ed or looped over
|
|
// use plain assignment
|
|
if ( owner.nodeType ) {
|
|
owner[ this.expando ] = value;
|
|
|
|
// Otherwise secure it in a non-enumerable property
|
|
// configurable must be true to allow the property to be
|
|
// deleted when data is removed
|
|
} else {
|
|
Object.defineProperty( owner, this.expando, {
|
|
value: value,
|
|
configurable: true
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
},
|
|
set: function( owner, data, value ) {
|
|
var prop,
|
|
cache = this.cache( owner );
|
|
|
|
// Handle: [ owner, key, value ] args
|
|
if ( typeof data === "string" ) {
|
|
cache[ data ] = value;
|
|
|
|
// Handle: [ owner, { properties } ] args
|
|
} else {
|
|
|
|
// Copy the properties one-by-one to the cache object
|
|
for ( prop in data ) {
|
|
cache[ prop ] = data[ prop ];
|
|
}
|
|
}
|
|
return cache;
|
|
},
|
|
get: function( owner, key ) {
|
|
return key === undefined ?
|
|
this.cache( owner ) :
|
|
owner[ this.expando ] && owner[ this.expando ][ key ];
|
|
},
|
|
access: function( owner, key, value ) {
|
|
var stored;
|
|
|
|
// In cases where either:
|
|
//
|
|
// 1. No key was specified
|
|
// 2. A string key was specified, but no value provided
|
|
//
|
|
// Take the "read" path and allow the get method to determine
|
|
// which value to return, respectively either:
|
|
//
|
|
// 1. The entire cache object
|
|
// 2. The data stored at the key
|
|
//
|
|
if ( key === undefined ||
|
|
( ( key && typeof key === "string" ) && value === undefined ) ) {
|
|
|
|
stored = this.get( owner, key );
|
|
|
|
return stored !== undefined ?
|
|
stored : this.get( owner, jQuery.camelCase( key ) );
|
|
}
|
|
|
|
// When the key is not a string, or both a key and value
|
|
// are specified, set or extend (existing objects) with either:
|
|
//
|
|
// 1. An object of properties
|
|
// 2. A key and value
|
|
//
|
|
this.set( owner, key, value );
|
|
|
|
// Since the "set" path can have two possible entry points
|
|
// return the expected data based on which path was taken[*]
|
|
return value !== undefined ? value : key;
|
|
},
|
|
remove: function( owner, key ) {
|
|
var i, name, camel,
|
|
cache = owner[ this.expando ];
|
|
|
|
if ( cache === undefined ) {
|
|
return;
|
|
}
|
|
|
|
if ( key === undefined ) {
|
|
this.register( owner );
|
|
|
|
} else {
|
|
|
|
// Support array or space separated string of keys
|
|
if ( jQuery.isArray( key ) ) {
|
|
|
|
// If "name" is an array of keys...
|
|
// When data is initially created, via ("key", "val") signature,
|
|
// keys will be converted to camelCase.
|
|
// Since there is no way to tell _how_ a key was added, remove
|
|
// both plain key and camelCase key. #12786
|
|
// This will only penalize the array argument path.
|
|
name = key.concat( key.map( jQuery.camelCase ) );
|
|
} else {
|
|
camel = jQuery.camelCase( key );
|
|
|
|
// Try the string as a key before any manipulation
|
|
if ( key in cache ) {
|
|
name = [ key, camel ];
|
|
} else {
|
|
|
|
// If a key with the spaces exists, use it.
|
|
// Otherwise, create an array by matching non-whitespace
|
|
name = camel;
|
|
name = name in cache ?
|
|
[ name ] : ( name.match( rnotwhite ) || [] );
|
|
}
|
|
}
|
|
|
|
i = name.length;
|
|
|
|
while ( i-- ) {
|
|
delete cache[ name[ i ] ];
|
|
}
|
|
}
|
|
|
|
// Remove the expando if there's no more data
|
|
if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
|
|
|
|
// Support: Chrome <= 35-45+
|
|
// Webkit & Blink performance suffers when deleting properties
|
|
// from DOM nodes, so set to undefined instead
|
|
// https://code.google.com/p/chromium/issues/detail?id=378607
|
|
if ( owner.nodeType ) {
|
|
owner[ this.expando ] = undefined;
|
|
} else {
|
|
delete owner[ this.expando ];
|
|
}
|
|
}
|
|
},
|
|
hasData: function( owner ) {
|
|
var cache = owner[ this.expando ];
|
|
return cache !== undefined && !jQuery.isEmptyObject( cache );
|
|
}
|
|
};
|
|
var dataPriv = new Data();
|
|
|
|
var dataUser = new Data();
|
|
|
|
|
|
|
|
// Implementation Summary
|
|
//
|
|
// 1. Enforce API surface and semantic compatibility with 1.9.x branch
|
|
// 2. Improve the module's maintainability by reducing the storage
|
|
// paths to a single mechanism.
|
|
// 3. Use the same single mechanism to support "private" and "user" data.
|
|
// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
|
|
// 5. Avoid exposing implementation details on user objects (eg. expando properties)
|
|
// 6. Provide a clear path for implementation upgrade to WeakMap in 2014
|
|
|
|
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
|
|
rmultiDash = /[A-Z]/g;
|
|
|
|
function dataAttr( elem, key, data ) {
|
|
var name;
|
|
|
|
// If nothing was found internally, try to fetch any
|
|
// data from the HTML5 data-* attribute
|
|
if ( data === undefined && elem.nodeType === 1 ) {
|
|
name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
|
|
data = elem.getAttribute( name );
|
|
|
|
if ( typeof data === "string" ) {
|
|
try {
|
|
data = data === "true" ? true :
|
|
data === "false" ? false :
|
|
data === "null" ? null :
|
|
|
|
// Only convert to a number if it doesn't change the string
|
|
+data + "" === data ? +data :
|
|
rbrace.test( data ) ? jQuery.parseJSON( data ) :
|
|
data;
|
|
} catch ( e ) {}
|
|
|
|
// Make sure we set the data so it isn't changed later
|
|
dataUser.set( elem, key, data );
|
|
} else {
|
|
data = undefined;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
jQuery.extend( {
|
|
hasData: function( elem ) {
|
|
return dataUser.hasData( elem ) || dataPriv.hasData( elem );
|
|
},
|
|
|
|
data: function( elem, name, data ) {
|
|
return dataUser.access( elem, name, data );
|
|
},
|
|
|
|
removeData: function( elem, name ) {
|
|
dataUser.remove( elem, name );
|
|
},
|
|
|
|
// TODO: Now that all calls to _data and _removeData have been replaced
|
|
// with direct calls to dataPriv methods, these can be deprecated.
|
|
_data: function( elem, name, data ) {
|
|
return dataPriv.access( elem, name, data );
|
|
},
|
|
|
|
_removeData: function( elem, name ) {
|
|
dataPriv.remove( elem, name );
|
|
}
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
data: function( key, value ) {
|
|
var i, name, data,
|
|
elem = this[ 0 ],
|
|
attrs = elem && elem.attributes;
|
|
|
|
// Gets all values
|
|
if ( key === undefined ) {
|
|
if ( this.length ) {
|
|
data = dataUser.get( elem );
|
|
|
|
if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
|
|
i = attrs.length;
|
|
while ( i-- ) {
|
|
|
|
// Support: IE11+
|
|
// The attrs elements can be null (#14894)
|
|
if ( attrs[ i ] ) {
|
|
name = attrs[ i ].name;
|
|
if ( name.indexOf( "data-" ) === 0 ) {
|
|
name = jQuery.camelCase( name.slice( 5 ) );
|
|
dataAttr( elem, name, data[ name ] );
|
|
}
|
|
}
|
|
}
|
|
dataPriv.set( elem, "hasDataAttrs", true );
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Sets multiple values
|
|
if ( typeof key === "object" ) {
|
|
return this.each( function() {
|
|
dataUser.set( this, key );
|
|
} );
|
|
}
|
|
|
|
return access( this, function( value ) {
|
|
var data, camelKey;
|
|
|
|
// The calling jQuery object (element matches) is not empty
|
|
// (and therefore has an element appears at this[ 0 ]) and the
|
|
// `value` parameter was not undefined. An empty jQuery object
|
|
// will result in `undefined` for elem = this[ 0 ] which will
|
|
// throw an exception if an attempt to read a data cache is made.
|
|
if ( elem && value === undefined ) {
|
|
|
|
// Attempt to get data from the cache
|
|
// with the key as-is
|
|
data = dataUser.get( elem, key ) ||
|
|
|
|
// Try to find dashed key if it exists (gh-2779)
|
|
// This is for 2.2.x only
|
|
dataUser.get( elem, key.replace( rmultiDash, "-$&" ).toLowerCase() );
|
|
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
camelKey = jQuery.camelCase( key );
|
|
|
|
// Attempt to get data from the cache
|
|
// with the key camelized
|
|
data = dataUser.get( elem, camelKey );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// Attempt to "discover" the data in
|
|
// HTML5 custom data-* attrs
|
|
data = dataAttr( elem, camelKey, undefined );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// We tried really hard, but the data doesn't exist.
|
|
return;
|
|
}
|
|
|
|
// Set the data...
|
|
camelKey = jQuery.camelCase( key );
|
|
this.each( function() {
|
|
|
|
// First, attempt to store a copy or reference of any
|
|
// data that might've been store with a camelCased key.
|
|
var data = dataUser.get( this, camelKey );
|
|
|
|
// For HTML5 data-* attribute interop, we have to
|
|
// store property names with dashes in a camelCase form.
|
|
// This might not apply to all properties...*
|
|
dataUser.set( this, camelKey, value );
|
|
|
|
// *... In the case of properties that might _actually_
|
|
// have dashes, we need to also store a copy of that
|
|
// unchanged property.
|
|
if ( key.indexOf( "-" ) > -1 && data !== undefined ) {
|
|
dataUser.set( this, key, value );
|
|
}
|
|
} );
|
|
}, null, value, arguments.length > 1, null, true );
|
|
},
|
|
|
|
removeData: function( key ) {
|
|
return this.each( function() {
|
|
dataUser.remove( this, key );
|
|
} );
|
|
}
|
|
} );
|
|
|
|
|
|
jQuery.extend( {
|
|
queue: function( elem, type, data ) {
|
|
var queue;
|
|
|
|
if ( elem ) {
|
|
type = ( type || "fx" ) + "queue";
|
|
queue = dataPriv.get( elem, type );
|
|
|
|
// Speed up dequeue by getting out quickly if this is just a lookup
|
|
if ( data ) {
|
|
if ( !queue || jQuery.isArray( data ) ) {
|
|
queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
|
|
} else {
|
|
queue.push( data );
|
|
}
|
|
}
|
|
return queue || [];
|
|
}
|
|
},
|
|
|
|
dequeue: function( elem, type ) {
|
|
type = type || "fx";
|
|
|
|
var queue = jQuery.queue( elem, type ),
|
|
startLength = queue.length,
|
|
fn = queue.shift(),
|
|
hooks = jQuery._queueHooks( elem, type ),
|
|
next = function() {
|
|
jQuery.dequeue( elem, type );
|
|
};
|
|
|
|
// If the fx queue is dequeued, always remove the progress sentinel
|
|
if ( fn === "inprogress" ) {
|
|
fn = queue.shift();
|
|
startLength--;
|
|
}
|
|
|
|
if ( fn ) {
|
|
|
|
// Add a progress sentinel to prevent the fx queue from being
|
|
// automatically dequeued
|
|
if ( type === "fx" ) {
|
|
queue.unshift( "inprogress" );
|
|
}
|
|
|
|
// Clear up the last queue stop function
|
|
delete hooks.stop;
|
|
fn.call( elem, next, hooks );
|
|
}
|
|
|
|
if ( !startLength && hooks ) {
|
|
hooks.empty.fire();
|
|
}
|
|
},
|
|
|
|
// Not public - generate a queueHooks object, or return the current one
|
|
_queueHooks: function( elem, type ) {
|
|
var key = type + "queueHooks";
|
|
return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
|
|
empty: jQuery.Callbacks( "once memory" ).add( function() {
|
|
dataPriv.remove( elem, [ type + "queue", key ] );
|
|
} )
|
|
} );
|
|
}
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
queue: function( type, data ) {
|
|
var setter = 2;
|
|
|
|
if ( typeof type !== "string" ) {
|
|
data = type;
|
|
type = "fx";
|
|
setter--;
|
|
}
|
|
|
|
if ( arguments.length < setter ) {
|
|
return jQuery.queue( this[ 0 ], type );
|
|
}
|
|
|
|
return data === undefined ?
|
|
this :
|
|
this.each( function() {
|
|
var queue = jQuery.queue( this, type, data );
|
|
|
|
// Ensure a hooks for this queue
|
|
jQuery._queueHooks( this, type );
|
|
|
|
if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
|
|
jQuery.dequeue( this, type );
|
|
}
|
|
} );
|
|
},
|
|
dequeue: function( type ) {
|
|
return this.each( function() {
|
|
jQuery.dequeue( this, type );
|
|
} );
|
|
},
|
|
clearQueue: function( type ) {
|
|
return this.queue( type || "fx", [] );
|
|
},
|
|
|
|
// Get a promise resolved when queues of a certain type
|
|
// are emptied (fx is the type by default)
|
|
promise: function( type, obj ) {
|
|
var tmp,
|
|
count = 1,
|
|
defer = jQuery.Deferred(),
|
|
elements = this,
|
|
i = this.length,
|
|
resolve = function() {
|
|
if ( !( --count ) ) {
|
|
defer.resolveWith( elements, [ elements ] );
|
|
}
|
|
};
|
|
|
|
if ( typeof type !== "string" ) {
|
|
obj = type;
|
|
type = undefined;
|
|
}
|
|
type = type || "fx";
|
|
|
|
while ( i-- ) {
|
|
tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
|
|
if ( tmp && tmp.empty ) {
|
|
count++;
|
|
tmp.empty.add( resolve );
|
|
}
|
|
}
|
|
resolve();
|
|
return defer.promise( obj );
|
|
}
|
|
} );
|
|
var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
|
|
|
|
var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
|
|
|
|
|
|
var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
|
|
|
|
var isHidden = function( elem, el ) {
|
|
|
|
// isHidden might be called from jQuery#filter function;
|
|
// in that case, element will be second argument
|
|
elem = el || elem;
|
|
return jQuery.css( elem, "display" ) === "none" ||
|
|
!jQuery.contains( elem.ownerDocument, elem );
|
|
};
|
|
|
|
|
|
|
|
function adjustCSS( elem, prop, valueParts, tween ) {
|
|
var adjusted,
|
|
scale = 1,
|
|
maxIterations = 20,
|
|
currentValue = tween ?
|
|
function() { return tween.cur(); } :
|
|
function() { return jQuery.css( elem, prop, "" ); },
|
|
initial = currentValue(),
|
|
unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
|
|
|
|
// Starting value computation is required for potential unit mismatches
|
|
initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
|
|
rcssNum.exec( jQuery.css( elem, prop ) );
|
|
|
|
if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
|
|
|
|
// Trust units reported by jQuery.css
|
|
unit = unit || initialInUnit[ 3 ];
|
|
|
|
// Make sure we update the tween properties later on
|
|
valueParts = valueParts || [];
|
|
|
|
// Iteratively approximate from a nonzero starting point
|
|
initialInUnit = +initial || 1;
|
|
|
|
do {
|
|
|
|
// If previous iteration zeroed out, double until we get *something*.
|
|
// Use string for doubling so we don't accidentally see scale as unchanged below
|
|
scale = scale || ".5";
|
|
|
|
// Adjust and apply
|
|
initialInUnit = initialInUnit / scale;
|
|
jQuery.style( elem, prop, initialInUnit + unit );
|
|
|
|
// Update scale, tolerating zero or NaN from tween.cur()
|
|
// Break the loop if scale is unchanged or perfect, or if we've just had enough.
|
|
} while (
|
|
scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
|
|
);
|
|
}
|
|
|
|
if ( valueParts ) {
|
|
initialInUnit = +initialInUnit || +initial || 0;
|
|
|
|
// Apply relative offset (+=/-=) if specified
|
|
adjusted = valueParts[ 1 ] ?
|
|
initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
|
|
+valueParts[ 2 ];
|
|
if ( tween ) {
|
|
tween.unit = unit;
|
|
tween.start = initialInUnit;
|
|
tween.end = adjusted;
|
|
}
|
|
}
|
|
return adjusted;
|
|
}
|
|
var rcheckableType = ( /^(?:checkbox|radio)$/i );
|
|
|
|
var rtagName = ( /<([\w:-]+)/ );
|
|
|
|
var rscriptType = ( /^$|\/(?:java|ecma)script/i );
|
|
|
|
|
|
|
|
// We have to close these tags to support XHTML (#13200)
|
|
var wrapMap = {
|
|
|
|
// Support: IE9
|
|
option: [ 1, "<select multiple='multiple'>", "</select>" ],
|
|
|
|
// XHTML parsers do not magically insert elements in the
|
|
// same way that tag soup parsers do. So we cannot shorten
|
|
// this by omitting <tbody> or other required elements.
|
|
thead: [ 1, "<table>", "</table>" ],
|
|
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
|
|
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
|
|
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
|
|
|
|
_default: [ 0, "", "" ]
|
|
};
|
|
|
|
// Support: IE9
|
|
wrapMap.optgroup = wrapMap.option;
|
|
|
|
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
|
|
wrapMap.th = wrapMap.td;
|
|
|
|
|
|
function getAll( context, tag ) {
|
|
|
|
// Support: IE9-11+
|
|
// Use typeof to avoid zero-argument method invocation on host objects (#15151)
|
|
var ret = typeof context.getElementsByTagName !== "undefined" ?
|
|
context.getElementsByTagName( tag || "*" ) :
|
|
typeof context.querySelectorAll !== "undefined" ?
|
|
context.querySelectorAll( tag || "*" ) :
|
|
[];
|
|
|
|
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
|
|
jQuery.merge( [ context ], ret ) :
|
|
ret;
|
|
}
|
|
|
|
|
|
// Mark scripts as having already been evaluated
|
|
function setGlobalEval( elems, refElements ) {
|
|
var i = 0,
|
|
l = elems.length;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
dataPriv.set(
|
|
elems[ i ],
|
|
"globalEval",
|
|
!refElements || dataPriv.get( refElements[ i ], "globalEval" )
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
var rhtml = /<|&#?\w+;/;
|
|
|
|
function buildFragment( elems, context, scripts, selection, ignored ) {
|
|
var elem, tmp, tag, wrap, contains, j,
|
|
fragment = context.createDocumentFragment(),
|
|
nodes = [],
|
|
i = 0,
|
|
l = elems.length;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
elem = elems[ i ];
|
|
|
|
if ( elem || elem === 0 ) {
|
|
|
|
// Add nodes directly
|
|
if ( jQuery.type( elem ) === "object" ) {
|
|
|
|
// Support: Android<4.1, PhantomJS<2
|
|
// push.apply(_, arraylike) throws on ancient WebKit
|
|
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
|
|
|
|
// Convert non-html into a text node
|
|
} else if ( !rhtml.test( elem ) ) {
|
|
nodes.push( context.createTextNode( elem ) );
|
|
|
|
// Convert html into DOM nodes
|
|
} else {
|
|
tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
|
|
|
|
// Deserialize a standard representation
|
|
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
|
|
wrap = wrapMap[ tag ] || wrapMap._default;
|
|
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
|
|
|
|
// Descend through wrappers to the right content
|
|
j = wrap[ 0 ];
|
|
while ( j-- ) {
|
|
tmp = tmp.lastChild;
|
|
}
|
|
|
|
// Support: Android<4.1, PhantomJS<2
|
|
// push.apply(_, arraylike) throws on ancient WebKit
|
|
jQuery.merge( nodes, tmp.childNodes );
|
|
|
|
// Remember the top-level container
|
|
tmp = fragment.firstChild;
|
|
|
|
// Ensure the created nodes are orphaned (#12392)
|
|
tmp.textContent = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove wrapper from fragment
|
|
fragment.textContent = "";
|
|
|
|
i = 0;
|
|
while ( ( elem = nodes[ i++ ] ) ) {
|
|
|
|
// Skip elements already in the context collection (trac-4087)
|
|
if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
|
|
if ( ignored ) {
|
|
ignored.push( elem );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
contains = jQuery.contains( elem.ownerDocument, elem );
|
|
|
|
// Append to fragment
|
|
tmp = getAll( fragment.appendChild( elem ), "script" );
|
|
|
|
// Preserve script evaluation history
|
|
if ( contains ) {
|
|
setGlobalEval( tmp );
|
|
}
|
|
|
|
// Capture executables
|
|
if ( scripts ) {
|
|
j = 0;
|
|
while ( ( elem = tmp[ j++ ] ) ) {
|
|
if ( rscriptType.test( elem.type || "" ) ) {
|
|
scripts.push( elem );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return fragment;
|
|
}
|
|
|
|
|
|
( function() {
|
|
var fragment = document.createDocumentFragment(),
|
|
div = fragment.appendChild( document.createElement( "div" ) ),
|
|
input = document.createElement( "input" );
|
|
|
|
// Support: Android 4.0-4.3, Safari<=5.1
|
|
// Check state lost if the name is set (#11217)
|
|
// Support: Windows Web Apps (WWA)
|
|
// `name` and `type` must use .setAttribute for WWA (#14901)
|
|
input.setAttribute( "type", "radio" );
|
|
input.setAttribute( "checked", "checked" );
|
|
input.setAttribute( "name", "t" );
|
|
|
|
div.appendChild( input );
|
|
|
|
// Support: Safari<=5.1, Android<4.2
|
|
// Older WebKit doesn't clone checked state correctly in fragments
|
|
support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
|
|
|
|
// Support: IE<=11+
|
|
// Make sure textarea (and checkbox) defaultValue is properly cloned
|
|
div.innerHTML = "<textarea>x</textarea>";
|
|
support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
|
|
} )();
|
|
|
|
|
|
var
|
|
rkeyEvent = /^key/,
|
|
rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
|
|
rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
|
|
|
|
function returnTrue() {
|
|
return true;
|
|
}
|
|
|
|
function returnFalse() {
|
|
return false;
|
|
}
|
|
|
|
// Support: IE9
|
|
// See #13393 for more info
|
|
function safeActiveElement() {
|
|
try {
|
|
return document.activeElement;
|
|
} catch ( err ) { }
|
|
}
|
|
|
|
function on( elem, types, selector, data, fn, one ) {
|
|
var origFn, type;
|
|
|
|
// Types can be a map of types/handlers
|
|
if ( typeof types === "object" ) {
|
|
|
|
// ( types-Object, selector, data )
|
|
if ( typeof selector !== "string" ) {
|
|
|
|
// ( types-Object, data )
|
|
data = data || selector;
|
|
selector = undefined;
|
|
}
|
|
for ( type in types ) {
|
|
on( elem, type, selector, data, types[ type ], one );
|
|
}
|
|
return elem;
|
|
}
|
|
|
|
if ( data == null && fn == null ) {
|
|
|
|
// ( types, fn )
|
|
fn = selector;
|
|
data = selector = undefined;
|
|
} else if ( fn == null ) {
|
|
if ( typeof selector === "string" ) {
|
|
|
|
// ( types, selector, fn )
|
|
fn = data;
|
|
data = undefined;
|
|
} else {
|
|
|
|
// ( types, data, fn )
|
|
fn = data;
|
|
data = selector;
|
|
selector = undefined;
|
|
}
|
|
}
|
|
if ( fn === false ) {
|
|
fn = returnFalse;
|
|
} else if ( !fn ) {
|
|
return elem;
|
|
}
|
|
|
|
if ( one === 1 ) {
|
|
origFn = fn;
|
|
fn = function( event ) {
|
|
|
|
// Can use an empty set, since event contains the info
|
|
jQuery().off( event );
|
|
return origFn.apply( this, arguments );
|
|
};
|
|
|
|
// Use same guid so caller can remove using origFn
|
|
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
|
|
}
|
|
return elem.each( function() {
|
|
jQuery.event.add( this, types, fn, data, selector );
|
|
} );
|
|
}
|
|
|
|
/*
|
|
* Helper functions for managing events -- not part of the public interface.
|
|
* Props to Dean Edwards' addEvent library for many of the ideas.
|
|
*/
|
|
jQuery.event = {
|
|
|
|
global: {},
|
|
|
|
add: function( elem, types, handler, data, selector ) {
|
|
|
|
var handleObjIn, eventHandle, tmp,
|
|
events, t, handleObj,
|
|
special, handlers, type, namespaces, origType,
|
|
elemData = dataPriv.get( elem );
|
|
|
|
// Don't attach events to noData or text/comment nodes (but allow plain objects)
|
|
if ( !elemData ) {
|
|
return;
|
|
}
|
|
|
|
// Caller can pass in an object of custom data in lieu of the handler
|
|
if ( handler.handler ) {
|
|
handleObjIn = handler;
|
|
handler = handleObjIn.handler;
|
|
selector = handleObjIn.selector;
|
|
}
|
|
|
|
// Make sure that the handler has a unique ID, used to find/remove it later
|
|
if ( !handler.guid ) {
|
|
handler.guid = jQuery.guid++;
|
|
}
|
|
|
|
// Init the element's event structure and main handler, if this is the first
|
|
if ( !( events = elemData.events ) ) {
|
|
events = elemData.events = {};
|
|
}
|
|
if ( !( eventHandle = elemData.handle ) ) {
|
|
eventHandle = elemData.handle = function( e ) {
|
|
|
|
// Discard the second event of a jQuery.event.trigger() and
|
|
// when an event is called after a page has unloaded
|
|
return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
|
|
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
|
|
};
|
|
}
|
|
|
|
// Handle multiple events separated by a space
|
|
types = ( types || "" ).match( rnotwhite ) || [ "" ];
|
|
t = types.length;
|
|
while ( t-- ) {
|
|
tmp = rtypenamespace.exec( types[ t ] ) || [];
|
|
type = origType = tmp[ 1 ];
|
|
namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
|
|
|
|
// There *must* be a type, no attaching namespace-only handlers
|
|
if ( !type ) {
|
|
continue;
|
|
}
|
|
|
|
// If event changes its type, use the special event handlers for the changed type
|
|
special = jQuery.event.special[ type ] || {};
|
|
|
|
// If selector defined, determine special event api type, otherwise given type
|
|
type = ( selector ? special.delegateType : special.bindType ) || type;
|
|
|
|
// Update special based on newly reset type
|
|
special = jQuery.event.special[ type ] || {};
|
|
|
|
// handleObj is passed to all event handlers
|
|
handleObj = jQuery.extend( {
|
|
type: type,
|
|
origType: origType,
|
|
data: data,
|
|
handler: handler,
|
|
guid: handler.guid,
|
|
selector: selector,
|
|
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
|
|
namespace: namespaces.join( "." )
|
|
}, handleObjIn );
|
|
|
|
// Init the event handler queue if we're the first
|
|
if ( !( handlers = events[ type ] ) ) {
|
|
handlers = events[ type ] = [];
|
|
handlers.delegateCount = 0;
|
|
|
|
// Only use addEventListener if the special events handler returns false
|
|
if ( !special.setup ||
|
|
special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
|
|
|
|
if ( elem.addEventListener ) {
|
|
elem.addEventListener( type, eventHandle );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( special.add ) {
|
|
special.add.call( elem, handleObj );
|
|
|
|
if ( !handleObj.handler.guid ) {
|
|
handleObj.handler.guid = handler.guid;
|
|
}
|
|
}
|
|
|
|
// Add to the element's handler list, delegates in front
|
|
if ( selector ) {
|
|
handlers.splice( handlers.delegateCount++, 0, handleObj );
|
|
} else {
|
|
handlers.push( handleObj );
|
|
}
|
|
|
|
// Keep track of which events have ever been used, for event optimization
|
|
jQuery.event.global[ type ] = true;
|
|
}
|
|
|
|
},
|
|
|
|
// Detach an event or set of events from an element
|
|
remove: function( elem, types, handler, selector, mappedTypes ) {
|
|
|
|
var j, origCount, tmp,
|
|
events, t, handleObj,
|
|
special, handlers, type, namespaces, origType,
|
|
elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
|
|
|
|
if ( !elemData || !( events = elemData.events ) ) {
|
|
return;
|
|
}
|
|
|
|
// Once for each type.namespace in types; type may be omitted
|
|
types = ( types || "" ).match( rnotwhite ) || [ "" ];
|
|
t = types.length;
|
|
while ( t-- ) {
|
|
tmp = rtypenamespace.exec( types[ t ] ) || [];
|
|
type = origType = tmp[ 1 ];
|
|
namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
|
|
|
|
// Unbind all events (on this namespace, if provided) for the element
|
|
if ( !type ) {
|
|
for ( type in events ) {
|
|
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
special = jQuery.event.special[ type ] || {};
|
|
type = ( selector ? special.delegateType : special.bindType ) || type;
|
|
handlers = events[ type ] || [];
|
|
tmp = tmp[ 2 ] &&
|
|
new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
|
|
|
|
// Remove matching events
|
|
origCount = j = handlers.length;
|
|
while ( j-- ) {
|
|
handleObj = handlers[ j ];
|
|
|
|
if ( ( mappedTypes || origType === handleObj.origType ) &&
|
|
( !handler || handler.guid === handleObj.guid ) &&
|
|
( !tmp || tmp.test( handleObj.namespace ) ) &&
|
|
( !selector || selector === handleObj.selector ||
|
|
selector === "**" && handleObj.selector ) ) {
|
|
handlers.splice( j, 1 );
|
|
|
|
if ( handleObj.selector ) {
|
|
handlers.delegateCount--;
|
|
}
|
|
if ( special.remove ) {
|
|
special.remove.call( elem, handleObj );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove generic event handler if we removed something and no more handlers exist
|
|
// (avoids potential for endless recursion during removal of special event handlers)
|
|
if ( origCount && !handlers.length ) {
|
|
if ( !special.teardown ||
|
|
special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
|
|
|
|
jQuery.removeEvent( elem, type, elemData.handle );
|
|
}
|
|
|
|
delete events[ type ];
|
|
}
|
|
}
|
|
|
|
// Remove data and the expando if it's no longer used
|
|
if ( jQuery.isEmptyObject( events ) ) {
|
|
dataPriv.remove( elem, "handle events" );
|
|
}
|
|
},
|
|
|
|
dispatch: function( event ) {
|
|
|
|
// Make a writable jQuery.Event from the native event object
|
|
event = jQuery.event.fix( event );
|
|
|
|
var i, j, ret, matched, handleObj,
|
|
handlerQueue = [],
|
|
args = slice.call( arguments ),
|
|
handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
|
|
special = jQuery.event.special[ event.type ] || {};
|
|
|
|
// Use the fix-ed jQuery.Event rather than the (read-only) native event
|
|
args[ 0 ] = event;
|
|
event.delegateTarget = this;
|
|
|
|
// Call the preDispatch hook for the mapped type, and let it bail if desired
|
|
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
|
|
return;
|
|
}
|
|
|
|
// Determine handlers
|
|
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
|
|
|
|
// Run delegates first; they may want to stop propagation beneath us
|
|
i = 0;
|
|
while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
|
|
event.currentTarget = matched.elem;
|
|
|
|
j = 0;
|
|
while ( ( handleObj = matched.handlers[ j++ ] ) &&
|
|
!event.isImmediatePropagationStopped() ) {
|
|
|
|
// Triggered event must either 1) have no namespace, or 2) have namespace(s)
|
|
// a subset or equal to those in the bound event (both can have no namespace).
|
|
if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
|
|
|
|
event.handleObj = handleObj;
|
|
event.data = handleObj.data;
|
|
|
|
ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
|
|
handleObj.handler ).apply( matched.elem, args );
|
|
|
|
if ( ret !== undefined ) {
|
|
if ( ( event.result = ret ) === false ) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the postDispatch hook for the mapped type
|
|
if ( special.postDispatch ) {
|
|
special.postDispatch.call( this, event );
|
|
}
|
|
|
|
return event.result;
|
|
},
|
|
|
|
handlers: function( event, handlers ) {
|
|
var i, matches, sel, handleObj,
|
|
handlerQueue = [],
|
|
delegateCount = handlers.delegateCount,
|
|
cur = event.target;
|
|
|
|
// Support (at least): Chrome, IE9
|
|
// Find delegate handlers
|
|
// Black-hole SVG <use> instance trees (#13180)
|
|
//
|
|
// Support: Firefox<=42+
|
|
// Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
|
|
if ( delegateCount && cur.nodeType &&
|
|
( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
|
|
|
|
for ( ; cur !== this; cur = cur.parentNode || this ) {
|
|
|
|
// Don't check non-elements (#13208)
|
|
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
|
|
if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) {
|
|
matches = [];
|
|
for ( i = 0; i < delegateCount; i++ ) {
|
|
handleObj = handlers[ i ];
|
|
|
|
// Don't conflict with Object.prototype properties (#13203)
|
|
sel = handleObj.selector + " ";
|
|
|
|
if ( matches[ sel ] === undefined ) {
|
|
matches[ sel ] = handleObj.needsContext ?
|
|
jQuery( sel, this ).index( cur ) > -1 :
|
|
jQuery.find( sel, this, null, [ cur ] ).length;
|
|
}
|
|
if ( matches[ sel ] ) {
|
|
matches.push( handleObj );
|
|
}
|
|
}
|
|
if ( matches.length ) {
|
|
handlerQueue.push( { elem: cur, handlers: matches } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the remaining (directly-bound) handlers
|
|
if ( delegateCount < handlers.length ) {
|
|
handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } );
|
|
}
|
|
|
|
return handlerQueue;
|
|
},
|
|
|
|
// Includes some event props shared by KeyEvent and MouseEvent
|
|
props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
|
|
"metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
|
|
|
|
fixHooks: {},
|
|
|
|
keyHooks: {
|
|
props: "char charCode key keyCode".split( " " ),
|
|
filter: function( event, original ) {
|
|
|
|
// Add which for key events
|
|
if ( event.which == null ) {
|
|
event.which = original.charCode != null ? original.charCode : original.keyCode;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
},
|
|
|
|
mouseHooks: {
|
|
props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " +
|
|
"screenX screenY toElement" ).split( " " ),
|
|
filter: function( event, original ) {
|
|
var eventDoc, doc, body,
|
|
button = original.button;
|
|
|
|
// Calculate pageX/Y if missing and clientX/Y available
|
|
if ( event.pageX == null && original.clientX != null ) {
|
|
eventDoc = event.target.ownerDocument || document;
|
|
doc = eventDoc.documentElement;
|
|
body = eventDoc.body;
|
|
|
|
event.pageX = original.clientX +
|
|
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
|
|
( doc && doc.clientLeft || body && body.clientLeft || 0 );
|
|
event.pageY = original.clientY +
|
|
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
|
|
( doc && doc.clientTop || body && body.clientTop || 0 );
|
|
}
|
|
|
|
// Add which for click: 1 === left; 2 === middle; 3 === right
|
|
// Note: button is not normalized, so don't use it
|
|
if ( !event.which && button !== undefined ) {
|
|
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
|
|
}
|
|
|
|
return event;
|
|
}
|
|
},
|
|
|
|
fix: function( event ) {
|
|
if ( event[ jQuery.expando ] ) {
|
|
return event;
|
|
}
|
|
|
|
// Create a writable copy of the event object and normalize some properties
|
|
var i, prop, copy,
|
|
type = event.type,
|
|
originalEvent = event,
|
|
fixHook = this.fixHooks[ type ];
|
|
|
|
if ( !fixHook ) {
|
|
this.fixHooks[ type ] = fixHook =
|
|
rmouseEvent.test( type ) ? this.mouseHooks :
|
|
rkeyEvent.test( type ) ? this.keyHooks :
|
|
{};
|
|
}
|
|
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
|
|
|
|
event = new jQuery.Event( originalEvent );
|
|
|
|
i = copy.length;
|
|
while ( i-- ) {
|
|
prop = copy[ i ];
|
|
event[ prop ] = originalEvent[ prop ];
|
|
}
|
|
|
|
// Support: Cordova 2.5 (WebKit) (#13255)
|
|
// All events should have a target; Cordova deviceready doesn't
|
|
if ( !event.target ) {
|
|
event.target = document;
|
|
}
|
|
|
|
// Support: Safari 6.0+, Chrome<28
|
|
// Target should not be a text node (#504, #13143)
|
|
if ( event.target.nodeType === 3 ) {
|
|
event.target = event.target.parentNode;
|
|
}
|
|
|
|
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
|
|
},
|
|
|
|
special: {
|
|
load: {
|
|
|
|
// Prevent triggered image.load events from bubbling to window.load
|
|
noBubble: true
|
|
},
|
|
focus: {
|
|
|
|
// Fire native event if possible so blur/focus sequence is correct
|
|
trigger: function() {
|
|
if ( this !== safeActiveElement() && this.focus ) {
|
|
this.focus();
|
|
return false;
|
|
}
|
|
},
|
|
delegateType: "focusin"
|
|
},
|
|
blur: {
|
|
trigger: function() {
|
|
if ( this === safeActiveElement() && this.blur ) {
|
|
this.blur();
|
|
return false;
|
|
}
|
|
},
|
|
delegateType: "focusout"
|
|
},
|
|
click: {
|
|
|
|
// For checkbox, fire native event so checked state will be right
|
|
trigger: function() {
|
|
if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
|
|
this.click();
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// For cross-browser consistency, don't fire native .click() on links
|
|
_default: function( event ) {
|
|
return jQuery.nodeName( event.target, "a" );
|
|
}
|
|
},
|
|
|
|
beforeunload: {
|
|
postDispatch: function( event ) {
|
|
|
|
// Support: Firefox 20+
|
|
// Firefox doesn't alert if the returnValue field is not set.
|
|
if ( event.result !== undefined && event.originalEvent ) {
|
|
event.originalEvent.returnValue = event.result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.removeEvent = function( elem, type, handle ) {
|
|
|
|
// This "if" is needed for plain objects
|
|
if ( elem.removeEventListener ) {
|
|
elem.removeEventListener( type, handle );
|
|
}
|
|
};
|
|
|
|
jQuery.Event = function( src, props ) {
|
|
|
|
// Allow instantiation without the 'new' keyword
|
|
if ( !( this instanceof jQuery.Event ) ) {
|
|
return new jQuery.Event( src, props );
|
|
}
|
|
|
|
// Event object
|
|
if ( src && src.type ) {
|
|
this.originalEvent = src;
|
|
this.type = src.type;
|
|
|
|
// Events bubbling up the document may have been marked as prevented
|
|
// by a handler lower down the tree; reflect the correct value.
|
|
this.isDefaultPrevented = src.defaultPrevented ||
|
|
src.defaultPrevented === undefined &&
|
|
|
|
// Support: Android<4.0
|
|
src.returnValue === false ?
|
|
returnTrue :
|
|
returnFalse;
|
|
|
|
// Event type
|
|
} else {
|
|
this.type = src;
|
|
}
|
|
|
|
// Put explicitly provided properties onto the event object
|
|
if ( props ) {
|
|
jQuery.extend( this, props );
|
|
}
|
|
|
|
// Create a timestamp if incoming event doesn't have one
|
|
this.timeStamp = src && src.timeStamp || jQuery.now();
|
|
|
|
// Mark it as fixed
|
|
this[ jQuery.expando ] = true;
|
|
};
|
|
|
|
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
|
|
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
|
jQuery.Event.prototype = {
|
|
constructor: jQuery.Event,
|
|
isDefaultPrevented: returnFalse,
|
|
isPropagationStopped: returnFalse,
|
|
isImmediatePropagationStopped: returnFalse,
|
|
isSimulated: false,
|
|
|
|
preventDefault: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isDefaultPrevented = returnTrue;
|
|
|
|
if ( e && !this.isSimulated ) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
stopPropagation: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isPropagationStopped = returnTrue;
|
|
|
|
if ( e && !this.isSimulated ) {
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
stopImmediatePropagation: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isImmediatePropagationStopped = returnTrue;
|
|
|
|
if ( e && !this.isSimulated ) {
|
|
e.stopImmediatePropagation();
|
|
}
|
|
|
|
this.stopPropagation();
|
|
}
|
|
};
|
|
|
|
// Create mouseenter/leave events using mouseover/out and event-time checks
|
|
// so that event delegation works in jQuery.
|
|
// Do the same for pointerenter/pointerleave and pointerover/pointerout
|
|
//
|
|
// Support: Safari 7 only
|
|
// Safari sends mouseenter too often; see:
|
|
// https://code.google.com/p/chromium/issues/detail?id=470258
|
|
// for the description of the bug (it existed in older Chrome versions as well).
|
|
jQuery.each( {
|
|
mouseenter: "mouseover",
|
|
mouseleave: "mouseout",
|
|
pointerenter: "pointerover",
|
|
pointerleave: "pointerout"
|
|
}, function( orig, fix ) {
|
|
jQuery.event.special[ orig ] = {
|
|
delegateType: fix,
|
|
bindType: fix,
|
|
|
|
handle: function( event ) {
|
|
var ret,
|
|
target = this,
|
|
related = event.relatedTarget,
|
|
handleObj = event.handleObj;
|
|
|
|
// For mouseenter/leave call the handler if related is outside the target.
|
|
// NB: No relatedTarget if the mouse left/entered the browser window
|
|
if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
|
|
event.type = handleObj.origType;
|
|
ret = handleObj.handler.apply( this, arguments );
|
|
event.type = fix;
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
on: function( types, selector, data, fn ) {
|
|
return on( this, types, selector, data, fn );
|
|
},
|
|
one: function( types, selector, data, fn ) {
|
|
return on( this, types, selector, data, fn, 1 );
|
|
},
|
|
off: function( types, selector, fn ) {
|
|
var handleObj, type;
|
|
if ( types && types.preventDefault && types.handleObj ) {
|
|
|
|
// ( event ) dispatched jQuery.Event
|
|
handleObj = types.handleObj;
|
|
jQuery( types.delegateTarget ).off(
|
|
handleObj.namespace ?
|
|
handleObj.origType + "." + handleObj.namespace :
|
|
handleObj.origType,
|
|
handleObj.selector,
|
|
handleObj.handler
|
|
);
|
|
return this;
|
|
}
|
|
if ( typeof types === "object" ) {
|
|
|
|
// ( types-object [, selector] )
|
|
for ( type in types ) {
|
|
this.off( type, selector, types[ type ] );
|
|
}
|
|
return this;
|
|
}
|
|
if ( selector === false || typeof selector === "function" ) {
|
|
|
|
// ( types [, fn] )
|
|
fn = selector;
|
|
selector = undefined;
|
|
}
|
|
if ( fn === false ) {
|
|
fn = returnFalse;
|
|
}
|
|
return this.each( function() {
|
|
jQuery.event.remove( this, types, fn, selector );
|
|
} );
|
|
}
|
|
} );
|
|
|
|
|
|
var
|
|
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
|
|
|
|
// Support: IE 10-11, Edge 10240+
|
|
// In IE/Edge using regex groups here causes severe slowdowns.
|
|
// See https://connect.microsoft.com/IE/feedback/details/1736512/
|
|
rnoInnerhtml = /<script|<style|<link/i,
|
|
|
|
// checked="checked" or checked
|
|
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
|
|
rscriptTypeMasked = /^true\/(.*)/,
|
|
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
|
|
|
|
// Manipulating tables requires a tbody
|
|
function manipulationTarget( elem, content ) {
|
|
return jQuery.nodeName( elem, "table" ) &&
|
|
jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
|
|
|
|
elem.getElementsByTagName( "tbody" )[ 0 ] ||
|
|
elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
|
|
elem;
|
|
}
|
|
|
|
// Replace/restore the type attribute of script elements for safe DOM manipulation
|
|
function disableScript( elem ) {
|
|
elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
|
|
return elem;
|
|
}
|
|
function restoreScript( elem ) {
|
|
var match = rscriptTypeMasked.exec( elem.type );
|
|
|
|
if ( match ) {
|
|
elem.type = match[ 1 ];
|
|
} else {
|
|
elem.removeAttribute( "type" );
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
function cloneCopyEvent( src, dest ) {
|
|
var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
|
|
|
|
if ( dest.nodeType !== 1 ) {
|
|
return;
|
|
}
|
|
|
|
// 1. Copy private data: events, handlers, etc.
|
|
if ( dataPriv.hasData( src ) ) {
|
|
pdataOld = dataPriv.access( src );
|
|
pdataCur = dataPriv.set( dest, pdataOld );
|
|
events = pdataOld.events;
|
|
|
|
if ( events ) {
|
|
delete pdataCur.handle;
|
|
pdataCur.events = {};
|
|
|
|
for ( type in events ) {
|
|
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
|
|
jQuery.event.add( dest, type, events[ type ][ i ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Copy user data
|
|
if ( dataUser.hasData( src ) ) {
|
|
udataOld = dataUser.access( src );
|
|
udataCur = jQuery.extend( {}, udataOld );
|
|
|
|
dataUser.set( dest, udataCur );
|
|
}
|
|
}
|
|
|
|
// Fix IE bugs, see support tests
|
|
function fixInput( src, dest ) {
|
|
var nodeName = dest.nodeName.toLowerCase();
|
|
|
|
// Fails to persist the checked state of a cloned checkbox or radio button.
|
|
if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
|
|
dest.checked = src.checked;
|
|
|
|
// Fails to return the selected option to the default selected state when cloning options
|
|
} else if ( nodeName === "input" || nodeName === "textarea" ) {
|
|
dest.defaultValue = src.defaultValue;
|
|
}
|
|
}
|
|
|
|
function domManip( collection, args, callback, ignored ) {
|
|
|
|
// Flatten any nested arrays
|
|
args = concat.apply( [], args );
|
|
|
|
var fragment, first, scripts, hasScripts, node, doc,
|
|
i = 0,
|
|
l = collection.length,
|
|
iNoClone = l - 1,
|
|
value = args[ 0 ],
|
|
isFunction = jQuery.isFunction( value );
|
|
|
|
// We can't cloneNode fragments that contain checked, in WebKit
|
|
if ( isFunction ||
|
|
( l > 1 && typeof value === "string" &&
|
|
!support.checkClone && rchecked.test( value ) ) ) {
|
|
return collection.each( function( index ) {
|
|
var self = collection.eq( index );
|
|
if ( isFunction ) {
|
|
args[ 0 ] = value.call( this, index, self.html() );
|
|
}
|
|
domManip( self, args, callback, ignored );
|
|
} );
|
|
}
|
|
|
|
if ( l ) {
|
|
fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
|
|
first = fragment.firstChild;
|
|
|
|
if ( fragment.childNodes.length === 1 ) {
|
|
fragment = first;
|
|
}
|
|
|
|
// Require either new content or an interest in ignored elements to invoke the callback
|
|
if ( first || ignored ) {
|
|
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
|
|
hasScripts = scripts.length;
|
|
|
|
// Use the original fragment for the last item
|
|
// instead of the first because it can end up
|
|
// being emptied incorrectly in certain situations (#8070).
|
|
for ( ; i < l; i++ ) {
|
|
node = fragment;
|
|
|
|
if ( i !== iNoClone ) {
|
|
node = jQuery.clone( node, true, true );
|
|
|
|
// Keep references to cloned scripts for later restoration
|
|
if ( hasScripts ) {
|
|
|
|
// Support: Android<4.1, PhantomJS<2
|
|
// push.apply(_, arraylike) throws on ancient WebKit
|
|
jQuery.merge( scripts, getAll( node, "script" ) );
|
|
}
|
|
}
|
|
|
|
callback.call( collection[ i ], node, i );
|
|
}
|
|
|
|
if ( hasScripts ) {
|
|
doc = scripts[ scripts.length - 1 ].ownerDocument;
|
|
|
|
// Reenable scripts
|
|
jQuery.map( scripts, restoreScript );
|
|
|
|
// Evaluate executable scripts on first document insertion
|
|
for ( i = 0; i < hasScripts; i++ ) {
|
|
node = scripts[ i ];
|
|
if ( rscriptType.test( node.type || "" ) &&
|
|
!dataPriv.access( node, "globalEval" ) &&
|
|
jQuery.contains( doc, node ) ) {
|
|
|
|
if ( node.src ) {
|
|
|
|
// Optional AJAX dependency, but won't run scripts if not present
|
|
if ( jQuery._evalUrl ) {
|
|
jQuery._evalUrl( node.src );
|
|
}
|
|
} else {
|
|
jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return collection;
|
|
}
|
|
|
|
function remove( elem, selector, keepData ) {
|
|
var node,
|
|
nodes = selector ? jQuery.filter( selector, elem ) : elem,
|
|
i = 0;
|
|
|
|
for ( ; ( node = nodes[ i ] ) != null; i++ ) {
|
|
if ( !keepData && node.nodeType === 1 ) {
|
|
jQuery.cleanData( getAll( node ) );
|
|
}
|
|
|
|
if ( node.parentNode ) {
|
|
if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
|
|
setGlobalEval( getAll( node, "script" ) );
|
|
}
|
|
node.parentNode.removeChild( node );
|
|
}
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
jQuery.extend( {
|
|
htmlPrefilter: function( html ) {
|
|
return html.replace( rxhtmlTag, "<$1></$2>" );
|
|
},
|
|
|
|
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
|
|
var i, l, srcElements, destElements,
|
|
clone = elem.cloneNode( true ),
|
|
inPage = jQuery.contains( elem.ownerDocument, elem );
|
|
|
|
// Fix IE cloning issues
|
|
if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
|
|
!jQuery.isXMLDoc( elem ) ) {
|
|
|
|
// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
|
|
destElements = getAll( clone );
|
|
srcElements = getAll( elem );
|
|
|
|
for ( i = 0, l = srcElements.length; i < l; i++ ) {
|
|
fixInput( srcElements[ i ], destElements[ i ] );
|
|
}
|
|
}
|
|
|
|
// Copy the events from the original to the clone
|
|
if ( dataAndEvents ) {
|
|
if ( deepDataAndEvents ) {
|
|
srcElements = srcElements || getAll( elem );
|
|
destElements = destElements || getAll( clone );
|
|
|
|
for ( i = 0, l = srcElements.length; i < l; i++ ) {
|
|
cloneCopyEvent( srcElements[ i ], destElements[ i ] );
|
|
}
|
|
} else {
|
|
cloneCopyEvent( elem, clone );
|
|
}
|
|
}
|
|
|
|
// Preserve script evaluation history
|
|
destElements = getAll( clone, "script" );
|
|
if ( destElements.length > 0 ) {
|
|
setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
|
|
}
|
|
|
|
// Return the cloned set
|
|
return clone;
|
|
},
|
|
|
|
cleanData: function( elems ) {
|
|
var data, elem, type,
|
|
special = jQuery.event.special,
|
|
i = 0;
|
|
|
|
for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
|
|
if ( acceptData( elem ) ) {
|
|
if ( ( data = elem[ dataPriv.expando ] ) ) {
|
|
if ( data.events ) {
|
|
for ( type in data.events ) {
|
|
if ( special[ type ] ) {
|
|
jQuery.event.remove( elem, type );
|
|
|
|
// This is a shortcut to avoid jQuery.event.remove's overhead
|
|
} else {
|
|
jQuery.removeEvent( elem, type, data.handle );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Support: Chrome <= 35-45+
|
|
// Assign undefined instead of using delete, see Data#remove
|
|
elem[ dataPriv.expando ] = undefined;
|
|
}
|
|
if ( elem[ dataUser.expando ] ) {
|
|
|
|
// Support: Chrome <= 35-45+
|
|
// Assign undefined instead of using delete, see Data#remove
|
|
elem[ dataUser.expando ] = undefined;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
|
|
// Keep domManip exposed until 3.0 (gh-2225)
|
|
domManip: domManip,
|
|
|
|
detach: function( selector ) {
|
|
return remove( this, selector, true );
|
|
},
|
|
|
|
remove: function( selector ) {
|
|
return remove( this, selector );
|
|
},
|
|
|
|
text: function( value ) {
|
|
return access( this, function( value ) {
|
|
return value === undefined ?
|
|
jQuery.text( this ) :
|
|
this.empty().each( function() {
|
|
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
|
this.textContent = value;
|
|
}
|
|
} );
|
|
}, null, value, arguments.length );
|
|
},
|
|
|
|
append: function() {
|
|
return domManip( this, arguments, function( elem ) {
|
|
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
|
var target = manipulationTarget( this, elem );
|
|
target.appendChild( elem );
|
|
}
|
|
} );
|
|
},
|
|
|
|
prepend: function() {
|
|
return domManip( this, arguments, function( elem ) {
|
|
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
|
var target = manipulationTarget( this, elem );
|
|
target.insertBefore( elem, target.firstChild );
|
|
}
|
|
} );
|
|
},
|
|
|
|
before: function() {
|
|
return domManip( this, arguments, function( elem ) {
|
|
if ( this.parentNode ) {
|
|
this.parentNode.insertBefore( elem, this );
|
|
}
|
|
} );
|
|
},
|
|
|
|
after: function() {
|
|
return domManip( this, arguments, function( elem ) {
|
|
if ( this.parentNode ) {
|
|
this.parentNode.insertBefore( elem, this.nextSibling );
|
|
}
|
|
} );
|
|
},
|
|
|
|
empty: function() {
|
|
var elem,
|
|
i = 0;
|
|
|
|
for ( ; ( elem = this[ i ] ) != null; i++ ) {
|
|
if ( elem.nodeType === 1 ) {
|
|
|
|
// Prevent memory leaks
|
|
jQuery.cleanData( getAll( elem, false ) );
|
|
|
|
// Remove any remaining nodes
|
|
elem.textContent = "";
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
clone: function( dataAndEvents, deepDataAndEvents ) {
|
|
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
|
|
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
|
|
|
|
return this.map( function() {
|
|
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
|
|
} );
|
|
},
|
|
|
|
html: function( value ) {
|
|
return access( this, function( value ) {
|
|
var elem = this[ 0 ] || {},
|
|
i = 0,
|
|
l = this.length;
|
|
|
|
if ( value === undefined && elem.nodeType === 1 ) {
|
|
return elem.innerHTML;
|
|
}
|
|
|
|
// See if we can take a shortcut and just use innerHTML
|
|
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
|
|
!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
|
|
|
|
value = jQuery.htmlPrefilter( value );
|
|
|
|
try {
|
|
for ( ; i < l; i++ ) {
|
|
elem = this[ i ] || {};
|
|
|
|
// Remove element nodes and prevent memory leaks
|
|
if ( elem.nodeType === 1 ) {
|
|
jQuery.cleanData( getAll( elem, false ) );
|
|
elem.innerHTML = value;
|
|
}
|
|
}
|
|
|
|
elem = 0;
|
|
|
|
// If using innerHTML throws an exception, use the fallback method
|
|
} catch ( e ) {}
|
|
}
|
|
|
|
if ( elem ) {
|
|
this.empty().append( value );
|
|
}
|
|
}, null, value, arguments.length );
|
|
},
|
|
|
|
replaceWith: function() {
|
|
var ignored = [];
|
|
|
|
// Make the changes, replacing each non-ignored context element with the new content
|
|
return domManip( this, arguments, function( elem ) {
|
|
var parent = this.parentNode;
|
|
|
|
if ( jQuery.inArray( this, ignored ) < 0 ) {
|
|
jQuery.cleanData( getAll( this ) );
|
|
if ( parent ) {
|
|
parent.replaceChild( elem, this );
|
|
}
|
|
}
|
|
|
|
// Force callback invocation
|
|
}, ignored );
|
|
}
|
|
} );
|
|
|
|
jQuery.each( {
|
|
appendTo: "append",
|
|
prependTo: "prepend",
|
|
insertBefore: "before",
|
|
insertAfter: "after",
|
|
replaceAll: "replaceWith"
|
|
}, function( name, original ) {
|
|
jQuery.fn[ name ] = function( selector ) {
|
|
var elems,
|
|
ret = [],
|
|
insert = jQuery( selector ),
|
|
last = insert.length - 1,
|
|
i = 0;
|
|
|
|
for ( ; i <= last; i++ ) {
|
|
elems = i === last ? this : this.clone( true );
|
|
jQuery( insert[ i ] )[ original ]( elems );
|
|
|
|
// Support: QtWebKit
|
|
// .get() because push.apply(_, arraylike) throws
|
|
push.apply( ret, elems.get() );
|
|
}
|
|
|
|
return this.pushStack( ret );
|
|
};
|
|
} );
|
|
|
|
|
|
var iframe,
|
|
elemdisplay = {
|
|
|
|
// Support: Firefox
|
|
// We have to pre-define these values for FF (#10227)
|
|
HTML: "block",
|
|
BODY: "block"
|
|
};
|
|
|
|
/**
|
|
* Retrieve the actual display of a element
|
|
* @param {String} name nodeName of the element
|
|
* @param {Object} doc Document object
|
|
*/
|
|
|
|
// Called only from within defaultDisplay
|
|
function actualDisplay( name, doc ) {
|
|
var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
|
|
|
|
display = jQuery.css( elem[ 0 ], "display" );
|
|
|
|
// We don't have any data stored on the element,
|
|
// so use "detach" method as fast way to get rid of the element
|
|
elem.detach();
|
|
|
|
return display;
|
|
}
|
|
|
|
/**
|
|
* Try to determine the default display value of an element
|
|
* @param {String} nodeName
|
|
*/
|
|
function defaultDisplay( nodeName ) {
|
|
var doc = document,
|
|
display = elemdisplay[ nodeName ];
|
|
|
|
if ( !display ) {
|
|
display = actualDisplay( nodeName, doc );
|
|
|
|
// If the simple way fails, read from inside an iframe
|
|
if ( display === "none" || !display ) {
|
|
|
|
// Use the already-created iframe if possible
|
|
iframe = ( iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" ) )
|
|
.appendTo( doc.documentElement );
|
|
|
|
// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
|
|
doc = iframe[ 0 ].contentDocument;
|
|
|
|
// Support: IE
|
|
doc.write();
|
|
doc.close();
|
|
|
|
display = actualDisplay( nodeName, doc );
|
|
iframe.detach();
|
|
}
|
|
|
|
// Store the correct default display
|
|
elemdisplay[ nodeName ] = display;
|
|
}
|
|
|
|
return display;
|
|
}
|
|
var rmargin = ( /^margin/ );
|
|
|
|
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
|
|
|
|
var getStyles = function( elem ) {
|
|
|
|
// Support: IE<=11+, Firefox<=30+ (#15098, #14150)
|
|
// IE throws on elements created in popups
|
|
// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
|
|
var view = elem.ownerDocument.defaultView;
|
|
|
|
if ( !view || !view.opener ) {
|
|
view = window;
|
|
}
|
|
|
|
return view.getComputedStyle( elem );
|
|
};
|
|
|
|
var swap = function( elem, options, callback, args ) {
|
|
var ret, name,
|
|
old = {};
|
|
|
|
// Remember the old values, and insert the new ones
|
|
for ( name in options ) {
|
|
old[ name ] = elem.style[ name ];
|
|
elem.style[ name ] = options[ name ];
|
|
}
|
|
|
|
ret = callback.apply( elem, args || [] );
|
|
|
|
// Revert the old values
|
|
for ( name in options ) {
|
|
elem.style[ name ] = old[ name ];
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
|
|
var documentElement = document.documentElement;
|
|
|
|
|
|
|
|
( function() {
|
|
var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
|
|
container = document.createElement( "div" ),
|
|
div = document.createElement( "div" );
|
|
|
|
// Finish early in limited (non-browser) environments
|
|
if ( !div.style ) {
|
|
return;
|
|
}
|
|
|
|
// Support: IE9-11+
|
|
// Style of cloned element affects source element cloned (#8908)
|
|
div.style.backgroundClip = "content-box";
|
|
div.cloneNode( true ).style.backgroundClip = "";
|
|
support.clearCloneStyle = div.style.backgroundClip === "content-box";
|
|
|
|
container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
|
|
"padding:0;margin-top:1px;position:absolute";
|
|
container.appendChild( div );
|
|
|
|
// Executing both pixelPosition & boxSizingReliable tests require only one layout
|
|
// so they're executed at the same time to save the second computation.
|
|
function computeStyleTests() {
|
|
div.style.cssText =
|
|
|
|
// Support: Firefox<29, Android 2.3
|
|
// Vendor-prefix box-sizing
|
|
"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;" +
|
|
"position:relative;display:block;" +
|
|
"margin:auto;border:1px;padding:1px;" +
|
|
"top:1%;width:50%";
|
|
div.innerHTML = "";
|
|
documentElement.appendChild( container );
|
|
|
|
var divStyle = window.getComputedStyle( div );
|
|
pixelPositionVal = divStyle.top !== "1%";
|
|
reliableMarginLeftVal = divStyle.marginLeft === "2px";
|
|
boxSizingReliableVal = divStyle.width === "4px";
|
|
|
|
// Support: Android 4.0 - 4.3 only
|
|
// Some styles come back with percentage values, even though they shouldn't
|
|
div.style.marginRight = "50%";
|
|
pixelMarginRightVal = divStyle.marginRight === "4px";
|
|
|
|
documentElement.removeChild( container );
|
|
}
|
|
|
|
jQuery.extend( support, {
|
|
pixelPosition: function() {
|
|
|
|
// This test is executed only once but we still do memoizing
|
|
// since we can use the boxSizingReliable pre-computing.
|
|
// No need to check if the test was already performed, though.
|
|
computeStyleTests();
|
|
return pixelPositionVal;
|
|
},
|
|
boxSizingReliable: function() {
|
|
if ( boxSizingReliableVal == null ) {
|
|
computeStyleTests();
|
|
}
|
|
return boxSizingReliableVal;
|
|
},
|
|
pixelMarginRight: function() {
|
|
|
|
// Support: Android 4.0-4.3
|
|
// We're checking for boxSizingReliableVal here instead of pixelMarginRightVal
|
|
// since that compresses better and they're computed together anyway.
|
|
if ( boxSizingReliableVal == null ) {
|
|
computeStyleTests();
|
|
}
|
|
return pixelMarginRightVal;
|
|
},
|
|
reliableMarginLeft: function() {
|
|
|
|
// Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37
|
|
if ( boxSizingReliableVal == null ) {
|
|
computeStyleTests();
|
|
}
|
|
return reliableMarginLeftVal;
|
|
},
|
|
reliableMarginRight: function() {
|
|
|
|
// Support: Android 2.3
|
|
// Check if div with explicit width and no margin-right incorrectly
|
|
// gets computed margin-right based on width of container. (#3333)
|
|
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
|
|
// This support function is only executed once so no memoizing is needed.
|
|
var ret,
|
|
marginDiv = div.appendChild( document.createElement( "div" ) );
|
|
|
|
// Reset CSS: box-sizing; display; margin; border; padding
|
|
marginDiv.style.cssText = div.style.cssText =
|
|
|
|
// Support: Android 2.3
|
|
// Vendor-prefix box-sizing
|
|
"-webkit-box-sizing:content-box;box-sizing:content-box;" +
|
|
"display:block;margin:0;border:0;padding:0";
|
|
marginDiv.style.marginRight = marginDiv.style.width = "0";
|
|
div.style.width = "1px";
|
|
documentElement.appendChild( container );
|
|
|
|
ret = !parseFloat( window.getComputedStyle( marginDiv ).marginRight );
|
|
|
|
documentElement.removeChild( container );
|
|
div.removeChild( marginDiv );
|
|
|
|
return ret;
|
|
}
|
|
} );
|
|
} )();
|
|
|
|
|
|
function curCSS( elem, name, computed ) {
|
|
var width, minWidth, maxWidth, ret,
|
|
style = elem.style;
|
|
|
|
computed = computed || getStyles( elem );
|
|
ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
|
|
|
|
// Support: Opera 12.1x only
|
|
// Fall back to style even without computed
|
|
// computed is undefined for elems on document fragments
|
|
if ( ( ret === "" || ret === undefined ) && !jQuery.contains( elem.ownerDocument, elem ) ) {
|
|
ret = jQuery.style( elem, name );
|
|
}
|
|
|
|
// Support: IE9
|
|
// getPropertyValue is only needed for .css('filter') (#12537)
|
|
if ( computed ) {
|
|
|
|
// A tribute to the "awesome hack by Dean Edwards"
|
|
// Android Browser returns percentage for some values,
|
|
// but width seems to be reliably pixels.
|
|
// This is against the CSSOM draft spec:
|
|
// http://dev.w3.org/csswg/cssom/#resolved-values
|
|
if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
|
|
|
|
// Remember the original values
|
|
width = style.width;
|
|
minWidth = style.minWidth;
|
|
maxWidth = style.maxWidth;
|
|
|
|
// Put in the new values to get a computed value out
|
|
style.minWidth = style.maxWidth = style.width = ret;
|
|
ret = computed.width;
|
|
|
|
// Revert the changed values
|
|
style.width = width;
|
|
style.minWidth = minWidth;
|
|
style.maxWidth = maxWidth;
|
|
}
|
|
}
|
|
|
|
return ret !== undefined ?
|
|
|
|
// Support: IE9-11+
|
|
// IE returns zIndex value as an integer.
|
|
ret + "" :
|
|
ret;
|
|
}
|
|
|
|
|
|
function addGetHookIf( conditionFn, hookFn ) {
|
|
|
|
// Define the hook, we'll check on the first run if it's really needed.
|
|
return {
|
|
get: function() {
|
|
if ( conditionFn() ) {
|
|
|
|
// Hook not needed (or it's not possible to use it due
|
|
// to missing dependency), remove it.
|
|
delete this.get;
|
|
return;
|
|
}
|
|
|
|
// Hook needed; redefine it so that the support test is not executed again.
|
|
return ( this.get = hookFn ).apply( this, arguments );
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
var
|
|
|
|
// Swappable if display is none or starts with table
|
|
// except "table", "table-cell", or "table-caption"
|
|
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
|
|
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
|
|
|
|
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
|
|
cssNormalTransform = {
|
|
letterSpacing: "0",
|
|
fontWeight: "400"
|
|
},
|
|
|
|
cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
|
|
emptyStyle = document.createElement( "div" ).style;
|
|
|
|
// Return a css property mapped to a potentially vendor prefixed property
|
|
function vendorPropName( name ) {
|
|
|
|
// Shortcut for names that are not vendor prefixed
|
|
if ( name in emptyStyle ) {
|
|
return name;
|
|
}
|
|
|
|
// Check for vendor prefixed names
|
|
var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
|
|
i = cssPrefixes.length;
|
|
|
|
while ( i-- ) {
|
|
name = cssPrefixes[ i ] + capName;
|
|
if ( name in emptyStyle ) {
|
|
return name;
|
|
}
|
|
}
|
|
}
|
|
|
|
function setPositiveNumber( elem, value, subtract ) {
|
|
|
|
// Any relative (+/-) values have already been
|
|
// normalized at this point
|
|
var matches = rcssNum.exec( value );
|
|
return matches ?
|
|
|
|
// Guard against undefined "subtract", e.g., when used as in cssHooks
|
|
Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
|
|
value;
|
|
}
|
|
|
|
function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
|
|
var i = extra === ( isBorderBox ? "border" : "content" ) ?
|
|
|
|
// If we already have the right measurement, avoid augmentation
|
|
4 :
|
|
|
|
// Otherwise initialize for horizontal or vertical properties
|
|
name === "width" ? 1 : 0,
|
|
|
|
val = 0;
|
|
|
|
for ( ; i < 4; i += 2 ) {
|
|
|
|
// Both box models exclude margin, so add it if we want it
|
|
if ( extra === "margin" ) {
|
|
val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
|
|
}
|
|
|
|
if ( isBorderBox ) {
|
|
|
|
// border-box includes padding, so remove it if we want content
|
|
if ( extra === "content" ) {
|
|
val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
|
|
}
|
|
|
|
// At this point, extra isn't border nor margin, so remove border
|
|
if ( extra !== "margin" ) {
|
|
val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
|
}
|
|
} else {
|
|
|
|
// At this point, extra isn't content, so add padding
|
|
val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
|
|
|
|
// At this point, extra isn't content nor padding, so add border
|
|
if ( extra !== "padding" ) {
|
|
val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
|
}
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
function getWidthOrHeight( elem, name, extra ) {
|
|
|
|
// Start with offset property, which is equivalent to the border-box value
|
|
var valueIsBorderBox = true,
|
|
val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
|
|
styles = getStyles( elem ),
|
|
isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
|
|
|
|
// Some non-html elements return undefined for offsetWidth, so check for null/undefined
|
|
// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
|
|
// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
|
|
if ( val <= 0 || val == null ) {
|
|
|
|
// Fall back to computed then uncomputed css if necessary
|
|
val = curCSS( elem, name, styles );
|
|
if ( val < 0 || val == null ) {
|
|
val = elem.style[ name ];
|
|
}
|
|
|
|
// Computed unit is not pixels. Stop here and return.
|
|
if ( rnumnonpx.test( val ) ) {
|
|
return val;
|
|
}
|
|
|
|
// Check for style in case a browser which returns unreliable values
|
|
// for getComputedStyle silently falls back to the reliable elem.style
|
|
valueIsBorderBox = isBorderBox &&
|
|
( support.boxSizingReliable() || val === elem.style[ name ] );
|
|
|
|
// Normalize "", auto, and prepare for extra
|
|
val = parseFloat( val ) || 0;
|
|
}
|
|
|
|
// Use the active box-sizing model to add/subtract irrelevant styles
|
|
return ( val +
|
|
augmentWidthOrHeight(
|
|
elem,
|
|
name,
|
|
extra || ( isBorderBox ? "border" : "content" ),
|
|
valueIsBorderBox,
|
|
styles
|
|
)
|
|
) + "px";
|
|
}
|
|
|
|
function showHide( elements, show ) {
|
|
var display, elem, hidden,
|
|
values = [],
|
|
index = 0,
|
|
length = elements.length;
|
|
|
|
for ( ; index < length; index++ ) {
|
|
elem = elements[ index ];
|
|
if ( !elem.style ) {
|
|
continue;
|
|
}
|
|
|
|
values[ index ] = dataPriv.get( elem, "olddisplay" );
|
|
display = elem.style.display;
|
|
if ( show ) {
|
|
|
|
// Reset the inline display of this element to learn if it is
|
|
// being hidden by cascaded rules or not
|
|
if ( !values[ index ] && display === "none" ) {
|
|
elem.style.display = "";
|
|
}
|
|
|
|
// Set elements which have been overridden with display: none
|
|
// in a stylesheet to whatever the default browser style is
|
|
// for such an element
|
|
if ( elem.style.display === "" && isHidden( elem ) ) {
|
|
values[ index ] = dataPriv.access(
|
|
elem,
|
|
"olddisplay",
|
|
defaultDisplay( elem.nodeName )
|
|
);
|
|
}
|
|
} else {
|
|
hidden = isHidden( elem );
|
|
|
|
if ( display !== "none" || !hidden ) {
|
|
dataPriv.set(
|
|
elem,
|
|
"olddisplay",
|
|
hidden ? display : jQuery.css( elem, "display" )
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the display of most of the elements in a second loop
|
|
// to avoid the constant reflow
|
|
for ( index = 0; index < length; index++ ) {
|
|
elem = elements[ index ];
|
|
if ( !elem.style ) {
|
|
continue;
|
|
}
|
|
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
|
|
elem.style.display = show ? values[ index ] || "" : "none";
|
|
}
|
|
}
|
|
|
|
return elements;
|
|
}
|
|
|
|
jQuery.extend( {
|
|
|
|
// Add in style property hooks for overriding the default
|
|
// behavior of getting and setting a style property
|
|
cssHooks: {
|
|
opacity: {
|
|
get: function( elem, computed ) {
|
|
if ( computed ) {
|
|
|
|
// We should always get a number back from opacity
|
|
var ret = curCSS( elem, "opacity" );
|
|
return ret === "" ? "1" : ret;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// Don't automatically add "px" to these possibly-unitless properties
|
|
cssNumber: {
|
|
"animationIterationCount": true,
|
|
"columnCount": true,
|
|
"fillOpacity": true,
|
|
"flexGrow": true,
|
|
"flexShrink": true,
|
|
"fontWeight": true,
|
|
"lineHeight": true,
|
|
"opacity": true,
|
|
"order": true,
|
|
"orphans": true,
|
|
"widows": true,
|
|
"zIndex": true,
|
|
"zoom": true
|
|
},
|
|
|
|
// Add in properties whose names you wish to fix before
|
|
// setting or getting the value
|
|
cssProps: {
|
|
"float": "cssFloat"
|
|
},
|
|
|
|
// Get and set the style property on a DOM Node
|
|
style: function( elem, name, value, extra ) {
|
|
|
|
// Don't set styles on text and comment nodes
|
|
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
|
|
return;
|
|
}
|
|
|
|
// Make sure that we're working with the right name
|
|
var ret, type, hooks,
|
|
origName = jQuery.camelCase( name ),
|
|
style = elem.style;
|
|
|
|
name = jQuery.cssProps[ origName ] ||
|
|
( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
|
|
|
|
// Gets hook for the prefixed version, then unprefixed version
|
|
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
|
|
|
|
// Check if we're setting a value
|
|
if ( value !== undefined ) {
|
|
type = typeof value;
|
|
|
|
// Convert "+=" or "-=" to relative numbers (#7345)
|
|
if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
|
|
value = adjustCSS( elem, name, ret );
|
|
|
|
// Fixes bug #9237
|
|
type = "number";
|
|
}
|
|
|
|
// Make sure that null and NaN values aren't set (#7116)
|
|
if ( value == null || value !== value ) {
|
|
return;
|
|
}
|
|
|
|
// If a number was passed in, add the unit (except for certain CSS properties)
|
|
if ( type === "number" ) {
|
|
value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
|
|
}
|
|
|
|
// Support: IE9-11+
|
|
// background-* props affect original clone's values
|
|
if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
|
|
style[ name ] = "inherit";
|
|
}
|
|
|
|
// If a hook was provided, use that value, otherwise just set the specified value
|
|
if ( !hooks || !( "set" in hooks ) ||
|
|
( value = hooks.set( elem, value, extra ) ) !== undefined ) {
|
|
|
|
style[ name ] = value;
|
|
}
|
|
|
|
} else {
|
|
|
|
// If a hook was provided get the non-computed value from there
|
|
if ( hooks && "get" in hooks &&
|
|
( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Otherwise just get the value from the style object
|
|
return style[ name ];
|
|
}
|
|
},
|
|
|
|
css: function( elem, name, extra, styles ) {
|
|
var val, num, hooks,
|
|
origName = jQuery.camelCase( name );
|
|
|
|
// Make sure that we're working with the right name
|
|
name = jQuery.cssProps[ origName ] ||
|
|
( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
|
|
|
|
// Try prefixed name followed by the unprefixed name
|
|
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
|
|
|
|
// If a hook was provided get the computed value from there
|
|
if ( hooks && "get" in hooks ) {
|
|
val = hooks.get( elem, true, extra );
|
|
}
|
|
|
|
// Otherwise, if a way to get the computed value exists, use that
|
|
if ( val === undefined ) {
|
|
val = curCSS( elem, name, styles );
|
|
}
|
|
|
|
// Convert "normal" to computed value
|
|
if ( val === "normal" && name in cssNormalTransform ) {
|
|
val = cssNormalTransform[ name ];
|
|
}
|
|
|
|
// Make numeric if forced or a qualifier was provided and val looks numeric
|
|
if ( extra === "" || extra ) {
|
|
num = parseFloat( val );
|
|
return extra === true || isFinite( num ) ? num || 0 : val;
|
|
}
|
|
return val;
|
|
}
|
|
} );
|
|
|
|
jQuery.each( [ "height", "width" ], function( i, name ) {
|
|
jQuery.cssHooks[ name ] = {
|
|
get: function( elem, computed, extra ) {
|
|
if ( computed ) {
|
|
|
|
// Certain elements can have dimension info if we invisibly show them
|
|
// but it must have a current display style that would benefit
|
|
return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
|
|
elem.offsetWidth === 0 ?
|
|
swap( elem, cssShow, function() {
|
|
return getWidthOrHeight( elem, name, extra );
|
|
} ) :
|
|
getWidthOrHeight( elem, name, extra );
|
|
}
|
|
},
|
|
|
|
set: function( elem, value, extra ) {
|
|
var matches,
|
|
styles = extra && getStyles( elem ),
|
|
subtract = extra && augmentWidthOrHeight(
|
|
elem,
|
|
name,
|
|
extra,
|
|
jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
|
|
styles
|
|
);
|
|
|
|
// Convert to pixels if value adjustment is needed
|
|
if ( subtract && ( matches = rcssNum.exec( value ) ) &&
|
|
( matches[ 3 ] || "px" ) !== "px" ) {
|
|
|
|
elem.style[ name ] = value;
|
|
value = jQuery.css( elem, name );
|
|
}
|
|
|
|
return setPositiveNumber( elem, value, subtract );
|
|
}
|
|
};
|
|
} );
|
|
|
|
jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
|
|
function( elem, computed ) {
|
|
if ( computed ) {
|
|
return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
|
|
elem.getBoundingClientRect().left -
|
|
swap( elem, { marginLeft: 0 }, function() {
|
|
return elem.getBoundingClientRect().left;
|
|
} )
|
|
) + "px";
|
|
}
|
|
}
|
|
);
|
|
|
|
// Support: Android 2.3
|
|
jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
|
|
function( elem, computed ) {
|
|
if ( computed ) {
|
|
return swap( elem, { "display": "inline-block" },
|
|
curCSS, [ elem, "marginRight" ] );
|
|
}
|
|
}
|
|
);
|
|
|
|
// These hooks are used by animate to expand properties
|
|
jQuery.each( {
|
|
margin: "",
|
|
padding: "",
|
|
border: "Width"
|
|
}, function( prefix, suffix ) {
|
|
jQuery.cssHooks[ prefix + suffix ] = {
|
|
expand: function( value ) {
|
|
var i = 0,
|
|
expanded = {},
|
|
|
|
// Assumes a single number if not a string
|
|
parts = typeof value === "string" ? value.split( " " ) : [ value ];
|
|
|
|
for ( ; i < 4; i++ ) {
|
|
expanded[ prefix + cssExpand[ i ] + suffix ] =
|
|
parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
|
|
}
|
|
|
|
return expanded;
|
|
}
|
|
};
|
|
|
|
if ( !rmargin.test( prefix ) ) {
|
|
jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
|
|
}
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
css: function( name, value ) {
|
|
return access( this, function( elem, name, value ) {
|
|
var styles, len,
|
|
map = {},
|
|
i = 0;
|
|
|
|
if ( jQuery.isArray( name ) ) {
|
|
styles = getStyles( elem );
|
|
len = name.length;
|
|
|
|
for ( ; i < len; i++ ) {
|
|
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
return value !== undefined ?
|
|
jQuery.style( elem, name, value ) :
|
|
jQuery.css( elem, name );
|
|
}, name, value, arguments.length > 1 );
|
|
},
|
|
show: function() {
|
|
return showHide( this, true );
|
|
},
|
|
hide: function() {
|
|
return showHide( this );
|
|
},
|
|
toggle: function( state ) {
|
|
if ( typeof state === "boolean" ) {
|
|
return state ? this.show() : this.hide();
|
|
}
|
|
|
|
return this.each( function() {
|
|
if ( isHidden( this ) ) {
|
|
jQuery( this ).show();
|
|
} else {
|
|
jQuery( this ).hide();
|
|
}
|
|
} );
|
|
}
|
|
} );
|
|
|
|
|
|
function Tween( elem, options, prop, end, easing ) {
|
|
return new Tween.prototype.init( elem, options, prop, end, easing );
|
|
}
|
|
jQuery.Tween = Tween;
|
|
|
|
Tween.prototype = {
|
|
constructor: Tween,
|
|
init: function( elem, options, prop, end, easing, unit ) {
|
|
this.elem = elem;
|
|
this.prop = prop;
|
|
this.easing = easing || jQuery.easing._default;
|
|
this.options = options;
|
|
this.start = this.now = this.cur();
|
|
this.end = end;
|
|
this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
|
|
},
|
|
cur: function() {
|
|
var hooks = Tween.propHooks[ this.prop ];
|
|
|
|
return hooks && hooks.get ?
|
|
hooks.get( this ) :
|
|
Tween.propHooks._default.get( this );
|
|
},
|
|
run: function( percent ) {
|
|
var eased,
|
|
hooks = Tween.propHooks[ this.prop ];
|
|
|
|
if ( this.options.duration ) {
|
|
this.pos = eased = jQuery.easing[ this.easing ](
|
|
percent, this.options.duration * percent, 0, 1, this.options.duration
|
|
);
|
|
} else {
|
|
this.pos = eased = percent;
|
|
}
|
|
this.now = ( this.end - this.start ) * eased + this.start;
|
|
|
|
if ( this.options.step ) {
|
|
this.options.step.call( this.elem, this.now, this );
|
|
}
|
|
|
|
if ( hooks && hooks.set ) {
|
|
hooks.set( this );
|
|
} else {
|
|
Tween.propHooks._default.set( this );
|
|
}
|
|
return this;
|
|
}
|
|
};
|
|
|
|
Tween.prototype.init.prototype = Tween.prototype;
|
|
|
|
Tween.propHooks = {
|
|
_default: {
|
|
get: function( tween ) {
|
|
var result;
|
|
|
|
// Use a property on the element directly when it is not a DOM element,
|
|
// or when there is no matching style property that exists.
|
|
if ( tween.elem.nodeType !== 1 ||
|
|
tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
|
|
return tween.elem[ tween.prop ];
|
|
}
|
|
|
|
// Passing an empty string as a 3rd parameter to .css will automatically
|
|
// attempt a parseFloat and fallback to a string if the parse fails.
|
|
// Simple values such as "10px" are parsed to Float;
|
|
// complex values such as "rotate(1rad)" are returned as-is.
|
|
result = jQuery.css( tween.elem, tween.prop, "" );
|
|
|
|
// Empty strings, null, undefined and "auto" are converted to 0.
|
|
return !result || result === "auto" ? 0 : result;
|
|
},
|
|
set: function( tween ) {
|
|
|
|
// Use step hook for back compat.
|
|
// Use cssHook if its there.
|
|
// Use .style if available and use plain properties where available.
|
|
if ( jQuery.fx.step[ tween.prop ] ) {
|
|
jQuery.fx.step[ tween.prop ]( tween );
|
|
} else if ( tween.elem.nodeType === 1 &&
|
|
( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
|
|
jQuery.cssHooks[ tween.prop ] ) ) {
|
|
jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
|
|
} else {
|
|
tween.elem[ tween.prop ] = tween.now;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Support: IE9
|
|
// Panic based approach to setting things on disconnected nodes
|
|
Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
|
|
set: function( tween ) {
|
|
if ( tween.elem.nodeType && tween.elem.parentNode ) {
|
|
tween.elem[ tween.prop ] = tween.now;
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.easing = {
|
|
linear: function( p ) {
|
|
return p;
|
|
},
|
|
swing: function( p ) {
|
|
return 0.5 - Math.cos( p * Math.PI ) / 2;
|
|
},
|
|
_default: "swing"
|
|
};
|
|
|
|
jQuery.fx = Tween.prototype.init;
|
|
|
|
// Back Compat <1.8 extension point
|
|
jQuery.fx.step = {};
|
|
|
|
|
|
|
|
|
|
var
|
|
fxNow, timerId,
|
|
rfxtypes = /^(?:toggle|show|hide)$/,
|
|
rrun = /queueHooks$/;
|
|
|
|
// Animations created synchronously will run synchronously
|
|
function createFxNow() {
|
|
window.setTimeout( function() {
|
|
fxNow = undefined;
|
|
} );
|
|
return ( fxNow = jQuery.now() );
|
|
}
|
|
|
|
// Generate parameters to create a standard animation
|
|
function genFx( type, includeWidth ) {
|
|
var which,
|
|
i = 0,
|
|
attrs = { height: type };
|
|
|
|
// If we include width, step value is 1 to do all cssExpand values,
|
|
// otherwise step value is 2 to skip over Left and Right
|
|
includeWidth = includeWidth ? 1 : 0;
|
|
for ( ; i < 4 ; i += 2 - includeWidth ) {
|
|
which = cssExpand[ i ];
|
|
attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
|
|
}
|
|
|
|
if ( includeWidth ) {
|
|
attrs.opacity = attrs.width = type;
|
|
}
|
|
|
|
return attrs;
|
|
}
|
|
|
|
function createTween( value, prop, animation ) {
|
|
var tween,
|
|
collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
|
|
index = 0,
|
|
length = collection.length;
|
|
for ( ; index < length; index++ ) {
|
|
if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
|
|
|
|
// We're done with this property
|
|
return tween;
|
|
}
|
|
}
|
|
}
|
|
|
|
function defaultPrefilter( elem, props, opts ) {
|
|
/* jshint validthis: true */
|
|
var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
|
|
anim = this,
|
|
orig = {},
|
|
style = elem.style,
|
|
hidden = elem.nodeType && isHidden( elem ),
|
|
dataShow = dataPriv.get( elem, "fxshow" );
|
|
|
|
// Handle queue: false promises
|
|
if ( !opts.queue ) {
|
|
hooks = jQuery._queueHooks( elem, "fx" );
|
|
if ( hooks.unqueued == null ) {
|
|
hooks.unqueued = 0;
|
|
oldfire = hooks.empty.fire;
|
|
hooks.empty.fire = function() {
|
|
if ( !hooks.unqueued ) {
|
|
oldfire();
|
|
}
|
|
};
|
|
}
|
|
hooks.unqueued++;
|
|
|
|
anim.always( function() {
|
|
|
|
// Ensure the complete handler is called before this completes
|
|
anim.always( function() {
|
|
hooks.unqueued--;
|
|
if ( !jQuery.queue( elem, "fx" ).length ) {
|
|
hooks.empty.fire();
|
|
}
|
|
} );
|
|
} );
|
|
}
|
|
|
|
// Height/width overflow pass
|
|
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
|
|
|
|
// Make sure that nothing sneaks out
|
|
// Record all 3 overflow attributes because IE9-10 do not
|
|
// change the overflow attribute when overflowX and
|
|
// overflowY are set to the same value
|
|
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
|
|
|
|
// Set display property to inline-block for height/width
|
|
// animations on inline elements that are having width/height animated
|
|
display = jQuery.css( elem, "display" );
|
|
|
|
// Test default display if display is currently "none"
|
|
checkDisplay = display === "none" ?
|
|
dataPriv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
|
|
|
|
if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
|
|
style.display = "inline-block";
|
|
}
|
|
}
|
|
|
|
if ( opts.overflow ) {
|
|
style.overflow = "hidden";
|
|
anim.always( function() {
|
|
style.overflow = opts.overflow[ 0 ];
|
|
style.overflowX = opts.overflow[ 1 ];
|
|
style.overflowY = opts.overflow[ 2 ];
|
|
} );
|
|
}
|
|
|
|
// show/hide pass
|
|
for ( prop in props ) {
|
|
value = props[ prop ];
|
|
if ( rfxtypes.exec( value ) ) {
|
|
delete props[ prop ];
|
|
toggle = toggle || value === "toggle";
|
|
if ( value === ( hidden ? "hide" : "show" ) ) {
|
|
|
|
// If there is dataShow left over from a stopped hide or show
|
|
// and we are going to proceed with show, we should pretend to be hidden
|
|
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
|
|
hidden = true;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
|
|
|
|
// Any non-fx value stops us from restoring the original display value
|
|
} else {
|
|
display = undefined;
|
|
}
|
|
}
|
|
|
|
if ( !jQuery.isEmptyObject( orig ) ) {
|
|
if ( dataShow ) {
|
|
if ( "hidden" in dataShow ) {
|
|
hidden = dataShow.hidden;
|
|
}
|
|
} else {
|
|
dataShow = dataPriv.access( elem, "fxshow", {} );
|
|
}
|
|
|
|
// Store state if its toggle - enables .stop().toggle() to "reverse"
|
|
if ( toggle ) {
|
|
dataShow.hidden = !hidden;
|
|
}
|
|
if ( hidden ) {
|
|
jQuery( elem ).show();
|
|
} else {
|
|
anim.done( function() {
|
|
jQuery( elem ).hide();
|
|
} );
|
|
}
|
|
anim.done( function() {
|
|
var prop;
|
|
|
|
dataPriv.remove( elem, "fxshow" );
|
|
for ( prop in orig ) {
|
|
jQuery.style( elem, prop, orig[ prop ] );
|
|
}
|
|
} );
|
|
for ( prop in orig ) {
|
|
tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
|
|
|
|
if ( !( prop in dataShow ) ) {
|
|
dataShow[ prop ] = tween.start;
|
|
if ( hidden ) {
|
|
tween.end = tween.start;
|
|
tween.start = prop === "width" || prop === "height" ? 1 : 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this is a noop like .hide().hide(), restore an overwritten display value
|
|
} else if ( ( display === "none" ? defaultDisplay( elem.nodeName ) : display ) === "inline" ) {
|
|
style.display = display;
|
|
}
|
|
}
|
|
|
|
function propFilter( props, specialEasing ) {
|
|
var index, name, easing, value, hooks;
|
|
|
|
// camelCase, specialEasing and expand cssHook pass
|
|
for ( index in props ) {
|
|
name = jQuery.camelCase( index );
|
|
easing = specialEasing[ name ];
|
|
value = props[ index ];
|
|
if ( jQuery.isArray( value ) ) {
|
|
easing = value[ 1 ];
|
|
value = props[ index ] = value[ 0 ];
|
|
}
|
|
|
|
if ( index !== name ) {
|
|
props[ name ] = value;
|
|
delete props[ index ];
|
|
}
|
|
|
|
hooks = jQuery.cssHooks[ name ];
|
|
if ( hooks && "expand" in hooks ) {
|
|
value = hooks.expand( value );
|
|
delete props[ name ];
|
|
|
|
// Not quite $.extend, this won't overwrite existing keys.
|
|
// Reusing 'index' because we have the correct "name"
|
|
for ( index in value ) {
|
|
if ( !( index in props ) ) {
|
|
props[ index ] = value[ index ];
|
|
specialEasing[ index ] = easing;
|
|
}
|
|
}
|
|
} else {
|
|
specialEasing[ name ] = easing;
|
|
}
|
|
}
|
|
}
|
|
|
|
function Animation( elem, properties, options ) {
|
|
var result,
|
|
stopped,
|
|
index = 0,
|
|
length = Animation.prefilters.length,
|
|
deferred = jQuery.Deferred().always( function() {
|
|
|
|
// Don't match elem in the :animated selector
|
|
delete tick.elem;
|
|
} ),
|
|
tick = function() {
|
|
if ( stopped ) {
|
|
return false;
|
|
}
|
|
var currentTime = fxNow || createFxNow(),
|
|
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
|
|
|
|
// Support: Android 2.3
|
|
// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
|
|
temp = remaining / animation.duration || 0,
|
|
percent = 1 - temp,
|
|
index = 0,
|
|
length = animation.tweens.length;
|
|
|
|
for ( ; index < length ; index++ ) {
|
|
animation.tweens[ index ].run( percent );
|
|
}
|
|
|
|
deferred.notifyWith( elem, [ animation, percent, remaining ] );
|
|
|
|
if ( percent < 1 && length ) {
|
|
return remaining;
|
|
} else {
|
|
deferred.resolveWith( elem, [ animation ] );
|
|
return false;
|
|
}
|
|
},
|
|
animation = deferred.promise( {
|
|
elem: elem,
|
|
props: jQuery.extend( {}, properties ),
|
|
opts: jQuery.extend( true, {
|
|
specialEasing: {},
|
|
easing: jQuery.easing._default
|
|
}, options ),
|
|
originalProperties: properties,
|
|
originalOptions: options,
|
|
startTime: fxNow || createFxNow(),
|
|
duration: options.duration,
|
|
tweens: [],
|
|
createTween: function( prop, end ) {
|
|
var tween = jQuery.Tween( elem, animation.opts, prop, end,
|
|
animation.opts.specialEasing[ prop ] || animation.opts.easing );
|
|
animation.tweens.push( tween );
|
|
return tween;
|
|
},
|
|
stop: function( gotoEnd ) {
|
|
var index = 0,
|
|
|
|
// If we are going to the end, we want to run all the tweens
|
|
// otherwise we skip this part
|
|
length = gotoEnd ? animation.tweens.length : 0;
|
|
if ( stopped ) {
|
|
return this;
|
|
}
|
|
stopped = true;
|
|
for ( ; index < length ; index++ ) {
|
|
animation.tweens[ index ].run( 1 );
|
|
}
|
|
|
|
// Resolve when we played the last frame; otherwise, reject
|
|
if ( gotoEnd ) {
|
|
deferred.notifyWith( elem, [ animation, 1, 0 ] );
|
|
deferred.resolveWith( elem, [ animation, gotoEnd ] );
|
|
} else {
|
|
deferred.rejectWith( elem, [ animation, gotoEnd ] );
|
|
}
|
|
return this;
|
|
}
|
|
} ),
|
|
props = animation.props;
|
|
|
|
propFilter( props, animation.opts.specialEasing );
|
|
|
|
for ( ; index < length ; index++ ) {
|
|
result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
|
|
if ( result ) {
|
|
if ( jQuery.isFunction( result.stop ) ) {
|
|
jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
|
|
jQuery.proxy( result.stop, result );
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
jQuery.map( props, createTween, animation );
|
|
|
|
if ( jQuery.isFunction( animation.opts.start ) ) {
|
|
animation.opts.start.call( elem, animation );
|
|
}
|
|
|
|
jQuery.fx.timer(
|
|
jQuery.extend( tick, {
|
|
elem: elem,
|
|
anim: animation,
|
|
queue: animation.opts.queue
|
|
} )
|
|
);
|
|
|
|
// attach callbacks from options
|
|
return animation.progress( animation.opts.progress )
|
|
.done( animation.opts.done, animation.opts.complete )
|
|
.fail( animation.opts.fail )
|
|
.always( animation.opts.always );
|
|
}
|
|
|
|
jQuery.Animation = jQuery.extend( Animation, {
|
|
tweeners: {
|
|
"*": [ function( prop, value ) {
|
|
var tween = this.createTween( prop, value );
|
|
adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
|
|
return tween;
|
|
} ]
|
|
},
|
|
|
|
tweener: function( props, callback ) {
|
|
if ( jQuery.isFunction( props ) ) {
|
|
callback = props;
|
|
props = [ "*" ];
|
|
} else {
|
|
props = props.match( rnotwhite );
|
|
}
|
|
|
|
var prop,
|
|
index = 0,
|
|
length = props.length;
|
|
|
|
for ( ; index < length ; index++ ) {
|
|
prop = props[ index ];
|
|
Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
|
|
Animation.tweeners[ prop ].unshift( callback );
|
|
}
|
|
},
|
|
|
|
prefilters: [ defaultPrefilter ],
|
|
|
|
prefilter: function( callback, prepend ) {
|
|
if ( prepend ) {
|
|
Animation.prefilters.unshift( callback );
|
|
} else {
|
|
Animation.prefilters.push( callback );
|
|
}
|
|
}
|
|
} );
|
|
|
|
jQuery.speed = function( speed, easing, fn ) {
|
|
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
|
|
complete: fn || !fn && easing ||
|
|
jQuery.isFunction( speed ) && speed,
|
|
duration: speed,
|
|
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
|
|
};
|
|
|
|
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ?
|
|
opt.duration : opt.duration in jQuery.fx.speeds ?
|
|
jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
|
|
|
|
// Normalize opt.queue - true/undefined/null -> "fx"
|
|
if ( opt.queue == null || opt.queue === true ) {
|
|
opt.queue = "fx";
|
|
}
|
|
|
|
// Queueing
|
|
opt.old = opt.complete;
|
|
|
|
opt.complete = function() {
|
|
if ( jQuery.isFunction( opt.old ) ) {
|
|
opt.old.call( this );
|
|
}
|
|
|
|
if ( opt.queue ) {
|
|
jQuery.dequeue( this, opt.queue );
|
|
}
|
|
};
|
|
|
|
return opt;
|
|
};
|
|
|
|
jQuery.fn.extend( {
|
|
fadeTo: function( speed, to, easing, callback ) {
|
|
|
|
// Show any hidden elements after setting opacity to 0
|
|
return this.filter( isHidden ).css( "opacity", 0 ).show()
|
|
|
|
// Animate to the value specified
|
|
.end().animate( { opacity: to }, speed, easing, callback );
|
|
},
|
|
animate: function( prop, speed, easing, callback ) {
|
|
var empty = jQuery.isEmptyObject( prop ),
|
|
optall = jQuery.speed( speed, easing, callback ),
|
|
doAnimation = function() {
|
|
|
|
// Operate on a copy of prop so per-property easing won't be lost
|
|
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
|
|
|
|
// Empty animations, or finishing resolves immediately
|
|
if ( empty || dataPriv.get( this, "finish" ) ) {
|
|
anim.stop( true );
|
|
}
|
|
};
|
|
doAnimation.finish = doAnimation;
|
|
|
|
return empty || optall.queue === false ?
|
|
this.each( doAnimation ) :
|
|
this.queue( optall.queue, doAnimation );
|
|
},
|
|
stop: function( type, clearQueue, gotoEnd ) {
|
|
var stopQueue = function( hooks ) {
|
|
var stop = hooks.stop;
|
|
delete hooks.stop;
|
|
stop( gotoEnd );
|
|
};
|
|
|
|
if ( typeof type !== "string" ) {
|
|
gotoEnd = clearQueue;
|
|
clearQueue = type;
|
|
type = undefined;
|
|
}
|
|
if ( clearQueue && type !== false ) {
|
|
this.queue( type || "fx", [] );
|
|
}
|
|
|
|
return this.each( function() {
|
|
var dequeue = true,
|
|
index = type != null && type + "queueHooks",
|
|
timers = jQuery.timers,
|
|
data = dataPriv.get( this );
|
|
|
|
if ( index ) {
|
|
if ( data[ index ] && data[ index ].stop ) {
|
|
stopQueue( data[ index ] );
|
|
}
|
|
} else {
|
|
for ( index in data ) {
|
|
if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
|
|
stopQueue( data[ index ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( index = timers.length; index--; ) {
|
|
if ( timers[ index ].elem === this &&
|
|
( type == null || timers[ index ].queue === type ) ) {
|
|
|
|
timers[ index ].anim.stop( gotoEnd );
|
|
dequeue = false;
|
|
timers.splice( index, 1 );
|
|
}
|
|
}
|
|
|
|
// Start the next in the queue if the last step wasn't forced.
|
|
// Timers currently will call their complete callbacks, which
|
|
// will dequeue but only if they were gotoEnd.
|
|
if ( dequeue || !gotoEnd ) {
|
|
jQuery.dequeue( this, type );
|
|
}
|
|
} );
|
|
},
|
|
finish: function( type ) {
|
|
if ( type !== false ) {
|
|
type = type || "fx";
|
|
}
|
|
return this.each( function() {
|
|
var index,
|
|
data = dataPriv.get( this ),
|
|
queue = data[ type + "queue" ],
|
|
hooks = data[ type + "queueHooks" ],
|
|
timers = jQuery.timers,
|
|
length = queue ? queue.length : 0;
|
|
|
|
// Enable finishing flag on private data
|
|
data.finish = true;
|
|
|
|
// Empty the queue first
|
|
jQuery.queue( this, type, [] );
|
|
|
|
if ( hooks && hooks.stop ) {
|
|
hooks.stop.call( this, true );
|
|
}
|
|
|
|
// Look for any active animations, and finish them
|
|
for ( index = timers.length; index--; ) {
|
|
if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
|
|
timers[ index ].anim.stop( true );
|
|
timers.splice( index, 1 );
|
|
}
|
|
}
|
|
|
|
// Look for any animations in the old queue and finish them
|
|
for ( index = 0; index < length; index++ ) {
|
|
if ( queue[ index ] && queue[ index ].finish ) {
|
|
queue[ index ].finish.call( this );
|
|
}
|
|
}
|
|
|
|
// Turn off finishing flag
|
|
delete data.finish;
|
|
} );
|
|
}
|
|
} );
|
|
|
|
jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
|
|
var cssFn = jQuery.fn[ name ];
|
|
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
|
return speed == null || typeof speed === "boolean" ?
|
|
cssFn.apply( this, arguments ) :
|
|
this.animate( genFx( name, true ), speed, easing, callback );
|
|
};
|
|
} );
|
|
|
|
// Generate shortcuts for custom animations
|
|
jQuery.each( {
|
|
slideDown: genFx( "show" ),
|
|
slideUp: genFx( "hide" ),
|
|
slideToggle: genFx( "toggle" ),
|
|
fadeIn: { opacity: "show" },
|
|
fadeOut: { opacity: "hide" },
|
|
fadeToggle: { opacity: "toggle" }
|
|
}, function( name, props ) {
|
|
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
|
return this.animate( props, speed, easing, callback );
|
|
};
|
|
} );
|
|
|
|
jQuery.timers = [];
|
|
jQuery.fx.tick = function() {
|
|
var timer,
|
|
i = 0,
|
|
timers = jQuery.timers;
|
|
|
|
fxNow = jQuery.now();
|
|
|
|
for ( ; i < timers.length; i++ ) {
|
|
timer = timers[ i ];
|
|
|
|
// Checks the timer has not already been removed
|
|
if ( !timer() && timers[ i ] === timer ) {
|
|
timers.splice( i--, 1 );
|
|
}
|
|
}
|
|
|
|
if ( !timers.length ) {
|
|
jQuery.fx.stop();
|
|
}
|
|
fxNow = undefined;
|
|
};
|
|
|
|
jQuery.fx.timer = function( timer ) {
|
|
jQuery.timers.push( timer );
|
|
if ( timer() ) {
|
|
jQuery.fx.start();
|
|
} else {
|
|
jQuery.timers.pop();
|
|
}
|
|
};
|
|
|
|
jQuery.fx.interval = 13;
|
|
jQuery.fx.start = function() {
|
|
if ( !timerId ) {
|
|
timerId = window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
|
|
}
|
|
};
|
|
|
|
jQuery.fx.stop = function() {
|
|
window.clearInterval( timerId );
|
|
|
|
timerId = null;
|
|
};
|
|
|
|
jQuery.fx.speeds = {
|
|
slow: 600,
|
|
fast: 200,
|
|
|
|
// Default speed
|
|
_default: 400
|
|
};
|
|
|
|
|
|
// Based off of the plugin by Clint Helfers, with permission.
|
|
// http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
|
|
jQuery.fn.delay = function( time, type ) {
|
|
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
|
|
type = type || "fx";
|
|
|
|
return this.queue( type, function( next, hooks ) {
|
|
var timeout = window.setTimeout( next, time );
|
|
hooks.stop = function() {
|
|
window.clearTimeout( timeout );
|
|
};
|
|
} );
|
|
};
|
|
|
|
|
|
( function() {
|
|
var input = document.createElement( "input" ),
|
|
select = document.createElement( "select" ),
|
|
opt = select.appendChild( document.createElement( "option" ) );
|
|
|
|
input.type = "checkbox";
|
|
|
|
// Support: iOS<=5.1, Android<=4.2+
|
|
// Default value for a checkbox should be "on"
|
|
support.checkOn = input.value !== "";
|
|
|
|
// Support: IE<=11+
|
|
// Must access selectedIndex to make default options select
|
|
support.optSelected = opt.selected;
|
|
|
|
// Support: Android<=2.3
|
|
// Options inside disabled selects are incorrectly marked as disabled
|
|
select.disabled = true;
|
|
support.optDisabled = !opt.disabled;
|
|
|
|
// Support: IE<=11+
|
|
// An input loses its value after becoming a radio
|
|
input = document.createElement( "input" );
|
|
input.value = "t";
|
|
input.type = "radio";
|
|
support.radioValue = input.value === "t";
|
|
} )();
|
|
|
|
|
|
var boolHook,
|
|
attrHandle = jQuery.expr.attrHandle;
|
|
|
|
jQuery.fn.extend( {
|
|
attr: function( name, value ) {
|
|
return access( this, jQuery.attr, name, value, arguments.length > 1 );
|
|
},
|
|
|
|
removeAttr: function( name ) {
|
|
return this.each( function() {
|
|
jQuery.removeAttr( this, name );
|
|
} );
|
|
}
|
|
} );
|
|
|
|
jQuery.extend( {
|
|
attr: function( elem, name, value ) {
|
|
var ret, hooks,
|
|
nType = elem.nodeType;
|
|
|
|
// Don't get/set attributes on text, comment and attribute nodes
|
|
if ( nType === 3 || nType === 8 || nType === 2 ) {
|
|
return;
|
|
}
|
|
|
|
// Fallback to prop when attributes are not supported
|
|
if ( typeof elem.getAttribute === "undefined" ) {
|
|
return jQuery.prop( elem, name, value );
|
|
}
|
|
|
|
// All attributes are lowercase
|
|
// Grab necessary hook if one is defined
|
|
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
|
|
name = name.toLowerCase();
|
|
hooks = jQuery.attrHooks[ name ] ||
|
|
( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
|
|
}
|
|
|
|
if ( value !== undefined ) {
|
|
if ( value === null ) {
|
|
jQuery.removeAttr( elem, name );
|
|
return;
|
|
}
|
|
|
|
if ( hooks && "set" in hooks &&
|
|
( ret = hooks.set( elem, value, name ) ) !== undefined ) {
|
|
return ret;
|
|
}
|
|
|
|
elem.setAttribute( name, value + "" );
|
|
return value;
|
|
}
|
|
|
|
if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
|
|
return ret;
|
|
}
|
|
|
|
ret = jQuery.find.attr( elem, name );
|
|
|
|
// Non-existent attributes return null, we normalize to undefined
|
|
return ret == null ? undefined : ret;
|
|
},
|
|
|
|
attrHooks: {
|
|
type: {
|
|
set: function( elem, value ) {
|
|
if ( !support.radioValue && value === "radio" &&
|
|
jQuery.nodeName( elem, "input" ) ) {
|
|
var val = elem.value;
|
|
elem.setAttribute( "type", value );
|
|
if ( val ) {
|
|
elem.value = val;
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
removeAttr: function( elem, value ) {
|
|
var name, propName,
|
|
i = 0,
|
|
attrNames = value && value.match( rnotwhite );
|
|
|
|
if ( attrNames && elem.nodeType === 1 ) {
|
|
while ( ( name = attrNames[ i++ ] ) ) {
|
|
propName = jQuery.propFix[ name ] || name;
|
|
|
|
// Boolean attributes get special treatment (#10870)
|
|
if ( jQuery.expr.match.bool.test( name ) ) {
|
|
|
|
// Set corresponding property to false
|
|
elem[ propName ] = false;
|
|
}
|
|
|
|
elem.removeAttribute( name );
|
|
}
|
|
}
|
|
}
|
|
} );
|
|
|
|
// Hooks for boolean attributes
|
|
boolHook = {
|
|
set: function( elem, value, name ) {
|
|
if ( value === false ) {
|
|
|
|
// Remove boolean attributes when set to false
|
|
jQuery.removeAttr( elem, name );
|
|
} else {
|
|
elem.setAttribute( name, name );
|
|
}
|
|
return name;
|
|
}
|
|
};
|
|
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
|
|
var getter = attrHandle[ name ] || jQuery.find.attr;
|
|
|
|
attrHandle[ name ] = function( elem, name, isXML ) {
|
|
var ret, handle;
|
|
if ( !isXML ) {
|
|
|
|
// Avoid an infinite loop by temporarily removing this function from the getter
|
|
handle = attrHandle[ name ];
|
|
attrHandle[ name ] = ret;
|
|
ret = getter( elem, name, isXML ) != null ?
|
|
name.toLowerCase() :
|
|
null;
|
|
attrHandle[ name ] = handle;
|
|
}
|
|
return ret;
|
|
};
|
|
} );
|
|
|
|
|
|
|
|
|
|
var rfocusable = /^(?:input|select|textarea|button)$/i,
|
|
rclickable = /^(?:a|area)$/i;
|
|
|
|
jQuery.fn.extend( {
|
|
prop: function( name, value ) {
|
|
return access( this, jQuery.prop, name, value, arguments.length > 1 );
|
|
},
|
|
|
|
removeProp: function( name ) {
|
|
return this.each( function() {
|
|
delete this[ jQuery.propFix[ name ] || name ];
|
|
} );
|
|
}
|
|
} );
|
|
|
|
jQuery.extend( {
|
|
prop: function( elem, name, value ) {
|
|
var ret, hooks,
|
|
nType = elem.nodeType;
|
|
|
|
// Don't get/set properties on text, comment and attribute nodes
|
|
if ( nType === 3 || nType === 8 || nType === 2 ) {
|
|
return;
|
|
}
|
|
|
|
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
|
|
|
|
// Fix name and attach hooks
|
|
name = jQuery.propFix[ name ] || name;
|
|
hooks = jQuery.propHooks[ name ];
|
|
}
|
|
|
|
if ( value !== undefined ) {
|
|
if ( hooks && "set" in hooks &&
|
|
( ret = hooks.set( elem, value, name ) ) !== undefined ) {
|
|
return ret;
|
|
}
|
|
|
|
return ( elem[ name ] = value );
|
|
}
|
|
|
|
if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
|
|
return ret;
|
|
}
|
|
|
|
return elem[ name ];
|
|
},
|
|
|
|
propHooks: {
|
|
tabIndex: {
|
|
get: function( elem ) {
|
|
|
|
// elem.tabIndex doesn't always return the
|
|
// correct value when it hasn't been explicitly set
|
|
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
|
|
// Use proper attribute retrieval(#12072)
|
|
var tabindex = jQuery.find.attr( elem, "tabindex" );
|
|
|
|
return tabindex ?
|
|
parseInt( tabindex, 10 ) :
|
|
rfocusable.test( elem.nodeName ) ||
|
|
rclickable.test( elem.nodeName ) && elem.href ?
|
|
0 :
|
|
-1;
|
|
}
|
|
}
|
|
},
|
|
|
|
propFix: {
|
|
"for": "htmlFor",
|
|
"class": "className"
|
|
}
|
|
} );
|
|
|
|
// Support: IE <=11 only
|
|
// Accessing the selectedIndex property
|
|
// forces the browser to respect setting selected
|
|
// on the option
|
|
// The getter ensures a default option is selected
|
|
// when in an optgroup
|
|
if ( !support.optSelected ) {
|
|
jQuery.propHooks.selected = {
|
|
get: function( elem ) {
|
|
var parent = elem.parentNode;
|
|
if ( parent && parent.parentNode ) {
|
|
parent.parentNode.selectedIndex;
|
|
}
|
|
return null;
|
|
},
|
|
set: function( elem ) {
|
|
var parent = elem.parentNode;
|
|
if ( parent ) {
|
|
parent.selectedIndex;
|
|
|
|
if ( parent.parentNode ) {
|
|
parent.parentNode.selectedIndex;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
jQuery.each( [
|
|
"tabIndex",
|
|
"readOnly",
|
|
"maxLength",
|
|
"cellSpacing",
|
|
"cellPadding",
|
|
"rowSpan",
|
|
"colSpan",
|
|
"useMap",
|
|
"frameBorder",
|
|
"contentEditable"
|
|
], function() {
|
|
jQuery.propFix[ this.toLowerCase() ] = this;
|
|
} );
|
|
|
|
|
|
|
|
|
|
var rclass = /[\t\r\n\f]/g;
|
|
|
|
function getClass( elem ) {
|
|
return elem.getAttribute && elem.getAttribute( "class" ) || "";
|
|
}
|
|
|
|
jQuery.fn.extend( {
|
|
addClass: function( value ) {
|
|
var classes, elem, cur, curValue, clazz, j, finalValue,
|
|
i = 0;
|
|
|
|
if ( jQuery.isFunction( value ) ) {
|
|
return this.each( function( j ) {
|
|
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
|
|
} );
|
|
}
|
|
|
|
if ( typeof value === "string" && value ) {
|
|
classes = value.match( rnotwhite ) || [];
|
|
|
|
while ( ( elem = this[ i++ ] ) ) {
|
|
curValue = getClass( elem );
|
|
cur = elem.nodeType === 1 &&
|
|
( " " + curValue + " " ).replace( rclass, " " );
|
|
|
|
if ( cur ) {
|
|
j = 0;
|
|
while ( ( clazz = classes[ j++ ] ) ) {
|
|
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
|
|
cur += clazz + " ";
|
|
}
|
|
}
|
|
|
|
// Only assign if different to avoid unneeded rendering.
|
|
finalValue = jQuery.trim( cur );
|
|
if ( curValue !== finalValue ) {
|
|
elem.setAttribute( "class", finalValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
removeClass: function( value ) {
|
|
var classes, elem, cur, curValue, clazz, j, finalValue,
|
|
i = 0;
|
|
|
|
if ( jQuery.isFunction( value ) ) {
|
|
return this.each( function( j ) {
|
|
jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
|
|
} );
|
|
}
|
|
|
|
if ( !arguments.length ) {
|
|
return this.attr( "class", "" );
|
|
}
|
|
|
|
if ( typeof value === "string" && value ) {
|
|
classes = value.match( rnotwhite ) || [];
|
|
|
|
while ( ( elem = this[ i++ ] ) ) {
|
|
curValue = getClass( elem );
|
|
|
|
// This expression is here for better compressibility (see addClass)
|
|
cur = elem.nodeType === 1 &&
|
|
( " " + curValue + " " ).replace( rclass, " " );
|
|
|
|
if ( cur ) {
|
|
j = 0;
|
|
while ( ( clazz = classes[ j++ ] ) ) {
|
|
|
|
// Remove *all* instances
|
|
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
|
|
cur = cur.replace( " " + clazz + " ", " " );
|
|
}
|
|
}
|
|
|
|
// Only assign if different to avoid unneeded rendering.
|
|
finalValue = jQuery.trim( cur );
|
|
if ( curValue !== finalValue ) {
|
|
elem.setAttribute( "class", finalValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
toggleClass: function( value, stateVal ) {
|
|
var type = typeof value;
|
|
|
|
if ( typeof stateVal === "boolean" && type === "string" ) {
|
|
return stateVal ? this.addClass( value ) : this.removeClass( value );
|
|
}
|
|
|
|
if ( jQuery.isFunction( value ) ) {
|
|
return this.each( function( i ) {
|
|
jQuery( this ).toggleClass(
|
|
value.call( this, i, getClass( this ), stateVal ),
|
|
stateVal
|
|
);
|
|
} );
|
|
}
|
|
|
|
return this.each( function() {
|
|
var className, i, self, classNames;
|
|
|
|
if ( type === "string" ) {
|
|
|
|
// Toggle individual class names
|
|
i = 0;
|
|
self = jQuery( this );
|
|
classNames = value.match( rnotwhite ) || [];
|
|
|
|
while ( ( className = classNames[ i++ ] ) ) {
|
|
|
|
// Check each className given, space separated list
|
|
if ( self.hasClass( className ) ) {
|
|
self.removeClass( className );
|
|
} else {
|
|
self.addClass( className );
|
|
}
|
|
}
|
|
|
|
// Toggle whole class name
|
|
} else if ( value === undefined || type === "boolean" ) {
|
|
className = getClass( this );
|
|
if ( className ) {
|
|
|
|
// Store className if set
|
|
dataPriv.set( this, "__className__", className );
|
|
}
|
|
|
|
// If the element has a class name or if we're passed `false`,
|
|
// then remove the whole classname (if there was one, the above saved it).
|
|
// Otherwise bring back whatever was previously saved (if anything),
|
|
// falling back to the empty string if nothing was stored.
|
|
if ( this.setAttribute ) {
|
|
this.setAttribute( "class",
|
|
className || value === false ?
|
|
"" :
|
|
dataPriv.get( this, "__className__" ) || ""
|
|
);
|
|
}
|
|
}
|
|
} );
|
|
},
|
|
|
|
hasClass: function( selector ) {
|
|
var className, elem,
|
|
i = 0;
|
|
|
|
className = " " + selector + " ";
|
|
while ( ( elem = this[ i++ ] ) ) {
|
|
if ( elem.nodeType === 1 &&
|
|
( " " + getClass( elem ) + " " ).replace( rclass, " " )
|
|
.indexOf( className ) > -1
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} );
|
|
|
|
|
|
|
|
|
|
var rreturn = /\r/g,
|
|
rspaces = /[\x20\t\r\n\f]+/g;
|
|
|
|
jQuery.fn.extend( {
|
|
val: function( value ) {
|
|
var hooks, ret, isFunction,
|
|
elem = this[ 0 ];
|
|
|
|
if ( !arguments.length ) {
|
|
if ( elem ) {
|
|
hooks = jQuery.valHooks[ elem.type ] ||
|
|
jQuery.valHooks[ elem.nodeName.toLowerCase() ];
|
|
|
|
if ( hooks &&
|
|
"get" in hooks &&
|
|
( ret = hooks.get( elem, "value" ) ) !== undefined
|
|
) {
|
|
return ret;
|
|
}
|
|
|
|
ret = elem.value;
|
|
|
|
return typeof ret === "string" ?
|
|
|
|
// Handle most common string cases
|
|
ret.replace( rreturn, "" ) :
|
|
|
|
// Handle cases where value is null/undef or number
|
|
ret == null ? "" : ret;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
isFunction = jQuery.isFunction( value );
|
|
|
|
return this.each( function( i ) {
|
|
var val;
|
|
|
|
if ( this.nodeType !== 1 ) {
|
|
return;
|
|
}
|
|
|
|
if ( isFunction ) {
|
|
val = value.call( this, i, jQuery( this ).val() );
|
|
} else {
|
|
val = value;
|
|
}
|
|
|
|
// Treat null/undefined as ""; convert numbers to string
|
|
if ( val == null ) {
|
|
val = "";
|
|
|
|
} else if ( typeof val === "number" ) {
|
|
val += "";
|
|
|
|
} else if ( jQuery.isArray( val ) ) {
|
|
val = jQuery.map( val, function( value ) {
|
|
return value == null ? "" : value + "";
|
|
} );
|
|
}
|
|
|
|
hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
|
|
|
|
// If set returns undefined, fall back to normal setting
|
|
if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
|
|
this.value = val;
|
|
}
|
|
} );
|
|
}
|
|
} );
|
|
|
|
jQuery.extend( {
|
|
valHooks: {
|
|
option: {
|
|
get: function( elem ) {
|
|
|
|
var val = jQuery.find.attr( elem, "value" );
|
|
return val != null ?
|
|
val :
|
|
|
|
// Support: IE10-11+
|
|
// option.text throws exceptions (#14686, #14858)
|
|
// Strip and collapse whitespace
|
|
// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
|
|
jQuery.trim( jQuery.text( elem ) ).replace( rspaces, " " );
|
|
}
|
|
},
|
|
select: {
|
|
get: function( elem ) {
|
|
var value, option,
|
|
options = elem.options,
|
|
index = elem.selectedIndex,
|
|
one = elem.type === "select-one" || index < 0,
|
|
values = one ? null : [],
|
|
max = one ? index + 1 : options.length,
|
|
i = index < 0 ?
|
|
max :
|
|
one ? index : 0;
|
|
|
|
// Loop through all the selected options
|
|
for ( ; i < max; i++ ) {
|
|
option = options[ i ];
|
|
|
|
// IE8-9 doesn't update selected after form reset (#2551)
|
|
if ( ( option.selected || i === index ) &&
|
|
|
|
// Don't return options that are disabled or in a disabled optgroup
|
|
( support.optDisabled ?
|
|
!option.disabled : option.getAttribute( "disabled" ) === null ) &&
|
|
( !option.parentNode.disabled ||
|
|
!jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
|
|
|
|
// Get the specific value for the option
|
|
value = jQuery( option ).val();
|
|
|
|
// We don't need an array for one selects
|
|
if ( one ) {
|
|
return value;
|
|
}
|
|
|
|
// Multi-Selects return an array
|
|
values.push( value );
|
|
}
|
|
}
|
|
|
|
return values;
|
|
},
|
|
|
|
set: function( elem, value ) {
|
|
var optionSet, option,
|
|
options = elem.options,
|
|
values = jQuery.makeArray( value ),
|
|
i = options.length;
|
|
|
|
while ( i-- ) {
|
|
option = options[ i ];
|
|
if ( option.selected =
|
|
jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
|
|
) {
|
|
optionSet = true;
|
|
}
|
|
}
|
|
|
|
// Force browsers to behave consistently when non-matching value is set
|
|
if ( !optionSet ) {
|
|
elem.selectedIndex = -1;
|
|
}
|
|
return values;
|
|
}
|
|
}
|
|
}
|
|
} );
|
|
|
|
// Radios and checkboxes getter/setter
|
|
jQuery.each( [ "radio", "checkbox" ], function() {
|
|
jQuery.valHooks[ this ] = {
|
|
set: function( elem, value ) {
|
|
if ( jQuery.isArray( value ) ) {
|
|
return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
|
|
}
|
|
}
|
|
};
|
|
if ( !support.checkOn ) {
|
|
jQuery.valHooks[ this ].get = function( elem ) {
|
|
return elem.getAttribute( "value" ) === null ? "on" : elem.value;
|
|
};
|
|
}
|
|
} );
|
|
|
|
|
|
|
|
|
|
// Return jQuery for attributes-only inclusion
|
|
|
|
|
|
var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;
|
|
|
|
jQuery.extend( jQuery.event, {
|
|
|
|
trigger: function( event, data, elem, onlyHandlers ) {
|
|
|
|
var i, cur, tmp, bubbleType, ontype, handle, special,
|
|
eventPath = [ elem || document ],
|
|
type = hasOwn.call( event, "type" ) ? event.type : event,
|
|
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
|
|
|
|
cur = tmp = elem = elem || document;
|
|
|
|
// Don't do events on text and comment nodes
|
|
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
|
|
return;
|
|
}
|
|
|
|
// focus/blur morphs to focusin/out; ensure we're not firing them right now
|
|
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( type.indexOf( "." ) > -1 ) {
|
|
|
|
// Namespaced trigger; create a regexp to match event type in handle()
|
|
namespaces = type.split( "." );
|
|
type = namespaces.shift();
|
|
namespaces.sort();
|
|
}
|
|
ontype = type.indexOf( ":" ) < 0 && "on" + type;
|
|
|
|
// Caller can pass in a jQuery.Event object, Object, or just an event type string
|
|
event = event[ jQuery.expando ] ?
|
|
event :
|
|
new jQuery.Event( type, typeof event === "object" && event );
|
|
|
|
// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
|
|
event.isTrigger = onlyHandlers ? 2 : 3;
|
|
event.namespace = namespaces.join( "." );
|
|
event.rnamespace = event.namespace ?
|
|
new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
|
|
null;
|
|
|
|
// Clean up the event in case it is being reused
|
|
event.result = undefined;
|
|
if ( !event.target ) {
|
|
event.target = elem;
|
|
}
|
|
|
|
// Clone any incoming data and prepend the event, creating the handler arg list
|
|
data = data == null ?
|
|
[ event ] :
|
|
jQuery.makeArray( data, [ event ] );
|
|
|
|
// Allow special events to draw outside the lines
|
|
special = jQuery.event.special[ type ] || {};
|
|
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
|
|
return;
|
|
}
|
|
|
|
// Determine event propagation path in advance, per W3C events spec (#9951)
|
|
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
|
|
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
|
|
|
|
bubbleType = special.delegateType || type;
|
|
if ( !rfocusMorph.test( bubbleType + type ) ) {
|
|
cur = cur.parentNode;
|
|
}
|
|
for ( ; cur; cur = cur.parentNode ) {
|
|
eventPath.push( cur );
|
|
tmp = cur;
|
|
}
|
|
|
|
// Only add window if we got to document (e.g., not plain obj or detached DOM)
|
|
if ( tmp === ( elem.ownerDocument || document ) ) {
|
|
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
|
|
}
|
|
}
|
|
|
|
// Fire handlers on the event path
|
|
i = 0;
|
|
while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
|
|
|
|
event.type = i > 1 ?
|
|
bubbleType :
|
|
special.bindType || type;
|
|
|
|
// jQuery handler
|
|
handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
|
|
dataPriv.get( cur, "handle" );
|
|
if ( handle ) {
|
|
handle.apply( cur, data );
|
|
}
|
|
|
|
// Native handler
|
|
handle = ontype && cur[ ontype ];
|
|
if ( handle && handle.apply && acceptData( cur ) ) {
|
|
event.result = handle.apply( cur, data );
|
|
if ( event.result === false ) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
event.type = type;
|
|
|
|
// If nobody prevented the default action, do it now
|
|
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
|
|
|
|
if ( ( !special._default ||
|
|
special._default.apply( eventPath.pop(), data ) === false ) &&
|
|
acceptData( elem ) ) {
|
|
|
|
// Call a native DOM method on the target with the same name name as the event.
|
|
// Don't do default actions on window, that's where global variables be (#6170)
|
|
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
|
|
|
|
// Don't re-trigger an onFOO event when we call its FOO() method
|
|
tmp = elem[ ontype ];
|
|
|
|
if ( tmp ) {
|
|
elem[ ontype ] = null;
|
|
}
|
|
|
|
// Prevent re-triggering of the same event, since we already bubbled it above
|
|
jQuery.event.triggered = type;
|
|
elem[ type ]();
|
|
jQuery.event.triggered = undefined;
|
|
|
|
if ( tmp ) {
|
|
elem[ ontype ] = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return event.result;
|
|
},
|
|
|
|
// Piggyback on a donor event to simulate a different one
|
|
// Used only for `focus(in | out)` events
|
|
simulate: function( type, elem, event ) {
|
|
var e = jQuery.extend(
|
|
new jQuery.Event(),
|
|
event,
|
|
{
|
|
type: type,
|
|
isSimulated: true
|
|
}
|
|
);
|
|
|
|
jQuery.event.trigger( e, null, elem );
|
|
}
|
|
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
|
|
trigger: function( type, data ) {
|
|
return this.each( function() {
|
|
jQuery.event.trigger( type, data, this );
|
|
} );
|
|
},
|
|
triggerHandler: function( type, data ) {
|
|
var elem = this[ 0 ];
|
|
if ( elem ) {
|
|
return jQuery.event.trigger( type, data, elem, true );
|
|
}
|
|
}
|
|
} );
|
|
|
|
|
|
jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick " +
|
|
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
|
|
"change select submit keydown keypress keyup error contextmenu" ).split( " " ),
|
|
function( i, name ) {
|
|
|
|
// Handle event binding
|
|
jQuery.fn[ name ] = function( data, fn ) {
|
|
return arguments.length > 0 ?
|
|
this.on( name, null, data, fn ) :
|
|
this.trigger( name );
|
|
};
|
|
} );
|
|
|
|
jQuery.fn.extend( {
|
|
hover: function( fnOver, fnOut ) {
|
|
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
|
|
}
|
|
} );
|
|
|
|
|
|
|
|
|
|
support.focusin = "onfocusin" in window;
|
|
|
|
|
|
// Support: Firefox
|
|
// Firefox doesn't have focus(in | out) events
|
|
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
|
|
//
|
|
// Support: Chrome, Safari
|
|
// focus(in | out) events fire after focus & blur events,
|
|
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
|
|
// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
|
|
if ( !support.focusin ) {
|
|
jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
|
|
|
|
// Attach a single capturing handler on the document while someone wants focusin/focusout
|
|
var handler = function( event ) {
|
|
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
|
|
};
|
|
|
|
jQuery.event.special[ fix ] = {
|
|
setup: function() {
|
|
var doc = this.ownerDocument || this,
|
|
attaches = dataPriv.access( doc, fix );
|
|
|
|
if ( !attaches ) {
|
|
doc.addEventListener( orig, handler, true );
|
|
}
|
|
dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
|
|
},
|
|
teardown: function() {
|
|
var doc = this.ownerDocument || this,
|
|
attaches = dataPriv.access( doc, fix ) - 1;
|
|
|
|
if ( !attaches ) {
|
|
doc.removeEventListener( orig, handler, true );
|
|
dataPriv.remove( doc, fix );
|
|
|
|
} else {
|
|
dataPriv.access( doc, fix, attaches );
|
|
}
|
|
}
|
|
};
|
|
} );
|
|
}
|
|
var location = window.location;
|
|
|
|
var nonce = jQuery.now();
|
|
|
|
var rquery = ( /\?/ );
|
|
|
|
|
|
|
|
// Support: Android 2.3
|
|
// Workaround failure to string-cast null input
|
|
jQuery.parseJSON = function( data ) {
|
|
return JSON.parse( data + "" );
|
|
};
|
|
|
|
|
|
// Cross-browser xml parsing
|
|
jQuery.parseXML = function( data ) {
|
|
var xml;
|
|
if ( !data || typeof data !== "string" ) {
|
|
return null;
|
|
}
|
|
|
|
// Support: IE9
|
|
try {
|
|
xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
|
|
} catch ( e ) {
|
|
xml = undefined;
|
|
}
|
|
|
|
if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
|
|
jQuery.error( "Invalid XML: " + data );
|
|
}
|
|
return xml;
|
|
};
|
|
|
|
|
|
var
|
|
rhash = /#.*$/,
|
|
rts = /([?&])_=[^&]*/,
|
|
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
|
|
|
|
// #7653, #8125, #8152: local protocol detection
|
|
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
|
|
rnoContent = /^(?:GET|HEAD)$/,
|
|
rprotocol = /^\/\//,
|
|
|
|
/* Prefilters
|
|
* 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
|
|
* 2) These are called:
|
|
* - BEFORE asking for a transport
|
|
* - AFTER param serialization (s.data is a string if s.processData is true)
|
|
* 3) key is the dataType
|
|
* 4) the catchall symbol "*" can be used
|
|
* 5) execution will start with transport dataType and THEN continue down to "*" if needed
|
|
*/
|
|
prefilters = {},
|
|
|
|
/* Transports bindings
|
|
* 1) key is the dataType
|
|
* 2) the catchall symbol "*" can be used
|
|
* 3) selection will start with transport dataType and THEN go to "*" if needed
|
|
*/
|
|
transports = {},
|
|
|
|
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
|
|
allTypes = "*/".concat( "*" ),
|
|
|
|
// Anchor tag for parsing the document origin
|
|
originAnchor = document.createElement( "a" );
|
|
originAnchor.href = location.href;
|
|
|
|
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
|
|
function addToPrefiltersOrTransports( structure ) {
|
|
|
|
// dataTypeExpression is optional and defaults to "*"
|
|
return function( dataTypeExpression, func ) {
|
|
|
|
if ( typeof dataTypeExpression !== "string" ) {
|
|
func = dataTypeExpression;
|
|
dataTypeExpression = "*";
|
|
}
|
|
|
|
var dataType,
|
|
i = 0,
|
|
dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
|
|
|
|
if ( jQuery.isFunction( func ) ) {
|
|
|
|
// For each dataType in the dataTypeExpression
|
|
while ( ( dataType = dataTypes[ i++ ] ) ) {
|
|
|
|
// Prepend if requested
|
|
if ( dataType[ 0 ] === "+" ) {
|
|
dataType = dataType.slice( 1 ) || "*";
|
|
( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
|
|
|
|
// Otherwise append
|
|
} else {
|
|
( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Base inspection function for prefilters and transports
|
|
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
|
|
|
|
var inspected = {},
|
|
seekingTransport = ( structure === transports );
|
|
|
|
function inspect( dataType ) {
|
|
var selected;
|
|
inspected[ dataType ] = true;
|
|
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
|
|
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
|
|
if ( typeof dataTypeOrTransport === "string" &&
|
|
!seekingTransport && !inspected[ dataTypeOrTransport ] ) {
|
|
|
|
options.dataTypes.unshift( dataTypeOrTransport );
|
|
inspect( dataTypeOrTransport );
|
|
return false;
|
|
} else if ( seekingTransport ) {
|
|
return !( selected = dataTypeOrTransport );
|
|
}
|
|
} );
|
|
return selected;
|
|
}
|
|
|
|
return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
|
|
}
|
|
|
|
// A special extend for ajax options
|
|
// that takes "flat" options (not to be deep extended)
|
|
// Fixes #9887
|
|
function ajaxExtend( target, src ) {
|
|
var key, deep,
|
|
flatOptions = jQuery.ajaxSettings.flatOptions || {};
|
|
|
|
for ( key in src ) {
|
|
if ( src[ key ] !== undefined ) {
|
|
( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
|
|
}
|
|
}
|
|
if ( deep ) {
|
|
jQuery.extend( true, target, deep );
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
/* Handles responses to an ajax request:
|
|
* - finds the right dataType (mediates between content-type and expected dataType)
|
|
* - returns the corresponding response
|
|
*/
|
|
function ajaxHandleResponses( s, jqXHR, responses ) {
|
|
|
|
var ct, type, finalDataType, firstDataType,
|
|
contents = s.contents,
|
|
dataTypes = s.dataTypes;
|
|
|
|
// Remove auto dataType and get content-type in the process
|
|
while ( dataTypes[ 0 ] === "*" ) {
|
|
dataTypes.shift();
|
|
if ( ct === undefined ) {
|
|
ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
|
|
}
|
|
}
|
|
|
|
// Check if we're dealing with a known content-type
|
|
if ( ct ) {
|
|
for ( type in contents ) {
|
|
if ( contents[ type ] && contents[ type ].test( ct ) ) {
|
|
dataTypes.unshift( type );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to see if we have a response for the expected dataType
|
|
if ( dataTypes[ 0 ] in responses ) {
|
|
finalDataType = dataTypes[ 0 ];
|
|
} else {
|
|
|
|
// Try convertible dataTypes
|
|
for ( type in responses ) {
|
|
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
|
|
finalDataType = type;
|
|
break;
|
|
}
|
|
if ( !firstDataType ) {
|
|
firstDataType = type;
|
|
}
|
|
}
|
|
|
|
// Or just use first one
|
|
finalDataType = finalDataType || firstDataType;
|
|
}
|
|
|
|
// If we found a dataType
|
|
// We add the dataType to the list if needed
|
|
// and return the corresponding response
|
|
if ( finalDataType ) {
|
|
if ( finalDataType !== dataTypes[ 0 ] ) {
|
|
dataTypes.unshift( finalDataType );
|
|
}
|
|
return responses[ finalDataType ];
|
|
}
|
|
}
|
|
|
|
/* Chain conversions given the request and the original response
|
|
* Also sets the responseXXX fields on the jqXHR instance
|
|
*/
|
|
function ajaxConvert( s, response, jqXHR, isSuccess ) {
|
|
var conv2, current, conv, tmp, prev,
|
|
converters = {},
|
|
|
|
// Work with a copy of dataTypes in case we need to modify it for conversion
|
|
dataTypes = s.dataTypes.slice();
|
|
|
|
// Create converters map with lowercased keys
|
|
if ( dataTypes[ 1 ] ) {
|
|
for ( conv in s.converters ) {
|
|
converters[ conv.toLowerCase() ] = s.converters[ conv ];
|
|
}
|
|
}
|
|
|
|
current = dataTypes.shift();
|
|
|
|
// Convert to each sequential dataType
|
|
while ( current ) {
|
|
|
|
if ( s.responseFields[ current ] ) {
|
|
jqXHR[ s.responseFields[ current ] ] = response;
|
|
}
|
|
|
|
// Apply the dataFilter if provided
|
|
if ( !prev && isSuccess && s.dataFilter ) {
|
|
response = s.dataFilter( response, s.dataType );
|
|
}
|
|
|
|
prev = current;
|
|
current = dataTypes.shift();
|
|
|
|
if ( current ) {
|
|
|
|
// There's only work to do if current dataType is non-auto
|
|
if ( current === "*" ) {
|
|
|
|
current = prev;
|
|
|
|
// Convert response if prev dataType is non-auto and differs from current
|
|
} else if ( prev !== "*" && prev !== current ) {
|
|
|
|
// Seek a direct converter
|
|
conv = converters[ prev + " " + current ] || converters[ "* " + current ];
|
|
|
|
// If none found, seek a pair
|
|
if ( !conv ) {
|
|
for ( conv2 in converters ) {
|
|
|
|
// If conv2 outputs current
|
|
tmp = conv2.split( " " );
|
|
if ( tmp[ 1 ] === current ) {
|
|
|
|
// If prev can be converted to accepted input
|
|
conv = converters[ prev + " " + tmp[ 0 ] ] ||
|
|
converters[ "* " + tmp[ 0 ] ];
|
|
if ( conv ) {
|
|
|
|
// Condense equivalence converters
|
|
if ( conv === true ) {
|
|
conv = converters[ conv2 ];
|
|
|
|
// Otherwise, insert the intermediate dataType
|
|
} else if ( converters[ conv2 ] !== true ) {
|
|
current = tmp[ 0 ];
|
|
dataTypes.unshift( tmp[ 1 ] );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply converter (if not an equivalence)
|
|
if ( conv !== true ) {
|
|
|
|
// Unless errors are allowed to bubble, catch and return them
|
|
if ( conv && s.throws ) {
|
|
response = conv( response );
|
|
} else {
|
|
try {
|
|
response = conv( response );
|
|
} catch ( e ) {
|
|
return {
|
|
state: "parsererror",
|
|
error: conv ? e : "No conversion from " + prev + " to " + current
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { state: "success", data: response };
|
|
}
|
|
|
|
jQuery.extend( {
|
|
|
|
// Counter for holding the number of active queries
|
|
active: 0,
|
|
|
|
// Last-Modified header cache for next request
|
|
lastModified: {},
|
|
etag: {},
|
|
|
|
ajaxSettings: {
|
|
url: location.href,
|
|
type: "GET",
|
|
isLocal: rlocalProtocol.test( location.protocol ),
|
|
global: true,
|
|
processData: true,
|
|
async: true,
|
|
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
|
|
/*
|
|
timeout: 0,
|
|
data: null,
|
|
dataType: null,
|
|
username: null,
|
|
password: null,
|
|
cache: null,
|
|
throws: false,
|
|
traditional: false,
|
|
headers: {},
|
|
*/
|
|
|
|
accepts: {
|
|
"*": allTypes,
|
|
text: "text/plain",
|
|
html: "text/html",
|
|
xml: "application/xml, text/xml",
|
|
json: "application/json, text/javascript"
|
|
},
|
|
|
|
contents: {
|
|
xml: /\bxml\b/,
|
|
html: /\bhtml/,
|
|
json: /\bjson\b/
|
|
},
|
|
|
|
responseFields: {
|
|
xml: "responseXML",
|
|
text: "responseText",
|
|
json: "responseJSON"
|
|
},
|
|
|
|
// Data converters
|
|
// Keys separate source (or catchall "*") and destination types with a single space
|
|
converters: {
|
|
|
|
// Convert anything to text
|
|
"* text": String,
|
|
|
|
// Text to html (true = no transformation)
|
|
"text html": true,
|
|
|
|
// Evaluate text as a json expression
|
|
"text json": jQuery.parseJSON,
|
|
|
|
// Parse text as xml
|
|
"text xml": jQuery.parseXML
|
|
},
|
|
|
|
// For options that shouldn't be deep extended:
|
|
// you can add your own custom options here if
|
|
// and when you create one that shouldn't be
|
|
// deep extended (see ajaxExtend)
|
|
flatOptions: {
|
|
url: true,
|
|
context: true
|
|
}
|
|
},
|
|
|
|
// Creates a full fledged settings object into target
|
|
// with both ajaxSettings and settings fields.
|
|
// If target is omitted, writes into ajaxSettings.
|
|
ajaxSetup: function( target, settings ) {
|
|
return settings ?
|
|
|
|
// Building a settings object
|
|
ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
|
|
|
|
// Extending ajaxSettings
|
|
ajaxExtend( jQuery.ajaxSettings, target );
|
|
},
|
|
|
|
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
|
|
ajaxTransport: addToPrefiltersOrTransports( transports ),
|
|
|
|
// Main method
|
|
ajax: function( url, options ) {
|
|
|
|
// If url is an object, simulate pre-1.5 signature
|
|
if ( typeof url === "object" ) {
|
|
options = url;
|
|
url = undefined;
|
|
}
|
|
|
|
// Force options to be an object
|
|
options = options || {};
|
|
|
|
var transport,
|
|
|
|
// URL without anti-cache param
|
|
cacheURL,
|
|
|
|
// Response headers
|
|
responseHeadersString,
|
|
responseHeaders,
|
|
|
|
// timeout handle
|
|
timeoutTimer,
|
|
|
|
// Url cleanup var
|
|
urlAnchor,
|
|
|
|
// To know if global events are to be dispatched
|
|
fireGlobals,
|
|
|
|
// Loop variable
|
|
i,
|
|
|
|
// Create the final options object
|
|
s = jQuery.ajaxSetup( {}, options ),
|
|
|
|
// Callbacks context
|
|
callbackContext = s.context || s,
|
|
|
|
// Context for global events is callbackContext if it is a DOM node or jQuery collection
|
|
globalEventContext = s.context &&
|
|
( callbackContext.nodeType || callbackContext.jquery ) ?
|
|
jQuery( callbackContext ) :
|
|
jQuery.event,
|
|
|
|
// Deferreds
|
|
deferred = jQuery.Deferred(),
|
|
completeDeferred = jQuery.Callbacks( "once memory" ),
|
|
|
|
// Status-dependent callbacks
|
|
statusCode = s.statusCode || {},
|
|
|
|
// Headers (they are sent all at once)
|
|
requestHeaders = {},
|
|
requestHeadersNames = {},
|
|
|
|
// The jqXHR state
|
|
state = 0,
|
|
|
|
// Default abort message
|
|
strAbort = "canceled",
|
|
|
|
// Fake xhr
|
|
jqXHR = {
|
|
readyState: 0,
|
|
|
|
// Builds headers hashtable if needed
|
|
getResponseHeader: function( key ) {
|
|
var match;
|
|
if ( state === 2 ) {
|
|
if ( !responseHeaders ) {
|
|
responseHeaders = {};
|
|
while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
|
|
responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
|
|
}
|
|
}
|
|
match = responseHeaders[ key.toLowerCase() ];
|
|
}
|
|
return match == null ? null : match;
|
|
},
|
|
|
|
// Raw string
|
|
getAllResponseHeaders: function() {
|
|
return state === 2 ? responseHeadersString : null;
|
|
},
|
|
|
|
// Caches the header
|
|
setRequestHeader: function( name, value ) {
|
|
var lname = name.toLowerCase();
|
|
if ( !state ) {
|
|
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
|
|
requestHeaders[ name ] = value;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Overrides response content-type header
|
|
overrideMimeType: function( type ) {
|
|
if ( !state ) {
|
|
s.mimeType = type;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Status-dependent callbacks
|
|
statusCode: function( map ) {
|
|
var code;
|
|
if ( map ) {
|
|
if ( state < 2 ) {
|
|
for ( code in map ) {
|
|
|
|
// Lazy-add the new callback in a way that preserves old ones
|
|
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
|
|
}
|
|
} else {
|
|
|
|
// Execute the appropriate callbacks
|
|
jqXHR.always( map[ jqXHR.status ] );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Cancel the request
|
|
abort: function( statusText ) {
|
|
var finalText = statusText || strAbort;
|
|
if ( transport ) {
|
|
transport.abort( finalText );
|
|
}
|
|
done( 0, finalText );
|
|
return this;
|
|
}
|
|
};
|
|
|
|
// Attach deferreds
|
|
deferred.promise( jqXHR ).complete = completeDeferred.add;
|
|
jqXHR.success = jqXHR.done;
|
|
jqXHR.error = jqXHR.fail;
|
|
|
|
// Remove hash character (#7531: and string promotion)
|
|
// Add protocol if not provided (prefilters might expect it)
|
|
// Handle falsy url in the settings object (#10093: consistency with old signature)
|
|
// We also use the url parameter if available
|
|
s.url = ( ( url || s.url || location.href ) + "" ).replace( rhash, "" )
|
|
.replace( rprotocol, location.protocol + "//" );
|
|
|
|
// Alias method option to type as per ticket #12004
|
|
s.type = options.method || options.type || s.method || s.type;
|
|
|
|
// Extract dataTypes list
|
|
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
|
|
|
|
// A cross-domain request is in order when the origin doesn't match the current origin.
|
|
if ( s.crossDomain == null ) {
|
|
urlAnchor = document.createElement( "a" );
|
|
|
|
// Support: IE8-11+
|
|
// IE throws exception if url is malformed, e.g. http://example.com:80x/
|
|
try {
|
|
urlAnchor.href = s.url;
|
|
|
|
// Support: IE8-11+
|
|
// Anchor's host property isn't correctly set when s.url is relative
|
|
urlAnchor.href = urlAnchor.href;
|
|
s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
|
|
urlAnchor.protocol + "//" + urlAnchor.host;
|
|
} catch ( e ) {
|
|
|
|
// If there is an error parsing the URL, assume it is crossDomain,
|
|
// it can be rejected by the transport if it is invalid
|
|
s.crossDomain = true;
|
|
}
|
|
}
|
|
|
|
// Convert data if not already a string
|
|
if ( s.data && s.processData && typeof s.data !== "string" ) {
|
|
s.data = jQuery.param( s.data, s.traditional );
|
|
}
|
|
|
|
// Apply prefilters
|
|
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
|
|
|
|
// If request was aborted inside a prefilter, stop there
|
|
if ( state === 2 ) {
|
|
return jqXHR;
|
|
}
|
|
|
|
// We can fire global events as of now if asked to
|
|
// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
|
|
fireGlobals = jQuery.event && s.global;
|
|
|
|
// Watch for a new set of requests
|
|
if ( fireGlobals && jQuery.active++ === 0 ) {
|
|
jQuery.event.trigger( "ajaxStart" );
|
|
}
|
|
|
|
// Uppercase the type
|
|
s.type = s.type.toUpperCase();
|
|
|
|
// Determine if request has content
|
|
s.hasContent = !rnoContent.test( s.type );
|
|
|
|
// Save the URL in case we're toying with the If-Modified-Since
|
|
// and/or If-None-Match header later on
|
|
cacheURL = s.url;
|
|
|
|
// More options handling for requests with no content
|
|
if ( !s.hasContent ) {
|
|
|
|
// If data is available, append data to url
|
|
if ( s.data ) {
|
|
cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
|
|
|
|
// #9682: remove data so that it's not used in an eventual retry
|
|
delete s.data;
|
|
}
|
|
|
|
// Add anti-cache in url if needed
|
|
if ( s.cache === false ) {
|
|
s.url = rts.test( cacheURL ) ?
|
|
|
|
// If there is already a '_' parameter, set its value
|
|
cacheURL.replace( rts, "$1_=" + nonce++ ) :
|
|
|
|
// Otherwise add one to the end
|
|
cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
|
|
}
|
|
}
|
|
|
|
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
|
if ( s.ifModified ) {
|
|
if ( jQuery.lastModified[ cacheURL ] ) {
|
|
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
|
|
}
|
|
if ( jQuery.etag[ cacheURL ] ) {
|
|
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
|
|
}
|
|
}
|
|
|
|
// Set the correct header, if data is being sent
|
|
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
|
|
jqXHR.setRequestHeader( "Content-Type", s.contentType );
|
|
}
|
|
|
|
// Set the Accepts header for the server, depending on the dataType
|
|
jqXHR.setRequestHeader(
|
|
"Accept",
|
|
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
|
|
s.accepts[ s.dataTypes[ 0 ] ] +
|
|
( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
|
|
s.accepts[ "*" ]
|
|
);
|
|
|
|
// Check for headers option
|
|
for ( i in s.headers ) {
|
|
jqXHR.setRequestHeader( i, s.headers[ i ] );
|
|
}
|
|
|
|
// Allow custom headers/mimetypes and early abort
|
|
if ( s.beforeSend &&
|
|
( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
|
|
|
|
// Abort if not done already and return
|
|
return jqXHR.abort();
|
|
}
|
|
|
|
// Aborting is no longer a cancellation
|
|
strAbort = "abort";
|
|
|
|
// Install callbacks on deferreds
|
|
for ( i in { success: 1, error: 1, complete: 1 } ) {
|
|
jqXHR[ i ]( s[ i ] );
|
|
}
|
|
|
|
// Get transport
|
|
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
|
|
|
|
// If no transport, we auto-abort
|
|
if ( !transport ) {
|
|
done( -1, "No Transport" );
|
|
} else {
|
|
jqXHR.readyState = 1;
|
|
|
|
// Send global event
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
|
|
}
|
|
|
|
// If request was aborted inside ajaxSend, stop there
|
|
if ( state === 2 ) {
|
|
return jqXHR;
|
|
}
|
|
|
|
// Timeout
|
|
if ( s.async && s.timeout > 0 ) {
|
|
timeoutTimer = window.setTimeout( function() {
|
|
jqXHR.abort( "timeout" );
|
|
}, s.timeout );
|
|
}
|
|
|
|
try {
|
|
state = 1;
|
|
transport.send( requestHeaders, done );
|
|
} catch ( e ) {
|
|
|
|
// Propagate exception as error if not done
|
|
if ( state < 2 ) {
|
|
done( -1, e );
|
|
|
|
// Simply rethrow otherwise
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Callback for when everything is done
|
|
function done( status, nativeStatusText, responses, headers ) {
|
|
var isSuccess, success, error, response, modified,
|
|
statusText = nativeStatusText;
|
|
|
|
// Called once
|
|
if ( state === 2 ) {
|
|
return;
|
|
}
|
|
|
|
// State is "done" now
|
|
state = 2;
|
|
|
|
// Clear timeout if it exists
|
|
if ( timeoutTimer ) {
|
|
window.clearTimeout( timeoutTimer );
|
|
}
|
|
|
|
// Dereference transport for early garbage collection
|
|
// (no matter how long the jqXHR object will be used)
|
|
transport = undefined;
|
|
|
|
// Cache response headers
|
|
responseHeadersString = headers || "";
|
|
|
|
// Set readyState
|
|
jqXHR.readyState = status > 0 ? 4 : 0;
|
|
|
|
// Determine if successful
|
|
isSuccess = status >= 200 && status < 300 || status === 304;
|
|
|
|
// Get response data
|
|
if ( responses ) {
|
|
response = ajaxHandleResponses( s, jqXHR, responses );
|
|
}
|
|
|
|
// Convert no matter what (that way responseXXX fields are always set)
|
|
response = ajaxConvert( s, response, jqXHR, isSuccess );
|
|
|
|
// If successful, handle type chaining
|
|
if ( isSuccess ) {
|
|
|
|
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
|
if ( s.ifModified ) {
|
|
modified = jqXHR.getResponseHeader( "Last-Modified" );
|
|
if ( modified ) {
|
|
jQuery.lastModified[ cacheURL ] = modified;
|
|
}
|
|
modified = jqXHR.getResponseHeader( "etag" );
|
|
if ( modified ) {
|
|
jQuery.etag[ cacheURL ] = modified;
|
|
}
|
|
}
|
|
|
|
// if no content
|
|
if ( status === 204 || s.type === "HEAD" ) {
|
|
statusText = "nocontent";
|
|
|
|
// if not modified
|
|
} else if ( status === 304 ) {
|
|
statusText = "notmodified";
|
|
|
|
// If we have data, let's convert it
|
|
} else {
|
|
statusText = response.state;
|
|
success = response.data;
|
|
error = response.error;
|
|
isSuccess = !error;
|
|
}
|
|
} else {
|
|
|
|
// Extract error from statusText and normalize for non-aborts
|
|
error = statusText;
|
|
if ( status || !statusText ) {
|
|
statusText = "error";
|
|
if ( status < 0 ) {
|
|
status = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set data for the fake xhr object
|
|
jqXHR.status = status;
|
|
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
|
|
|
|
// Success/Error
|
|
if ( isSuccess ) {
|
|
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
|
|
} else {
|
|
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
|
|
}
|
|
|
|
// Status-dependent callbacks
|
|
jqXHR.statusCode( statusCode );
|
|
statusCode = undefined;
|
|
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
|
|
[ jqXHR, s, isSuccess ? success : error ] );
|
|
}
|
|
|
|
// Complete
|
|
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
|
|
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
|
|
|
|
// Handle the global AJAX counter
|
|
if ( !( --jQuery.active ) ) {
|
|
jQuery.event.trigger( "ajaxStop" );
|
|
}
|
|
}
|
|
}
|
|
|
|
return jqXHR;
|
|
},
|
|
|
|
getJSON: function( url, data, callback ) {
|
|
return jQuery.get( url, data, callback, "json" );
|
|
},
|
|
|
|
getScript: function( url, callback ) {
|
|
return jQuery.get( url, undefined, callback, "script" );
|
|
}
|
|
} );
|
|
|
|
jQuery.each( [ "get", "post" ], function( i, method ) {
|
|
jQuery[ method ] = function( url, data, callback, type ) {
|
|
|
|
// Shift arguments if data argument was omitted
|
|
if ( jQuery.isFunction( data ) ) {
|
|
type = type || callback;
|
|
callback = data;
|
|
data = undefined;
|
|
}
|
|
|
|
// The url can be an options object (which then must have .url)
|
|
return jQuery.ajax( jQuery.extend( {
|
|
url: url,
|
|
type: method,
|
|
dataType: type,
|
|
data: data,
|
|
success: callback
|
|
}, jQuery.isPlainObject( url ) && url ) );
|
|
};
|
|
} );
|
|
|
|
|
|
jQuery._evalUrl = function( url ) {
|
|
return jQuery.ajax( {
|
|
url: url,
|
|
|
|
// Make this explicit, since user can override this through ajaxSetup (#11264)
|
|
type: "GET",
|
|
dataType: "script",
|
|
async: false,
|
|
global: false,
|
|
"throws": true
|
|
} );
|
|
};
|
|
|
|
|
|
jQuery.fn.extend( {
|
|
wrapAll: function( html ) {
|
|
var wrap;
|
|
|
|
if ( jQuery.isFunction( html ) ) {
|
|
return this.each( function( i ) {
|
|
jQuery( this ).wrapAll( html.call( this, i ) );
|
|
} );
|
|
}
|
|
|
|
if ( this[ 0 ] ) {
|
|
|
|
// The elements to wrap the target around
|
|
wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
|
|
|
|
if ( this[ 0 ].parentNode ) {
|
|
wrap.insertBefore( this[ 0 ] );
|
|
}
|
|
|
|
wrap.map( function() {
|
|
var elem = this;
|
|
|
|
while ( elem.firstElementChild ) {
|
|
elem = elem.firstElementChild;
|
|
}
|
|
|
|
return elem;
|
|
} ).append( this );
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
wrapInner: function( html ) {
|
|
if ( jQuery.isFunction( html ) ) {
|
|
return this.each( function( i ) {
|
|
jQuery( this ).wrapInner( html.call( this, i ) );
|
|
} );
|
|
}
|
|
|
|
return this.each( function() {
|
|
var self = jQuery( this ),
|
|
contents = self.contents();
|
|
|
|
if ( contents.length ) {
|
|
contents.wrapAll( html );
|
|
|
|
} else {
|
|
self.append( html );
|
|
}
|
|
} );
|
|
},
|
|
|
|
wrap: function( html ) {
|
|
var isFunction = jQuery.isFunction( html );
|
|
|
|
return this.each( function( i ) {
|
|
jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
|
|
} );
|
|
},
|
|
|
|
unwrap: function() {
|
|
return this.parent().each( function() {
|
|
if ( !jQuery.nodeName( this, "body" ) ) {
|
|
jQuery( this ).replaceWith( this.childNodes );
|
|
}
|
|
} ).end();
|
|
}
|
|
} );
|
|
|
|
|
|
jQuery.expr.filters.hidden = function( elem ) {
|
|
return !jQuery.expr.filters.visible( elem );
|
|
};
|
|
jQuery.expr.filters.visible = function( elem ) {
|
|
|
|
// Support: Opera <= 12.12
|
|
// Opera reports offsetWidths and offsetHeights less than zero on some elements
|
|
// Use OR instead of AND as the element is not visible if either is true
|
|
// See tickets #10406 and #13132
|
|
return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
|
|
};
|
|
|
|
|
|
|
|
|
|
var r20 = /%20/g,
|
|
rbracket = /\[\]$/,
|
|
rCRLF = /\r?\n/g,
|
|
rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
|
|
rsubmittable = /^(?:input|select|textarea|keygen)/i;
|
|
|
|
function buildParams( prefix, obj, traditional, add ) {
|
|
var name;
|
|
|
|
if ( jQuery.isArray( obj ) ) {
|
|
|
|
// Serialize array item.
|
|
jQuery.each( obj, function( i, v ) {
|
|
if ( traditional || rbracket.test( prefix ) ) {
|
|
|
|
// Treat each array item as a scalar.
|
|
add( prefix, v );
|
|
|
|
} else {
|
|
|
|
// Item is non-scalar (array or object), encode its numeric index.
|
|
buildParams(
|
|
prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
|
|
v,
|
|
traditional,
|
|
add
|
|
);
|
|
}
|
|
} );
|
|
|
|
} else if ( !traditional && jQuery.type( obj ) === "object" ) {
|
|
|
|
// Serialize object item.
|
|
for ( name in obj ) {
|
|
buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
|
|
}
|
|
|
|
} else {
|
|
|
|
// Serialize scalar item.
|
|
add( prefix, obj );
|
|
}
|
|
}
|
|
|
|
// Serialize an array of form elements or a set of
|
|
// key/values into a query string
|
|
jQuery.param = function( a, traditional ) {
|
|
var prefix,
|
|
s = [],
|
|
add = function( key, value ) {
|
|
|
|
// If value is a function, invoke it and return its value
|
|
value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
|
|
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
|
|
};
|
|
|
|
// Set traditional to true for jQuery <= 1.3.2 behavior.
|
|
if ( traditional === undefined ) {
|
|
traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
|
|
}
|
|
|
|
// If an array was passed in, assume that it is an array of form elements.
|
|
if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
|
|
|
|
// Serialize the form elements
|
|
jQuery.each( a, function() {
|
|
add( this.name, this.value );
|
|
} );
|
|
|
|
} else {
|
|
|
|
// If traditional, encode the "old" way (the way 1.3.2 or older
|
|
// did it), otherwise encode params recursively.
|
|
for ( prefix in a ) {
|
|
buildParams( prefix, a[ prefix ], traditional, add );
|
|
}
|
|
}
|
|
|
|
// Return the resulting serialization
|
|
return s.join( "&" ).replace( r20, "+" );
|
|
};
|
|
|
|
jQuery.fn.extend( {
|
|
serialize: function() {
|
|
return jQuery.param( this.serializeArray() );
|
|
},
|
|
serializeArray: function() {
|
|
return this.map( function() {
|
|
|
|
// Can add propHook for "elements" to filter or add form elements
|
|
var elements = jQuery.prop( this, "elements" );
|
|
return elements ? jQuery.makeArray( elements ) : this;
|
|
} )
|
|
.filter( function() {
|
|
var type = this.type;
|
|
|
|
// Use .is( ":disabled" ) so that fieldset[disabled] works
|
|
return this.name && !jQuery( this ).is( ":disabled" ) &&
|
|
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
|
|
( this.checked || !rcheckableType.test( type ) );
|
|
} )
|
|
.map( function( i, elem ) {
|
|
var val = jQuery( this ).val();
|
|
|
|
return val == null ?
|
|
null :
|
|
jQuery.isArray( val ) ?
|
|
jQuery.map( val, function( val ) {
|
|
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
|
} ) :
|
|
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
|
} ).get();
|
|
}
|
|
} );
|
|
|
|
|
|
jQuery.ajaxSettings.xhr = function() {
|
|
try {
|
|
return new window.XMLHttpRequest();
|
|
} catch ( e ) {}
|
|
};
|
|
|
|
var xhrSuccessStatus = {
|
|
|
|
// File protocol always yields status code 0, assume 200
|
|
0: 200,
|
|
|
|
// Support: IE9
|
|
// #1450: sometimes IE returns 1223 when it should be 204
|
|
1223: 204
|
|
},
|
|
xhrSupported = jQuery.ajaxSettings.xhr();
|
|
|
|
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
|
|
support.ajax = xhrSupported = !!xhrSupported;
|
|
|
|
jQuery.ajaxTransport( function( options ) {
|
|
var callback, errorCallback;
|
|
|
|
// Cross domain only allowed if supported through XMLHttpRequest
|
|
if ( support.cors || xhrSupported && !options.crossDomain ) {
|
|
return {
|
|
send: function( headers, complete ) {
|
|
var i,
|
|
xhr = options.xhr();
|
|
|
|
xhr.open(
|
|
options.type,
|
|
options.url,
|
|
options.async,
|
|
options.username,
|
|
options.password
|
|
);
|
|
|
|
// Apply custom fields if provided
|
|
if ( options.xhrFields ) {
|
|
for ( i in options.xhrFields ) {
|
|
xhr[ i ] = options.xhrFields[ i ];
|
|
}
|
|
}
|
|
|
|
// Override mime type if needed
|
|
if ( options.mimeType && xhr.overrideMimeType ) {
|
|
xhr.overrideMimeType( options.mimeType );
|
|
}
|
|
|
|
// X-Requested-With header
|
|
// For cross-domain requests, seeing as conditions for a preflight are
|
|
// akin to a jigsaw puzzle, we simply never set it to be sure.
|
|
// (it can always be set on a per-request basis or even using ajaxSetup)
|
|
// For same-domain requests, won't change header if already provided.
|
|
if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
|
|
headers[ "X-Requested-With" ] = "XMLHttpRequest";
|
|
}
|
|
|
|
// Set headers
|
|
for ( i in headers ) {
|
|
xhr.setRequestHeader( i, headers[ i ] );
|
|
}
|
|
|
|
// Callback
|
|
callback = function( type ) {
|
|
return function() {
|
|
if ( callback ) {
|
|
callback = errorCallback = xhr.onload =
|
|
xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;
|
|
|
|
if ( type === "abort" ) {
|
|
xhr.abort();
|
|
} else if ( type === "error" ) {
|
|
|
|
// Support: IE9
|
|
// On a manual native abort, IE9 throws
|
|
// errors on any property access that is not readyState
|
|
if ( typeof xhr.status !== "number" ) {
|
|
complete( 0, "error" );
|
|
} else {
|
|
complete(
|
|
|
|
// File: protocol always yields status 0; see #8605, #14207
|
|
xhr.status,
|
|
xhr.statusText
|
|
);
|
|
}
|
|
} else {
|
|
complete(
|
|
xhrSuccessStatus[ xhr.status ] || xhr.status,
|
|
xhr.statusText,
|
|
|
|
// Support: IE9 only
|
|
// IE9 has no XHR2 but throws on binary (trac-11426)
|
|
// For XHR2 non-text, let the caller handle it (gh-2498)
|
|
( xhr.responseType || "text" ) !== "text" ||
|
|
typeof xhr.responseText !== "string" ?
|
|
{ binary: xhr.response } :
|
|
{ text: xhr.responseText },
|
|
xhr.getAllResponseHeaders()
|
|
);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
// Listen to events
|
|
xhr.onload = callback();
|
|
errorCallback = xhr.onerror = callback( "error" );
|
|
|
|
// Support: IE9
|
|
// Use onreadystatechange to replace onabort
|
|
// to handle uncaught aborts
|
|
if ( xhr.onabort !== undefined ) {
|
|
xhr.onabort = errorCallback;
|
|
} else {
|
|
xhr.onreadystatechange = function() {
|
|
|
|
// Check readyState before timeout as it changes
|
|
if ( xhr.readyState === 4 ) {
|
|
|
|
// Allow onerror to be called first,
|
|
// but that will not handle a native abort
|
|
// Also, save errorCallback to a variable
|
|
// as xhr.onerror cannot be accessed
|
|
window.setTimeout( function() {
|
|
if ( callback ) {
|
|
errorCallback();
|
|
}
|
|
} );
|
|
}
|
|
};
|
|
}
|
|
|
|
// Create the abort callback
|
|
callback = callback( "abort" );
|
|
|
|
try {
|
|
|
|
// Do send the request (this may raise an exception)
|
|
xhr.send( options.hasContent && options.data || null );
|
|
} catch ( e ) {
|
|
|
|
// #14683: Only rethrow if this hasn't been notified as an error yet
|
|
if ( callback ) {
|
|
throw e;
|
|
}
|
|
}
|
|
},
|
|
|
|
abort: function() {
|
|
if ( callback ) {
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
} );
|
|
|
|
|
|
|
|
|
|
// Install script dataType
|
|
jQuery.ajaxSetup( {
|
|
accepts: {
|
|
script: "text/javascript, application/javascript, " +
|
|
"application/ecmascript, application/x-ecmascript"
|
|
},
|
|
contents: {
|
|
script: /\b(?:java|ecma)script\b/
|
|
},
|
|
converters: {
|
|
"text script": function( text ) {
|
|
jQuery.globalEval( text );
|
|
return text;
|
|
}
|
|
}
|
|
} );
|
|
|
|
// Handle cache's special case and crossDomain
|
|
jQuery.ajaxPrefilter( "script", function( s ) {
|
|
if ( s.cache === undefined ) {
|
|
s.cache = false;
|
|
}
|
|
if ( s.crossDomain ) {
|
|
s.type = "GET";
|
|
}
|
|
} );
|
|
|
|
// Bind script tag hack transport
|
|
jQuery.ajaxTransport( "script", function( s ) {
|
|
|
|
// This transport only deals with cross domain requests
|
|
if ( s.crossDomain ) {
|
|
var script, callback;
|
|
return {
|
|
send: function( _, complete ) {
|
|
script = jQuery( "<script>" ).prop( {
|
|
charset: s.scriptCharset,
|
|
src: s.url
|
|
} ).on(
|
|
"load error",
|
|
callback = function( evt ) {
|
|
script.remove();
|
|
callback = null;
|
|
if ( evt ) {
|
|
complete( evt.type === "error" ? 404 : 200, evt.type );
|
|
}
|
|
}
|
|
);
|
|
|
|
// Use native DOM manipulation to avoid our domManip AJAX trickery
|
|
document.head.appendChild( script[ 0 ] );
|
|
},
|
|
abort: function() {
|
|
if ( callback ) {
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
} );
|
|
|
|
|
|
|
|
|
|
var oldCallbacks = [],
|
|
rjsonp = /(=)\?(?=&|$)|\?\?/;
|
|
|
|
// Default jsonp settings
|
|
jQuery.ajaxSetup( {
|
|
jsonp: "callback",
|
|
jsonpCallback: function() {
|
|
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
|
|
this[ callback ] = true;
|
|
return callback;
|
|
}
|
|
} );
|
|
|
|
// Detect, normalize options and install callbacks for jsonp requests
|
|
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
|
|
|
|
var callbackName, overwritten, responseContainer,
|
|
jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
|
|
"url" :
|
|
typeof s.data === "string" &&
|
|
( s.contentType || "" )
|
|
.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
|
|
rjsonp.test( s.data ) && "data"
|
|
);
|
|
|
|
// Handle iff the expected data type is "jsonp" or we have a parameter to set
|
|
if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
|
|
|
|
// Get callback name, remembering preexisting value associated with it
|
|
callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
|
|
s.jsonpCallback() :
|
|
s.jsonpCallback;
|
|
|
|
// Insert callback into url or form data
|
|
if ( jsonProp ) {
|
|
s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
|
|
} else if ( s.jsonp !== false ) {
|
|
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
|
|
}
|
|
|
|
// Use data converter to retrieve json after script execution
|
|
s.converters[ "script json" ] = function() {
|
|
if ( !responseContainer ) {
|
|
jQuery.error( callbackName + " was not called" );
|
|
}
|
|
return responseContainer[ 0 ];
|
|
};
|
|
|
|
// Force json dataType
|
|
s.dataTypes[ 0 ] = "json";
|
|
|
|
// Install callback
|
|
overwritten = window[ callbackName ];
|
|
window[ callbackName ] = function() {
|
|
responseContainer = arguments;
|
|
};
|
|
|
|
// Clean-up function (fires after converters)
|
|
jqXHR.always( function() {
|
|
|
|
// If previous value didn't exist - remove it
|
|
if ( overwritten === undefined ) {
|
|
jQuery( window ).removeProp( callbackName );
|
|
|
|
// Otherwise restore preexisting value
|
|
} else {
|
|
window[ callbackName ] = overwritten;
|
|
}
|
|
|
|
// Save back as free
|
|
if ( s[ callbackName ] ) {
|
|
|
|
// Make sure that re-using the options doesn't screw things around
|
|
s.jsonpCallback = originalSettings.jsonpCallback;
|
|
|
|
// Save the callback name for future use
|
|
oldCallbacks.push( callbackName );
|
|
}
|
|
|
|
// Call if it was a function and we have a response
|
|
if ( responseContainer && jQuery.isFunction( overwritten ) ) {
|
|
overwritten( responseContainer[ 0 ] );
|
|
}
|
|
|
|
responseContainer = overwritten = undefined;
|
|
} );
|
|
|
|
// Delegate to script
|
|
return "script";
|
|
}
|
|
} );
|
|
|
|
|
|
|
|
|
|
// Argument "data" should be string of html
|
|
// context (optional): If specified, the fragment will be created in this context,
|
|
// defaults to document
|
|
// keepScripts (optional): If true, will include scripts passed in the html string
|
|
jQuery.parseHTML = function( data, context, keepScripts ) {
|
|
if ( !data || typeof data !== "string" ) {
|
|
return null;
|
|
}
|
|
if ( typeof context === "boolean" ) {
|
|
keepScripts = context;
|
|
context = false;
|
|
}
|
|
context = context || document;
|
|
|
|
var parsed = rsingleTag.exec( data ),
|
|
scripts = !keepScripts && [];
|
|
|
|
// Single tag
|
|
if ( parsed ) {
|
|
return [ context.createElement( parsed[ 1 ] ) ];
|
|
}
|
|
|
|
parsed = buildFragment( [ data ], context, scripts );
|
|
|
|
if ( scripts && scripts.length ) {
|
|
jQuery( scripts ).remove();
|
|
}
|
|
|
|
return jQuery.merge( [], parsed.childNodes );
|
|
};
|
|
|
|
|
|
// Keep a copy of the old load method
|
|
var _load = jQuery.fn.load;
|
|
|
|
/**
|
|
* Load a url into a page
|
|
*/
|
|
jQuery.fn.load = function( url, params, callback ) {
|
|
if ( typeof url !== "string" && _load ) {
|
|
return _load.apply( this, arguments );
|
|
}
|
|
|
|
var selector, type, response,
|
|
self = this,
|
|
off = url.indexOf( " " );
|
|
|
|
if ( off > -1 ) {
|
|
selector = jQuery.trim( url.slice( off ) );
|
|
url = url.slice( 0, off );
|
|
}
|
|
|
|
// If it's a function
|
|
if ( jQuery.isFunction( params ) ) {
|
|
|
|
// We assume that it's the callback
|
|
callback = params;
|
|
params = undefined;
|
|
|
|
// Otherwise, build a param string
|
|
} else if ( params && typeof params === "object" ) {
|
|
type = "POST";
|
|
}
|
|
|
|
// If we have elements to modify, make the request
|
|
if ( self.length > 0 ) {
|
|
jQuery.ajax( {
|
|
url: url,
|
|
|
|
// If "type" variable is undefined, then "GET" method will be used.
|
|
// Make value of this field explicit since
|
|
// user can override it through ajaxSetup method
|
|
type: type || "GET",
|
|
dataType: "html",
|
|
data: params
|
|
} ).done( function( responseText ) {
|
|
|
|
// Save response for use in complete callback
|
|
response = arguments;
|
|
|
|
self.html( selector ?
|
|
|
|
// If a selector was specified, locate the right elements in a dummy div
|
|
// Exclude scripts to avoid IE 'Permission Denied' errors
|
|
jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
|
|
|
|
// Otherwise use the full result
|
|
responseText );
|
|
|
|
// If the request succeeds, this function gets "data", "status", "jqXHR"
|
|
// but they are ignored because response was set above.
|
|
// If it fails, this function gets "jqXHR", "status", "error"
|
|
} ).always( callback && function( jqXHR, status ) {
|
|
self.each( function() {
|
|
callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
|
|
} );
|
|
} );
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
|
|
|
|
// Attach a bunch of functions for handling common AJAX events
|
|
jQuery.each( [
|
|
"ajaxStart",
|
|
"ajaxStop",
|
|
"ajaxComplete",
|
|
"ajaxError",
|
|
"ajaxSuccess",
|
|
"ajaxSend"
|
|
], function( i, type ) {
|
|
jQuery.fn[ type ] = function( fn ) {
|
|
return this.on( type, fn );
|
|
};
|
|
} );
|
|
|
|
|
|
|
|
|
|
jQuery.expr.filters.animated = function( elem ) {
|
|
return jQuery.grep( jQuery.timers, function( fn ) {
|
|
return elem === fn.elem;
|
|
} ).length;
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Gets a window from an element
|
|
*/
|
|
function getWindow( elem ) {
|
|
return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
|
|
}
|
|
|
|
jQuery.offset = {
|
|
setOffset: function( elem, options, i ) {
|
|
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
|
|
position = jQuery.css( elem, "position" ),
|
|
curElem = jQuery( elem ),
|
|
props = {};
|
|
|
|
// Set position first, in-case top/left are set even on static elem
|
|
if ( position === "static" ) {
|
|
elem.style.position = "relative";
|
|
}
|
|
|
|
curOffset = curElem.offset();
|
|
curCSSTop = jQuery.css( elem, "top" );
|
|
curCSSLeft = jQuery.css( elem, "left" );
|
|
calculatePosition = ( position === "absolute" || position === "fixed" ) &&
|
|
( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
|
|
|
|
// Need to be able to calculate position if either
|
|
// top or left is auto and position is either absolute or fixed
|
|
if ( calculatePosition ) {
|
|
curPosition = curElem.position();
|
|
curTop = curPosition.top;
|
|
curLeft = curPosition.left;
|
|
|
|
} else {
|
|
curTop = parseFloat( curCSSTop ) || 0;
|
|
curLeft = parseFloat( curCSSLeft ) || 0;
|
|
}
|
|
|
|
if ( jQuery.isFunction( options ) ) {
|
|
|
|
// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
|
|
options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
|
|
}
|
|
|
|
if ( options.top != null ) {
|
|
props.top = ( options.top - curOffset.top ) + curTop;
|
|
}
|
|
if ( options.left != null ) {
|
|
props.left = ( options.left - curOffset.left ) + curLeft;
|
|
}
|
|
|
|
if ( "using" in options ) {
|
|
options.using.call( elem, props );
|
|
|
|
} else {
|
|
curElem.css( props );
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.fn.extend( {
|
|
offset: function( options ) {
|
|
if ( arguments.length ) {
|
|
return options === undefined ?
|
|
this :
|
|
this.each( function( i ) {
|
|
jQuery.offset.setOffset( this, options, i );
|
|
} );
|
|
}
|
|
|
|
var docElem, win,
|
|
elem = this[ 0 ],
|
|
box = { top: 0, left: 0 },
|
|
doc = elem && elem.ownerDocument;
|
|
|
|
if ( !doc ) {
|
|
return;
|
|
}
|
|
|
|
docElem = doc.documentElement;
|
|
|
|
// Make sure it's not a disconnected DOM node
|
|
if ( !jQuery.contains( docElem, elem ) ) {
|
|
return box;
|
|
}
|
|
|
|
box = elem.getBoundingClientRect();
|
|
win = getWindow( doc );
|
|
return {
|
|
top: box.top + win.pageYOffset - docElem.clientTop,
|
|
left: box.left + win.pageXOffset - docElem.clientLeft
|
|
};
|
|
},
|
|
|
|
position: function() {
|
|
if ( !this[ 0 ] ) {
|
|
return;
|
|
}
|
|
|
|
var offsetParent, offset,
|
|
elem = this[ 0 ],
|
|
parentOffset = { top: 0, left: 0 };
|
|
|
|
// Fixed elements are offset from window (parentOffset = {top:0, left: 0},
|
|
// because it is its only offset parent
|
|
if ( jQuery.css( elem, "position" ) === "fixed" ) {
|
|
|
|
// Assume getBoundingClientRect is there when computed position is fixed
|
|
offset = elem.getBoundingClientRect();
|
|
|
|
} else {
|
|
|
|
// Get *real* offsetParent
|
|
offsetParent = this.offsetParent();
|
|
|
|
// Get correct offsets
|
|
offset = this.offset();
|
|
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
|
|
parentOffset = offsetParent.offset();
|
|
}
|
|
|
|
// Add offsetParent borders
|
|
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
|
|
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
|
|
}
|
|
|
|
// Subtract parent offsets and element margins
|
|
return {
|
|
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
|
|
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
|
|
};
|
|
},
|
|
|
|
// This method will return documentElement in the following cases:
|
|
// 1) For the element inside the iframe without offsetParent, this method will return
|
|
// documentElement of the parent window
|
|
// 2) For the hidden or detached element
|
|
// 3) For body or html element, i.e. in case of the html node - it will return itself
|
|
//
|
|
// but those exceptions were never presented as a real life use-cases
|
|
// and might be considered as more preferable results.
|
|
//
|
|
// This logic, however, is not guaranteed and can change at any point in the future
|
|
offsetParent: function() {
|
|
return this.map( function() {
|
|
var offsetParent = this.offsetParent;
|
|
|
|
while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
|
|
offsetParent = offsetParent.offsetParent;
|
|
}
|
|
|
|
return offsetParent || documentElement;
|
|
} );
|
|
}
|
|
} );
|
|
|
|
// Create scrollLeft and scrollTop methods
|
|
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
|
|
var top = "pageYOffset" === prop;
|
|
|
|
jQuery.fn[ method ] = function( val ) {
|
|
return access( this, function( elem, method, val ) {
|
|
var win = getWindow( elem );
|
|
|
|
if ( val === undefined ) {
|
|
return win ? win[ prop ] : elem[ method ];
|
|
}
|
|
|
|
if ( win ) {
|
|
win.scrollTo(
|
|
!top ? val : win.pageXOffset,
|
|
top ? val : win.pageYOffset
|
|
);
|
|
|
|
} else {
|
|
elem[ method ] = val;
|
|
}
|
|
}, method, val, arguments.length );
|
|
};
|
|
} );
|
|
|
|
// Support: Safari<7-8+, Chrome<37-44+
|
|
// Add the top/left cssHooks using jQuery.fn.position
|
|
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
|
|
// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
|
|
// getComputedStyle returns percent when specified for top/left/bottom/right;
|
|
// rather than make the css module depend on the offset module, just check for it here
|
|
jQuery.each( [ "top", "left" ], function( i, prop ) {
|
|
jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
|
|
function( elem, computed ) {
|
|
if ( computed ) {
|
|
computed = curCSS( elem, prop );
|
|
|
|
// If curCSS returns percentage, fallback to offset
|
|
return rnumnonpx.test( computed ) ?
|
|
jQuery( elem ).position()[ prop ] + "px" :
|
|
computed;
|
|
}
|
|
}
|
|
);
|
|
} );
|
|
|
|
|
|
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
|
|
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
|
|
jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
|
|
function( defaultExtra, funcName ) {
|
|
|
|
// Margin is only for outerHeight, outerWidth
|
|
jQuery.fn[ funcName ] = function( margin, value ) {
|
|
var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
|
|
extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
|
|
|
|
return access( this, function( elem, type, value ) {
|
|
var doc;
|
|
|
|
if ( jQuery.isWindow( elem ) ) {
|
|
|
|
// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
|
|
// isn't a whole lot we can do. See pull request at this URL for discussion:
|
|
// https://github.com/jquery/jquery/pull/764
|
|
return elem.document.documentElement[ "client" + name ];
|
|
}
|
|
|
|
// Get document width or height
|
|
if ( elem.nodeType === 9 ) {
|
|
doc = elem.documentElement;
|
|
|
|
// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
|
|
// whichever is greatest
|
|
return Math.max(
|
|
elem.body[ "scroll" + name ], doc[ "scroll" + name ],
|
|
elem.body[ "offset" + name ], doc[ "offset" + name ],
|
|
doc[ "client" + name ]
|
|
);
|
|
}
|
|
|
|
return value === undefined ?
|
|
|
|
// Get width or height on the element, requesting but not forcing parseFloat
|
|
jQuery.css( elem, type, extra ) :
|
|
|
|
// Set width or height on the element
|
|
jQuery.style( elem, type, value, extra );
|
|
}, type, chainable ? margin : undefined, chainable, null );
|
|
};
|
|
} );
|
|
} );
|
|
|
|
|
|
jQuery.fn.extend( {
|
|
|
|
bind: function( types, data, fn ) {
|
|
return this.on( types, null, data, fn );
|
|
},
|
|
unbind: function( types, fn ) {
|
|
return this.off( types, null, fn );
|
|
},
|
|
|
|
delegate: function( selector, types, data, fn ) {
|
|
return this.on( types, selector, data, fn );
|
|
},
|
|
undelegate: function( selector, types, fn ) {
|
|
|
|
// ( namespace ) or ( selector, types [, fn] )
|
|
return arguments.length === 1 ?
|
|
this.off( selector, "**" ) :
|
|
this.off( types, selector || "**", fn );
|
|
},
|
|
size: function() {
|
|
return this.length;
|
|
}
|
|
} );
|
|
|
|
jQuery.fn.andSelf = jQuery.fn.addBack;
|
|
|
|
|
|
|
|
|
|
// Register as a named AMD module, since jQuery can be concatenated with other
|
|
// files that may use define, but not via a proper concatenation script that
|
|
// understands anonymous AMD modules. A named AMD is safest and most robust
|
|
// way to register. Lowercase jquery is used because AMD module names are
|
|
// derived from file names, and jQuery is normally delivered in a lowercase
|
|
// file name. Do this after creating the global so that if an AMD module wants
|
|
// to call noConflict to hide this version of jQuery, it will work.
|
|
|
|
// Note that for maximum portability, libraries that are not jQuery should
|
|
// declare themselves as anonymous modules, and avoid setting a global if an
|
|
// AMD loader is present. jQuery is a special case. For more information, see
|
|
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
|
|
|
|
if ( true ) {
|
|
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function() {
|
|
return jQuery;
|
|
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
|
|
}
|
|
|
|
|
|
|
|
var
|
|
|
|
// Map over jQuery in case of overwrite
|
|
_jQuery = window.jQuery,
|
|
|
|
// Map over the $ in case of overwrite
|
|
_$ = window.$;
|
|
|
|
jQuery.noConflict = function( deep ) {
|
|
if ( window.$ === jQuery ) {
|
|
window.$ = _$;
|
|
}
|
|
|
|
if ( deep && window.jQuery === jQuery ) {
|
|
window.jQuery = _jQuery;
|
|
}
|
|
|
|
return jQuery;
|
|
};
|
|
|
|
// Expose jQuery and $ identifiers, even in AMD
|
|
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
|
|
// and CommonJS for browser emulators (#13566)
|
|
if ( !noGlobal ) {
|
|
window.jQuery = window.$ = jQuery;
|
|
}
|
|
|
|
return jQuery;
|
|
}));
|
|
|
|
|
|
/***/ }),
|
|
/* 4 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.Panel = undefined;
|
|
|
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
__webpack_require__(2);
|
|
|
|
var _colors = __webpack_require__(5);
|
|
|
|
var _colors2 = _interopRequireDefault(_colors);
|
|
|
|
var _jquery = __webpack_require__(3);
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function Panel(panel, isDualTilt) {
|
|
var scale = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
|
|
var treatCoordinatesAsCenterpoints = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
|
|
|
|
this.Container_constructor();
|
|
this.panel = panel;
|
|
this.isDualTilt = isDualTilt;
|
|
this.textOverlays = {};
|
|
this.selected = false;
|
|
this.scale = scale;
|
|
this.treatCoordinatesAsCenterpoints = treatCoordinatesAsCenterpoints;
|
|
this.setup();
|
|
}
|
|
|
|
var panelMethods = {
|
|
setup: function setup() {
|
|
if (this.treatCoordinatesAsCenterpoints) {
|
|
this.x = (this.panel.x - this.panel.width / 2.0) * this.scale;
|
|
this.y = (this.panel.y - this.panel.height / 2.0) * this.scale;
|
|
} else {
|
|
this.x = this.panel.x * this.scale;
|
|
this.y = this.panel.y * this.scale;
|
|
}
|
|
this.width = this.scale * this.panel.width;
|
|
this.height = this.scale * this.panel.height;
|
|
this.drawBackground();
|
|
},
|
|
drawBackground: function drawBackground() {
|
|
if (this.background === undefined) {
|
|
this.background = new createjs.Shape();
|
|
} else {
|
|
this.removeChild(this.background);
|
|
}
|
|
var fillColor = void 0;
|
|
var borderColor = void 0;
|
|
var textColor = void 0;
|
|
var secondFillColor = void 0;
|
|
if (this.panel.data.seismic_anchors > 0 && this.panel.data.wind_anchors > 0) {
|
|
fillColor = _colors2.default.seismic_background;
|
|
secondFillColor = _colors2.default.wind_background;
|
|
textColor = _colors2.default.dark_text;
|
|
borderColor = _colors2.default.border;
|
|
} else if (this.panel.data.seismic_anchors > 0) {
|
|
fillColor = _colors2.default.seismic_background;
|
|
textColor = _colors2.default.dark_text;
|
|
borderColor = _colors2.default.border;
|
|
} else if (this.panel.data.wind_anchors > 0) {
|
|
fillColor = _colors2.default.wind_background;
|
|
textColor = _colors2.default.dark_text;
|
|
borderColor = _colors2.default.border;
|
|
} else {
|
|
fillColor = _colors2.default.default_background;
|
|
textColor = _colors2.default.light_text;
|
|
borderColor = fillColor;
|
|
}
|
|
|
|
if (this.selected) {
|
|
fillColor = _colors2.default.selected_background;
|
|
textColor = _colors2.default.dark_text;
|
|
borderColor = _colors2.default.border;
|
|
}
|
|
|
|
this.textColor = textColor;
|
|
|
|
var width = this.width;
|
|
var height = this.height;
|
|
|
|
var borderWidth = 0.5;
|
|
|
|
// main background fill rectangle with white border
|
|
this.background.graphics.setStrokeStyle(borderWidth).beginStroke('white').beginFill(fillColor).drawRect(0, 0, width, height).endFill().endStroke();
|
|
|
|
// diagonal split background color for multiple anchor types
|
|
if (secondFillColor !== undefined) {
|
|
this.background.graphics.beginFill(secondFillColor).moveTo(borderWidth, borderWidth).lineTo(width - borderWidth, height - borderWidth).lineTo(width - borderWidth, borderWidth).closePath().endFill().endStroke();
|
|
}
|
|
|
|
// inner border for use with light background colors
|
|
this.background.graphics.beginStroke(borderColor).drawRect(borderWidth, borderWidth, width - 2 * borderWidth, height - 2 * borderWidth).endStroke();
|
|
|
|
// line drawing of dual-tilt indicator (the right-facing triangle)
|
|
if (this.isDualTilt) {
|
|
this.background.graphics.setStrokeStyle(0.25).beginStroke(_colors2.default.border).moveTo(width / 2., borderWidth).lineTo(width / 2., height - borderWidth).lineTo(width - borderWidth, height / 2.).closePath().endStroke();
|
|
}
|
|
this.addChildAt(this.background, 0);
|
|
},
|
|
select: function select() {
|
|
this.selected = true;
|
|
this.drawBackground();
|
|
this.redrawOverlays();
|
|
},
|
|
deselect: function deselect() {
|
|
this.selected = false;
|
|
this.drawBackground();
|
|
this.redrawOverlays();
|
|
},
|
|
setOverlay: function setOverlay(overlay) {
|
|
var data = this.panel.data;
|
|
var self = this;
|
|
_jquery2.default.each(this.textOverlays, function (idx, overlay) {
|
|
self.removeChild(overlay);
|
|
});
|
|
this.textOverlays = {};
|
|
if (overlay == "ALL") {
|
|
this.addOverlay('panel_id', data.panel_id, "2.5px", 0.3, 0.55);
|
|
this.addOverlay('wind_zones', data.wind_zones, '2.5px', 0.7, 0.55);
|
|
this.addOverlay('ballast', data.ballast, "1.5px", 0.125, 0.25);
|
|
this.addOverlay('wind_anchors', data.wind_anchors, "1.5px", 0.125, 0.8);
|
|
this.addOverlay('seismic_anchors', "S".repeat(data.seismic_anchors), "1.5px", 0.35, 0.8);
|
|
this.addOverlay('cross_trays', data.cross_trays, "1.5px", 0.6, 0.25);
|
|
this.addOverlay('link_trays', data.link_trays, "1.5px", 0.85, 0.25);
|
|
this.addOverlay('subarray', data.subarray, "1.5px", 0.6, 0.8);
|
|
this.addOverlay('psf', data.psf.toPrecision(3), "1.5px", 0.35, 0.25);
|
|
this.addOverlay('panel_type', data.panel_type, "1.5px", 0.85, 0.8);
|
|
} else if (overlay == 'ANCHOR') {
|
|
this.addOverlay('ballast', data.ballast, "2.5px", 0.3, 0.55);
|
|
this.addOverlay('anchors', data.seismic_anchors + data.wind_anchors, '2.5px', 0.7, 0.55);
|
|
}
|
|
this.currentOverlay = overlay;
|
|
},
|
|
addOverlay: function addOverlay(name, text, fontSize, relativeX, relativeY) {
|
|
var overlay = new createjs.Text();
|
|
overlay.text = text;
|
|
overlay.font = fontSize + " sans-serif";
|
|
overlay.x = this.width * relativeX;
|
|
overlay.y = this.height * relativeY;
|
|
overlay.textAlign = "center";
|
|
overlay.textBaseline = "middle";
|
|
overlay.color = this.textColor;
|
|
overlay.maxWidth = this.width * 0.5;
|
|
this.textOverlays[name] = overlay;
|
|
this.addChild(overlay);
|
|
},
|
|
redrawOverlays: function redrawOverlays() {
|
|
this.setOverlay(this.currentOverlay);
|
|
}
|
|
};
|
|
|
|
_extends(Panel.prototype, createjs.extend(Panel, createjs.Container));
|
|
_extends(Panel.prototype, panelMethods);
|
|
exports.Panel = Panel = createjs.promote(Panel, "Container");
|
|
|
|
exports.Panel = Panel;
|
|
|
|
/***/ }),
|
|
/* 5 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
var Colors = {
|
|
canvas_background: '#F9F9F9',
|
|
|
|
seismic_background: '#F1E8A2',
|
|
wind_background: '#B8F3E5',
|
|
default_background: '#133256',
|
|
selected_background: 'white',
|
|
light_text: 'white',
|
|
dark_text: '#6490BA',
|
|
border: '#537DAA'
|
|
};
|
|
|
|
exports.default = Colors;
|
|
|
|
/***/ }),
|
|
/* 6 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
var _jquery = __webpack_require__(3);
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var ZoomControl = function () {
|
|
function ZoomControl(arrayVisualization) {
|
|
_classCallCheck(this, ZoomControl);
|
|
|
|
this.visualization = arrayVisualization;
|
|
this.zooms = [1, 2, 3, 4, 5, 6, 7];
|
|
this.zoomLevel = 0;
|
|
}
|
|
|
|
_createClass(ZoomControl, [{
|
|
key: "init",
|
|
value: function init(zoomSelector) {
|
|
var self = this;
|
|
zoomSelector.find("#increase_zoom").click(function () {
|
|
if (self.zoomLevel != self.zooms.length - 1) {
|
|
self.setZoom(self.zoomLevel + 1, zoomSelector);
|
|
}
|
|
});
|
|
|
|
zoomSelector.find("#decrease_zoom").click(function () {
|
|
if (self.zoomLevel != 0) {
|
|
self.setZoom(self.zoomLevel - 1, zoomSelector);
|
|
}
|
|
});
|
|
|
|
var _loop = function _loop(i) {
|
|
zoomSelector.find("#zoom_indicator_" + i).click(function () {
|
|
self.setZoom(i, zoomSelector);
|
|
});
|
|
};
|
|
|
|
for (var i = 0; i < self.zooms.length; i++) {
|
|
_loop(i);
|
|
}
|
|
|
|
self.setZoom(0, zoomSelector);
|
|
}
|
|
}, {
|
|
key: "setZoom",
|
|
value: function setZoom(zoomLevel, zoomSelector) {
|
|
var oldZoom = this.zoomLevel;
|
|
var totalZooms = this.zooms.length;
|
|
this.zoomLevel = zoomLevel % totalZooms;
|
|
|
|
var zoom = this.zooms[this.zoomLevel];
|
|
var zoom_percentage = Math.round(100 / (totalZooms - 1) * this.zoomLevel);
|
|
this.visualization.setZoom(zoom);
|
|
|
|
zoomSelector.find("#zoom_indicator_" + oldZoom).removeClass("zoom_active");
|
|
zoomSelector.find("#zoom_indicator_" + this.zoomLevel).addClass("zoom_active");
|
|
zoomSelector.find("#zoom_level").text(zoom_percentage + "%");
|
|
}
|
|
}]);
|
|
|
|
return ZoomControl;
|
|
}();
|
|
|
|
exports.default = ZoomControl;
|
|
|
|
/***/ }),
|
|
/* 7 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
var _jquery = __webpack_require__(3);
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var OverlayControl = function () {
|
|
function OverlayControl(arrayVisualization) {
|
|
_classCallCheck(this, OverlayControl);
|
|
|
|
this.visualization = arrayVisualization;
|
|
}
|
|
|
|
_createClass(OverlayControl, [{
|
|
key: "init",
|
|
value: function init(overlaySelector, legendSelector) {
|
|
var self = this;
|
|
|
|
this.setOverlay("ANCHOR", overlaySelector, legendSelector);
|
|
|
|
overlaySelector.find("#anchor_overlay").click(function () {
|
|
self.setOverlay("ANCHOR", overlaySelector, legendSelector);
|
|
});
|
|
|
|
overlaySelector.find("#all_overlay").click(function () {
|
|
self.setOverlay("ALL", overlaySelector, legendSelector);
|
|
});
|
|
}
|
|
}, {
|
|
key: "setOverlay",
|
|
value: function setOverlay(overlay, overlaySelector, legendSelector) {
|
|
var selectedButton = void 0;
|
|
var selectedLegend = void 0;
|
|
if (overlay == "ANCHOR") {
|
|
selectedButton = overlaySelector.find("#anchor_overlay");
|
|
selectedLegend = legendSelector.find("img.anchors_mode");
|
|
} else if (overlay == "ALL") {
|
|
selectedButton = overlaySelector.find("#all_overlay");
|
|
selectedLegend = legendSelector.find("img.all_mode");
|
|
}
|
|
|
|
this.visualization.setOverlay(overlay);
|
|
|
|
overlaySelector.find('.overlay_toggle').removeClass("overlay_active");
|
|
selectedButton.addClass("overlay_active");
|
|
|
|
legendSelector.find('.legend').hide();
|
|
selectedLegend.show();
|
|
}
|
|
}]);
|
|
|
|
return OverlayControl;
|
|
}();
|
|
|
|
exports.default = OverlayControl;
|
|
|
|
/***/ }),
|
|
/* 8 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
var _jquery = __webpack_require__(3);
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var SeismicControl = function () {
|
|
function SeismicControl(arrayVisualization, subarrayDisplay) {
|
|
_classCallCheck(this, SeismicControl);
|
|
|
|
this.visualization = arrayVisualization;
|
|
this.subarrayDisplay = subarrayDisplay;
|
|
}
|
|
|
|
_createClass(SeismicControl, [{
|
|
key: "init",
|
|
value: function init(seismicSelector, bannerSelector) {
|
|
var self = this;
|
|
|
|
seismicSelector.find("#add_seismic").click(function () {
|
|
self.visualization.addSeismicAnchor();
|
|
});
|
|
|
|
seismicSelector.find("#remove_seismic").click(function () {
|
|
self.visualization.removeSeismicAnchor();
|
|
});
|
|
|
|
seismicSelector.find("#save_seismic_changes").click(function () {
|
|
var updatedData = self.visualization.panelData.map(function (panel) {
|
|
return {
|
|
panel_id: panel.data.panel_id,
|
|
seismic_anchors: panel.data.seismic_anchors
|
|
};
|
|
});
|
|
|
|
_jquery2.default.ajax({
|
|
url: "/api/update_panel_data",
|
|
data: JSON.stringify(updatedData),
|
|
contentType: "application/json; charset=utf-8",
|
|
method: 'POST'
|
|
}).then(function (data) {
|
|
bannerSelector.removeClass("seismic_error");
|
|
bannerSelector.removeClass("seismic_success");
|
|
bannerSelector.removeClass("hidden");
|
|
|
|
var seismicClass = void 0;
|
|
var bannerText = void 0;
|
|
|
|
bannerSelector.find(".circle").removeClass("icon-ok");
|
|
bannerSelector.find(".circle").text("!");
|
|
if (data.status !== undefined && data.status == "success") {
|
|
seismicClass = "seismic_success";
|
|
bannerText = "Changes to the Seismic Anchors have been successfully saved!";
|
|
bannerSelector.find(".circle").addClass("icon-ok");
|
|
bannerSelector.find(".circle").text("");
|
|
self.visualization.refreshPanels(data.panel_data);
|
|
self.subarrayDisplay.didUpdateSubarrayData(data.subarray_data);
|
|
} else if (data.error !== undefined) {
|
|
seismicClass = "seismic_error";
|
|
bannerText = data.error;
|
|
self.subarrayDisplay.didUpdateSubarrayData(data.subarray_data);
|
|
} else {
|
|
seismicClass = "seismic_error";
|
|
bannerText = "Unknown error, please try again.";
|
|
}
|
|
|
|
bannerSelector.addClass(seismicClass);
|
|
bannerSelector.find(".seismic_save_message").text(bannerText);
|
|
});
|
|
});
|
|
|
|
bannerSelector.find(".dismiss_button").click(function () {
|
|
bannerSelector.addClass("hidden");
|
|
bannerSelector.removeClass("seismic_error");
|
|
bannerSelector.removeClass("seismic_success");
|
|
});
|
|
}
|
|
}]);
|
|
|
|
return SeismicControl;
|
|
}();
|
|
|
|
exports.default = SeismicControl;
|
|
|
|
/***/ }),
|
|
/* 9 */
|
|
/***/ (function(module, exports) {
|
|
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var SubarrayDisplay = function () {
|
|
function SubarrayDisplay() {
|
|
_classCallCheck(this, SubarrayDisplay);
|
|
}
|
|
|
|
_createClass(SubarrayDisplay, [{
|
|
key: 'init',
|
|
value: function init(currentSeismicCountSelector, neededAnchorSelector, weightSelector, panelData) {
|
|
this.currentSeismicCountSelector = currentSeismicCountSelector;
|
|
this.neededAnchorSelector = neededAnchorSelector;
|
|
this.weightSelector = weightSelector;
|
|
this.didModifyPanels(panelData);
|
|
}
|
|
}, {
|
|
key: 'didModifyPanels',
|
|
value: function didModifyPanels(panelData) {
|
|
var seismicCount = this.computeSeismicAnchorCounts(panelData);
|
|
|
|
this.assignSeismicCountItems(seismicCount);
|
|
}
|
|
}, {
|
|
key: 'didUpdateSubarrayData',
|
|
value: function didUpdateSubarrayData(subarrayData) {
|
|
for (var i = 0; i < subarrayData.length; i++) {
|
|
var data = subarrayData[i];
|
|
var subarray = i + 2;
|
|
var neededAnchors = data.required_seismic_anchors;
|
|
var weight = data.weight;
|
|
this.neededAnchorSelector.find('td:nth-child(' + subarray + ')').text(neededAnchors);
|
|
this.weightSelector.find('td:nth-child(' + subarray + ')').text(weight.toLocaleString());
|
|
}
|
|
}
|
|
}, {
|
|
key: 'computeSeismicAnchorCounts',
|
|
value: function computeSeismicAnchorCounts(panelData) {
|
|
var seismicCount = {};
|
|
for (var i = 0; i < panelData.length; i++) {
|
|
var panel = panelData[i].data;
|
|
if (seismicCount[panel.subarray] === undefined) {
|
|
seismicCount[panel.subarray] = 0;
|
|
}
|
|
seismicCount[panel.subarray] += panel.seismic_anchors;
|
|
}
|
|
|
|
return seismicCount;
|
|
}
|
|
}, {
|
|
key: 'assignSeismicCountItems',
|
|
value: function assignSeismicCountItems(seismicAnchorCounts) {
|
|
for (var subarray in seismicAnchorCounts) {
|
|
if (seismicAnchorCounts.hasOwnProperty(subarray)) {
|
|
var childId = parseInt(Object.keys(seismicAnchorCounts).indexOf(subarray)) + 2;
|
|
this.currentSeismicCountSelector.find('td:nth-child(' + childId + ')').text(seismicAnchorCounts[subarray]);
|
|
}
|
|
}
|
|
}
|
|
}]);
|
|
|
|
return SubarrayDisplay;
|
|
}();
|
|
|
|
exports.default = SubarrayDisplay;
|
|
|
|
/***/ }),
|
|
/* 10 */
|
|
/***/ (function(module, exports) {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
var AutoUpload = function AutoUpload() {
|
|
$("#file_upload").change(function (e) {
|
|
var ten_megabyte_max_upload = 10000000;
|
|
$("#error_container_txt").empty();
|
|
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
|
|
e.currentTarget.form.submit();
|
|
} else {
|
|
$("#error_container_txt").append('<span class="error_message centered_error" id="error_message_txt">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
|
|
}
|
|
});
|
|
|
|
$("#dxf_upload").change(function (e) {
|
|
var ten_megabyte_max_upload = 10000000;
|
|
$("#error_container_dxf").empty();
|
|
if (e.currentTarget.files[0].size < ten_megabyte_max_upload) {
|
|
e.currentTarget.form.submit();
|
|
} else {
|
|
$("#error_container_dxf").append('<span class="error_message centered_error" id="error_message_dxf">The system configuration you have uploaded is too large. Try splitting your design into two separate text files and run the tool twice.</span>');
|
|
}
|
|
});
|
|
};
|
|
|
|
exports.default = AutoUpload;
|
|
|
|
/***/ })
|
|
/******/ ]); |