/**
* @class PMUI
* Base class PMUI
* @singleton
*/
var PMUI = {},
getUsersOS;
/**
* Detect the users' OS
* @return {string}
*/
getUsersOS = function () {
var userAgent = window.navigator.userAgent,
platform = window.navigator.platform,
macOsPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
iosPlatforms = ['iPhone', 'iPad', 'iPod'],
os = null;
if (macOsPlatforms.indexOf(platform) !== -1) {
os = 'MacOS';
} else if (iosPlatforms.indexOf(platform) !== -1) {
os = 'iOS';
} else if (windowsPlatforms.indexOf(platform) !== -1) {
os = 'Windows';
} else if (/Android/.test(userAgent)) {
os = 'Android';
} else if (!os && /Linux/.test(platform)) {
os = 'Linux';
}
return os;
};
PMUI.version = '0.1.1';
PMUI.isCtrl = false;
PMUI.isAlt = false;
PMUI.isShift = false;
PMUI.metaKey = false;
PMUI.activeCanvas = null;
PMUI.currentContextMenu = null;
PMUI.keyCodeF5 = 116;
PMUI.keyDown = false;
PMUI.isDelete = false;
PMUI.OS = getUsersOS();
$(document).keydown(function (e) {
var elementSelected,
flowSelected;
if (PMUI.activeCanvas) {
elementSelected = PMUI.activeCanvas.getCurrentSelection();
flowSelected = PMUI.activeCanvas.getCurrentConnection();
switch (e.which) {
case 8: //BACKSPACE
if (PMUI.metaKey && PMUI.OS === "MacOS" && !PMUI.activeCanvas.readOnly &&
(elementSelected.asArray().length !== 0 || flowSelected !== null) && !PMUI.isDelete) {
if (PMUI.activeCanvas && !PMUI.activeCanvas.currentLabel) {
PMUI.isDelete = true;
PMUI.activeCanvas.removeElements();
}
}
break;
case 16: // SHIFT KEY
PMUI.isShift = true;
break;
case 17: // CTRL KEY
if (!PMUI.isAlt) {
PMUI.isCtrl = true;
} else if (PMUI.OS !== "MacOS") {
PMUI.isAlt = false;
PMUI.isCtrl = false;
}
break;
case 18: //ALT KEY
if (!PMUI.isCtrl) {
PMUI.isAlt = true;
} else if (PMUI.OS !== "MacOS") {
PMUI.isCtrl = false;
PMUI.isAlt = false;
}
break;
case 116: // F5 KEY
e.preventDefault();
window.location.reload(true);
break;
case 91: //meta key - window key - command key
PMUI.metaKey = true;
break;
case 37:
// Left
if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
e.preventDefault();
if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "LEFT")) {
PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'LEFT');
}
}
break;
case 38:
// Top
if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
e.preventDefault();
if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "TOP")) {
PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'TOP');
}
}
break;
case 39:
// Right
if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
e.preventDefault();
if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "RIGHT")) {
PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'RIGHT');
}
}
break;
case 40:
// Bottom
if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
e.preventDefault();
if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "BOTTOM")) {
PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'BOTTOM');
}
}
break;
case 67: // char 'c'
if (!PMUI.activeCanvas.currentLabel && PMUI.isCtrl) {
if (PMUI.activeCanvas.copyAndPaste) {
e.preventDefault();
PMUI.activeCanvas.copy();
}
}
break;
case 86: // char 'v'
if (!PMUI.activeCanvas.currentLabel && PMUI.isCtrl) {
if (PMUI.activeCanvas.copyAndPaste) {
e.preventDefault();
PMUI.activeCanvas.paste();
}
}
break;
case 90: // char 'z'
if ((PMUI.isCtrl && PMUI.OS !== "MacOS") || (PMUI.metaKey && PMUI.OS === "MacOS")
&& !PMUI.activeCanvas.readOnly) {
if (PMUI.isShift) {
// ctrl + shift + z (redo)
PMUI.activeCanvas.redo();
e.preventDefault();
} else {
// ctrl + z (undo)
PMUI.activeCanvas.undo();
e.preventDefault();
}
}
break;
case 46: //Delete Key - Don't go to default.
if (!PMUI.isCtrl && !PMUI.isAlt && !PMUI.metaKey && PMUI.activeCanvas && !PMUI.keyDown &&
!PMUI.activeCanvas.currentLabel && !PMUI.activeCanvas.readOnly && !PMUI.isDelete &&
(elementSelected.asArray().length !== 0 || flowSelected !== null)) {
PMUI.isDelete = true;
PMUI.activeCanvas.removeElements();
}
break;
default:
PMUI.keyDown = true;
break;
}
}
}).keypress(function (e) {
}).keyup(function (e) {
var current;
if (PMUI.activeCanvas) {
e.preventDefault();
switch (e.which) {
case 8: //BACKSPACE
if (PMUI.metaKey && PMUI.OS === "MacOS" && !PMUI.activeCanvas.readOnly) {
if (PMUI.activeCanvas && !PMUI.activeCanvas.currentLabel) {
PMUI.activeCanvas.removeElements();
}
}
break;
case 13: // ENTER
if (PMUI.activeCanvas && PMUI.activeCanvas.currentLabel) {
PMUI.activeCanvas.currentLabel.loseFocus();
}
PMUI.keyDown = false;
break;
case 91: // META KEY
PMUI.metaKey = false;
break;
case 16: // SHIFT KEY
PMUI.isShift = false;
break;
case 17: //CTRL KEY
PMUI.isCtrl = false;
break;
case 18: //ALT KEY
PMUI.isAlt = false;
break;
case 46:
//PMUI.isDelete = false;
break;
case 113: //F2 KEY
if (PMUI.activeCanvas &&
PMUI.activeCanvas.getCurrentSelection().getLast() !== null) {
//Run other code here when the element
// 'CurElement' is deleted
current = PMUI.activeCanvas.getCurrentSelection().getLast();
if (current !== undefined && current.label.html !== null) {
$(current.label.html).dblclick();
$(current.label.text.html).focus();
}
}
break;
default:
PMUI.keyDown = false;
break;
}
}
});
/**
* Extends the PMUI namespace with the given `path` and making a pointer
* from `path` to the given `class` (note that the `path`'s last token will be the pointer visible from outside
* the definition of the class).
*
* // e.g.
* // let's define a class inside an anonymous function
* // so that the global scope is not polluted
* (function () {
* var Canvas = function () {...};
*
* // let's extend the namespace
* PMUI.extendNamespace('PMUI.core.Panel', Canvas);
*
* }());
*
* // now PMDraw.draw.Canvas is a pointer to the class defined above
*
* Another example:
*
* // let's define a class inside an anonymous function
* // so that the global scope is not polluted
* (function () {
* var Shape = function () {...};
*
* // let's extend the namespace
* PMUI.extendNamespace('PMUI.draw.RandomName', Shape);
*
* }());
*
* // now PMUI.draw.RandomName is a pointer to the class Shape
* // note that this class can only be accessed through this pointer
*
* @param {string} path
* @param {Object} newClass
* @return {Object} The argument `newClass`
*/
PMUI.extendNamespace = function (path, newClass) {
var current,
pathArray,
extension,
i;
if (arguments.length !== 2) {
throw new Error("PMUI.extendNamespace(): method needs 2 arguments");
}
pathArray = path.split('.');
if (pathArray[0] === 'PMUI') {
pathArray = pathArray.slice(1);
}
current = PMUI;
// create the 'path' namespace
for (i = 0; i < pathArray.length - 1; i += 1) {
extension = pathArray[i];
if (typeof current[extension] === 'undefined') {
current[extension] = {};
}
current = current[extension];
}
extension = pathArray[pathArray.length - 1];
if (current[extension]) {
}
current[extension] = newClass;
return newClass;
};
/**
* Checks if `path` (a string separated with dots) is a valid path inside the `from` object if provided otherwise
* checks if `path` is a valid path inside the {@link PMUI} object,
* if so then returns a pointer to the object which is the last token of the string
*
* // e.g
* validPath('PMDraw.event.Keyboard.modifiers.alt'); // returns a pointer to alt
* validPath('modifiers.alt', PMUI.event.Keyboard); // returns a pointer to alt
*
* @param {string} path
* @param {Object} [from]
* @return {Object}
*/
PMUI.validPath = function (path, from) {
var pathArray = path.split('.'),
current,
extension,
i;
if (!from) {
if (pathArray[0] === 'PMUI') {
pathArray = pathArray.slice(1);
}
current = PMUI;
} else {
current = from;
}
for (i = 0; i < pathArray.length; i += 1) {
extension = pathArray[i];
if (!current[extension]) {
return null;
}
current = current[extension];
}
return current;
};
/**
* Creates an object whose [[Prototype]] link points to an object's prototype (the object is gathered using the
* argument `path` and it's the last token in the string), since `subClass` is given it will also mimic the
* creation of the property `constructor` and a pointer to its parent called `superclass`:
*
* // constructor pointer
* subClass.prototype.constructor === subClass // true
*
* // let's assume that superClass is the last token in the string 'path'
* subClass.superclass === superClass // true
*
* An example of use:
*
* (function () {
* var Core = function () {...};
*
* // extending the namespace
* PMDraw.extendNamespace('PMDraw.draw.Core', Core);
*
* }());
*
* (function () {
* var BehavioralElement = function () {...};
*
* // this class inherits from PMDraw.draw.Core
* PMDraw.inheritFrom('PMDraw.draw.Core', BehavioralElement);
*
* // extending the namespace
* PMDraw.extendNamespace('PMDraw.draw.BehavioralElement', BehavioralElement);
*
* }());
*
* @param {string} path
* @param {Object} subClass
* @return {Object}
*/
PMUI.inheritFrom = function (path, subClass) {
var current,
extension,
pathArray,
i,
prototype;
if (arguments.length !== 2) {
throw new Error("PMUI.inheritFrom(): method needs 2 arguments");
}
// function used to create an object whose [[Prototype]] link
// points to `object`
function clone(object) {
var F = function () {
};
F.prototype = object;
return new F();
}
pathArray = path.split('.');
if (pathArray[0] === 'PMUI') {
pathArray = pathArray.slice(1);
}
current = PMUI;
// find that class the 'path' namespace
for (i = 0; i < pathArray.length; i += 1) {
extension = pathArray[i];
if (typeof current[extension] === 'undefined') {
throw new Error("PMUI.inheritFrom(): object " + extension + " not found, full path was " + path);
}
current = current[extension];
}
prototype = clone(current.prototype);
prototype.constructor = subClass;
subClass.prototype = prototype;
subClass.superclass = current;
};
/**
* Generates 32-digits alphanumeric unique IDs
* @return {String} Alphanumeric 32-char unique string
*/
PMUI.generateUniqueId = function () {
var rand = function (min, max) {
// Returns a random number
//
// version: 1109.2015
// discuss at: http://phpjs.org/functions/rand
// + original by: Leslie Hoare
// + bugfixed by: Onno Marsman
// % note 1: See the commented out code below for a
// version which will work with our experimental
// (though probably unnecessary) srand() function)
// * example 1: rand(1, 1);
// * returns 1: 1
// fix for jsLint
// from: var argc = arguments.length;
if (typeof min === "undefined") {
min = 0;
}
if (typeof max === "undefined") {
max = 999999999;
}
return Math.floor(Math.random() * (max - min + 1)) + min;
},
uniqid = function (prefix, more_entropy) {
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + revised by: Kankrelune (http://www.webfaktory.info/)
// % note 1: Uses an internal counter (in php_js global) to avoid collision
// * example 1: uniqid();
// * returns 1: 'a30285b160c14'
// * example 2: uniqid('foo');
// * returns 2: 'fooa30285b1cd361'
// * example 3: uniqid('bar', true);
// * returns 3: 'bara20285b23dfd1.31879087'
if (typeof prefix === 'undefined') {
prefix = "";
}
var retId,
formatSeed = function (seed, reqWidth) {
var tempString = "",
i;
seed = parseInt(seed, 10).toString(16); // to hex str
if (reqWidth < seed.length) { // so long we split
return seed.slice(seed.length - reqWidth);
}
if (reqWidth > seed.length) { // so short we pad
// jsLint fix
tempString = "";
for (i = 0; i < 1 + (reqWidth - seed.length); i += 1) {
tempString += "0";
}
return tempString + seed;
}
return seed;
};
// BEGIN REDUNDANT
if (!this.php_js) {
this.php_js = {};
}
// END REDUNDANT
if (!this.php_js.uniqidSeed) { // init seed with big random int
this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
}
this.php_js.uniqidSeed += 1;
retId = prefix; // start with prefix, add current milliseconds hex string
retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
if (more_entropy) {
// for more entropy we add a float lower to 10
retId += (Math.random() * 10).toFixed(8).toString();
}
return retId;
},
sUID;
do {
sUID = uniqid(rand(0, 999999999), true);
sUID = sUID.replace('.', '0');
} while (sUID.length !== 32);
return sUID;
};
/**
* Creates and returns a HTML element
* @param {String} type The type for the element to be created, for example: div, span, p
* @return {HTMLElement} An HTML element
*/
PMUI.createHTMLElement = function (type) {
return document.createElement(type);
};
/**
* Calculates the text width usign a font family
* @param {String} text The text which width will be calculated
* @param {String} font The font family and size (expressed as the 'font' css properties)
* to be used to calculate the width
* @return {Number}
*/
PMUI.calculateWidth = function (text, font) {
var f = font || '12px arial',
$o = $(this.createHTMLElement('div')), w;
$o.text(text)
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
.appendTo($('body'));
w = $o.width();
$o.remove();
return w;
};
PMUI.emToPx = function (emUnits, context) {
var testDiv = PMUI.createHTMLElement('div'), theWidth, $div;
testDiv.style.margin = '0';
testDiv.style.padding = '0';
testDiv.style.position = 'absolute';
testDiv.style.display = 'inline-block';
testDiv.style.backgroundColor = 'none';
testDiv.style.width = emUnits + "em";
context = PMUI.isHTMLElement(context) ? context : document.body;
if (context !== document.body && !jQuery(context).parents('body').length) {
throw new Error("The context must be an HTML element appended to the DOM.");
}
context.appendChild(testDiv);
$div = jQuery(testDiv);
theWidth = $div.outerWidth();
$div.remove();
return theWidth;
};
/**
* Get PMUI Version.
* @return {String}
*/
PMUI.getVersion = function () {
return this.version;
};
/**
* Trigger events defined in the element
* @param {PMUI.core.Element} el Element associated with the event
* @param {String} eventName Event Name or alias
* @param {Object} scope Calling scope for the event
*/
PMUI.triggerEvent = function (el, eventName, scope) {
var scopeTrigger = scope || this;
if (el instanceof PMUI.core.Element) {
if (el.events[eventName] instanceof PMUI.event.Event) {
el.events[eventName].handler.call(scopeTrigger);
} else {
throw new Error('Event name is not registered int this element');
}
} else {
throw new Error('Current Element is not able to trigger events');
}
};
/**
* Sets the active canvas.
* @param {PMUI.draw.Canvas} canvas
* @chainable
*/
PMUI.setActiveCanvas = function (canvas) {
PMUI.activeCanvas = canvas;
return this;
};
/**
* Gets the active canvas
* @return {PMUI.draw.Canvas}
*/
PMUI.getActiveCanvas = function () {
return PMUI.activeCanvas;
};
/**
* Converts the coordinates `xCoord` and `yCoord` (assuming that xCoord and yCoord are pageCoordinates)
* or the page coordinates gathered from the object `e` if there is no `xCoord` or `yCoord` to
* `shape` coordinates, this new coordinate also considers the scroll done in the canvas
*
* // e.g.
* // Let's assume that:
* // the canvas coordinates are [100, 100] and that it has no scroll
* // the shape coordinates are [100, 100] (inside the canvas)
* // e is an object containing page.X = 300, page.Y = 300
* Utils.pageCoordinatesToShapeCoordinates(shape, e) // new Point(100, 100) respect to the shape
*
*
* @param {Object} shape
* @param {Object} e
* @param {number} [xCoord]
* @param {number} [yCoord]
* @return {PMUI.util.Point} a point relative to the canvas
*/
PMUI.pageCoordinatesToShapeCoordinates = function (shape, e, xCoord, yCoord, customShape) {
var coordinates,
x = (!xCoord) ? e.pageX : xCoord,
y = (!yCoord) ? e.pageY : yCoord,
orgX = (!xCoord) ? e.pageX : xCoord,
orgY = (!yCoord) ? e.pageY : yCoord,
canvas = shape.getCanvas();
x += canvas.getLeftScroll() - shape.getAbsoluteX() - canvas.getX();
y += canvas.getTopScroll() - shape.getAbsoluteY() - canvas.getY();
if (orgX === x) {
x -= parseInt(customShape.width / 2, 10);
}
if (orgY === y) {
y -= parseInt(customShape.height / 2, 10);
}
coordinates = new PMUI.util.Point(x, y);
return coordinates;
};
/**
* Validates the parameters that represents the coordinates for create a new shape
* @param {Object} canvas Represents the current canvas for the project
* @param {Object} e Represents the event that triggered on the event
* @param {Object} customShape Represents the object that will create a new element on the canvas
* @return {Boolean} flag After the review of the coordinates the method return true if the component
* will create on the canvas and false if not possible create it.
*/
PMUI.validCoordinatedToCreate = function (canvas, e, customShape) {
return function (canvas, e, customShape) {
return true;
};
};
/**
* Converts the coordinates of the `shape` to page coordinates, this method
* also considers the scroll of the canvas in the calculation
*
* // e.g.
* // Let's assume that:
* // the canvas coordinates are [100, 100] and that it has no scroll
* // the shape coordinates are [100, 100] (inside the canvas)
* Utils.getPointRelativeToPage(shape) // new Point(200, 200) respect to the page
*
* @param {Object} shape
* @return {PMUI.util.Point} a point relative to the page
*/
PMUI.getPointRelativeToPage = function (shape) {
var canvas = shape.getCanvas(),
x = shape.absoluteX + canvas.getX() - canvas.getLeftScroll() +
shape.zoomWidth / 2,
y = shape.absoluteY + canvas.getY() - canvas.getTopScroll() +
shape.zoomHeight / 2;
return new PMUI.util.Point(x, y);
};
PMUI.json2xml = function (o) {
var len,
toXML,
safeXMLValue,
a;
len = function (o) {
var n = 0,
a;
for (a in o) {
if (o.hasOwnProperty(a)) {
n += 1;
}
}
return n;
};
toXML = function (tag, o) {
var a,
b,
doc = '<' + tag,
sw = false,
i;
if (typeof o === 'undefined' || o === null) {
doc += '/>';
return doc;
}
if (typeof o !== 'object') {
doc += '>' + safeXMLValue(o) + '' + tag + '>';
return doc;
}
if (o.constructor === Object) {
for (a in o) {
if (o.hasOwnProperty(a)) {
if (a.charAt(0) === '@') {
if (typeof o[a] !== 'object') {
doc += ' ' + a.substring(1) + '="' + o[a] + '"';
delete o[a];
} else {
throw new Error((typeof o[a])
+ ' being attribute is not supported.');
}
}
}
}
if (len(o) === 0) {
doc += '/>';
sw = true;
} else {
doc += '>';
}
if (sw) {
return doc;
}
if (typeof o['#text'] !== 'undefined') {
if (typeof o['#text'] !== 'object') {
doc += o['#text'];
delete o['#text'];
} else {
throw new Error((typeof o['#text'])
+ ' being #text is not supported.');
}
}
for (b in o) {
if (o.hasOwnProperty(b)) {
if (o[b].constructor === Array) {
for (i = 0; i < o[b].length; i += 1) {
if (typeof o[b][i] !== 'object'
|| o[b][i].constructor === Object) {
doc += toXML(b, o[b][i]);
} else {
throw new Error((typeof o[b][i])
+ ' is not supported.');
}
}
} else if (o[b].constructor === Object
|| typeof o[b] !== 'object') {
doc += toXML(b, o[b]);
} else {
throw new Error((typeof o[b]) + ' is not supported.');
}
}
}
doc += '' + tag + '>';
return doc;
}
};
safeXMLValue = function (value) {
var s = value.toString();
s = s.replace(/\&/g, '&');
s = s.replace(/\"/g, '"');
s = s.replace(//g, '>');
return s;
};
if (typeof o === 'object' && o.constructor === Object && len(o) === 1) {
for (a in o) {
if (o.hasOwnProperty(a)) {
return toXML(a, o[a]);
}
}
}
};
PMUI.linkToPMUIObject = function (element, pmuiObject) {
if (this.isHTMLElement(element) && pmuiObject instanceof PMUI.core.Element) {
jQuery(element).data('pmui', pmuiObject);
} else {
throw new Error("PMUI.linkToPMUIObject(): the first parameter must be a HTMLElement and the second one must be "
+ "an instance of PMUI.core.Element.");
}
};
PMUI.getPMUIObject = function (element) {
if (this.isHTMLElement(element)) {
return jQuery(element).data("pmui");
}
throw new Error("PMUI.getPMUIObject(): the parameter must be a HTMLElement.");
};
PMUI.isHTMLElement = function (obj) {
try {
//Using W3 DOM2 (works for FF, Opera and Chrom)
return obj instanceof HTMLElement;
}
catch (e) {
//Browsers not supporting W3 DOM2 don't have HTMLElement and
//an exception is thrown and we end up here. Testing some
//properties that all elements have. (works on IE7)
return (typeof obj === "object") &&
(obj.nodeType === 1) && (typeof obj.style === "object") &&
(typeof obj.ownerDocument === "object");
}
};
PMUI.removeCurrentMenu = function () {
if (PMUI.currentContextMenu && PMUI.currentContextMenu.displayed) {
PMUI.currentContextMenu.hide();
PMUI.currentContextMenu = null;
}
};
PMUI.init = function () {
String.prototype.translate = PMUI.lang.I18N.translate;
String.prototype.translateContext = PMUI.lang.I18N.translateContext;
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (elt /*, from*/) {
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from += 1) {
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
return this;
};
PMUI.loadLanguage = function (data, lang, loaded) {
PMUI.lang.I18N.loadLanguage(data, lang, loaded);
return this;
};
PMUI.setCurrentLanguage = function (lang) {
PMUI.lang.I18N.setCurrentLanguage(lang);
return this;
};
PMUI.setDefaultLanguage = function (lang) {
PMUI.lang.I18N.setDefaultLanguage(lang);
return this;
};
PMUI.convertDateFormat = function (dateInput) {
//TODO Implement this method considering locate options
return dateInput;
};
PMUI.castValue = function (value, format) {
try {
switch (format) {
case 'string':
out = String(value);
break;
case 'number':
if (value != '') {
out = Number(value);
} else {
out = 'NaN';
}
break;
case 'date':
out = PMUI.convertDateFormat(value);
break;
case 'boolean':
out = Boolean(value);
break;
default:
out = value;
}
return out;
} catch (e) {
throw new Error("The value cannot be showed in '" + format + "' format");
}
};
/**
* validates that the items do not leave the parent container
* @param elements array of items selecteds
* @param direction string of cursor key
* @returns {boolean}
*/
PMUI.getCoordinatesElement = function (elements, direction) {
var element,
factorPixel,
swError,
factorZoom,
ParentPoints,
ShapePoints,
limitTop,
limitRight,
limitBottom,
limitLeft;
swError = false;
if (elements.length > 0) {
if (elements[0].parent.parent !== null) {
element = elements[0];
factorPixel = [4, 4, 4, 2];
factorZoom = element.getCanvas().getZoomFactor();
ParentPoints = PMUI.getCoordenatesParent(element.parent, factorZoom);
ShapePoints = PMUI.getAbsoluteShape(elements, factorZoom);
limitTop = ParentPoints.PInit.getY() + (factorPixel[0] * factorZoom);
limitRight = ParentPoints.PFinal.getX() - (factorPixel[1] * factorZoom);
limitBottom = ParentPoints.PFinal.getY() - (factorPixel[2] * factorZoom);
limitLeft = ParentPoints.PInit.getX() + (factorPixel[3] * factorZoom);
switch (direction) {
case 'TOP':
if (limitTop > ShapePoints.PInit.getY()) {
swError = true;
}
break;
case 'RIGHT':
if (limitRight < ShapePoints.PFinal.getX()) {
swError = true;
}
break;
case 'BOTTOM':
if (limitBottom < ShapePoints.PFinal.getY()) {
swError = true;
}
break;
case 'LEFT':
if (limitLeft > ShapePoints.PInit.getX()) {
swError = true;
}
break;
}
}
}
return swError;
};
/**
* Get the parent coordenates
* @param parent Object parent of the item selected
* @param factorZoom
* @returns {Object of Objects Point}
*/
PMUI.getCoordenatesParent = function (parent, factorZoom) {
var coordenatesParent,
xInit,
yInit,
xFinal,
yFinal,
pointInit,
pointFinal,
headParent;
headParent = 0;
if (parent.type === "PMPool") {
headParent = parent.headLineCoord * factorZoom;
}
xInit = parent.getAbsoluteX() + headParent;
yInit = parent.getAbsoluteY();
xFinal = parent.getAbsoluteX() + (parent.getWidth() * factorZoom);
yFinal = parent.getAbsoluteY() + (parent.getHeight() * factorZoom);
pointInit = new PMUI.util.Point(xInit, yInit);
pointFinal = new PMUI.util.Point(xFinal, yFinal);
coordenatesParent = {
"PInit": pointInit,
"PFinal": pointFinal
};
return coordenatesParent;
};
/**
* Get coordenates min y max of the items selecteds
* @param shapes Array of items selecteds
* @param factorZoom
* @returns {Object of Objects Point}
*/
PMUI.getAbsoluteShape = function (shapes, factorZoom) {
var coordenatesShapeAux,
coordenatesShape,
arrayShapesAux = [],
pointShapeInit,
pointShapeFinal,
shape,
xFinalAux,
yFinalAux,
i;
for (i = 0; i < shapes.length; i += 1) {
shape = shapes[i];
xFinalAux = shape.getAbsoluteX() + (shape.getWidth() * factorZoom);
yFinalAux = shape.getAbsoluteY() + (shape.getHeight() * factorZoom);
pointShapeInit = new PMUI.util.Point(shape.getAbsoluteX(), shape.getAbsoluteY());
pointShapeFinal = new PMUI.util.Point(xFinalAux, yFinalAux);
coordenatesShapeAux = {
"PInit": pointShapeInit,
"PFinal": pointShapeFinal
};
arrayShapesAux.push(coordenatesShapeAux);
}
coordenatesShape = PMUI.getMinMaxCoordenates(arrayShapesAux);
return coordenatesShape;
};
/**
* Get the Min y Max coordenates of a array of Objects Point
* @param coordenates array of Objects Point
* @returns {Object of Objects Point}
*/
PMUI.getMinMaxCoordenates = function (coordenates) {
var objectCoordenates,
xInit,
yInit,
xFinal,
yFinal,
j;
xInit = coordenates[0].PInit.getX();
yInit = coordenates[0].PInit.getY();
xFinal = coordenates[0].PFinal.getX();
yFinal = coordenates[0].PFinal.getY();
for (j = 0; j < coordenates.length; j += 1) {
if (coordenates[j].PInit.getX() < xInit) {
xInit = coordenates[j].PInit.getX();
}
if (coordenates[j].PInit.getY() < yInit) {
yInit = coordenates[j].PInit.getY();
}
if (coordenates[j].PFinal.getX() > xFinal) {
xFinal = coordenates[j].PFinal.getX();
}
if (coordenates[j].PFinal.getY() > yFinal) {
yFinal = coordenates[j].PFinal.getY();
}
}
objectCoordenates = {
"PInit": new PMUI.util.Point(xInit, yInit),
"PFinal": new PMUI.util.Point(xFinal, yFinal)
};
return objectCoordenates;
};
PMUI.isInDOM = function (html) {
var targetHTML = html instanceof PMUI.core.Element ? html.html : html,
aux;
if (targetHTML === null) {
return false;
} else if (!PMUI.isHTMLElement(targetHTML)) {
throw new Error('isInDOM(): invalid parameter, only HTML or instance of PMUI.core.Element are allowed.');
}
aux = jQuery(html).parents('html')[0];
return !!aux && aux.parentNode === document;
};
String.prototype.capitalize = function () {
return this.toLowerCase().replace(/(^|\s)([a-z])/g, function (m, p1, p2) {
return p1 + p2.toUpperCase();
});
};
if (typeof exports !== "undefined") {
module.exports = PMUI;
}
(function () {
/**
* @class PMUI.util.ArrayList
* Construct a List similar to Java's ArrayList that encapsulates methods for
* making a list that supports operations like get, insert and others.
*
* some examples:
* var item,
* arrayList = new ArrayList();
* arrayList.getSize() // 0
* arrayList.insert({ // insert an object
* id: 100,
* width: 100,
* height: 100
* });
* arrayList.getSize(); // 1
* arrayList.asArray(); // [{id : 100, ...}]
* item = arrayList.find('id', 100); // finds the first element with an id that equals 100
* arrayList.remove(item); // remove item from the arrayList
* arrayList.getSize(); // 0
* arrayList.isEmpty(); // true because the arrayList has no elements
*
* @constructor Returns an instance of the class ArrayList
*/
var ArrayList = function () {
/**
* The elements of the arrayList
* @property {Array}
* @private
*/
var elements = [],
/**
* The size of the array
* @property {number} [size=0]
* @private
*/
size = 0,
index,
i;
return {
/**
* The ID of this ArrayList is generated using the function Math.random
* @property {number} id
*/
id: Math.random(),
/**
* Gets an element in the specified index or undefined if the index
* is not present in the array
* @param {number} index
* @returns {Object / undefined}
*/
get: function (index) {
return elements[index];
},
/**
* Inserts an element at the end of the list
* @param {Object} item
* @chainable
*/
insert: function (item) {
elements[size] = item;
size += 1;
return this;
},
/**
* Inserts an element in a specific position
* @param {Object} item
* @chainable
*/
insertAt: function (item, index) {
elements.splice(index, 0, item);
size = elements.length;
return this;
},
/**
* Removes an item from the list
* @param {Object} item
* @return {boolean}
*/
remove: function (item) {
index = this.indexOf(item);
if (index === -1) {
return false;
}
//swap(elements[index], elements[size-1]);
size -= 1;
elements.splice(index, 1);
return true;
},
/**
* Gets the length of the list
* @return {number}
*/
getSize: function () {
return size;
},
/**
* Returns true if the list is empty
* @returns {boolean}
*/
isEmpty: function () {
return size === 0;
},
/**
* Returns the first occurrence of an element, if the element is not
* contained in the list then returns -1
* @param {Object} item
* @return {number}
*/
indexOf: function (item) {
for (i = 0; i < size; i += 1) {
if (item === elements[i]) {
return i;
}
}
return -1;
},
/**
* Returns the the first object of the list that has the
* specified attribute with the specified value
* if the object is not found it returns undefined
* @param {string} attribute
* @param {string} value
* @return {Object / undefined}
*/
find: function (attribute, value) {
var i,
current;
for (i = 0; i < elements.length; i += 1) {
current = elements[i];
if (current[attribute] === value) {
return current;
}
}
return undefined;
},
/**
* Returns true if the list contains the item and false otherwise
* @param {Object} item
* @return {boolean}
*/
contains: function (item) {
if (this.indexOf(item) !== -1) {
return true;
}
return false;
},
/**
* Sorts the list using compFunction if possible, if no compFunction
* is passed as an parameter then a default sorting method will be used. This default method will sort in
* ascending order.
* @param {Function} [compFunction] The criteria function used to find out the position for the elements in
* the array list. This function will receive two parameters, each one will be an element from the array
* list, the function will compare those elements and it must return:
*
* - 1, if the first element must be before the second element.
* - -1, if the second element must be before the first element.
* - 0, if the current situation doesn't met any of the two situations above. In this case both elements
* can be evaluated as they had the same value. For example, in an array list of numbers, when you are
* trying to apply a lineal sorting (ascending/descending) in a array list of numbers, if the array sorting
* function finds two elements with the value 3 they should be evaluated returning 0, since both values are
* the same.
*
* IMPORTANT NOTE: for a correct performance the sent parameter must return at least two of the values
* listed above, if it doesn't the function can produce an infinite loop and thus an error.
* @return {boolean}
*/
// sort : function (compFunction) {
// var compFunction = compFunction || function(a, b) {
// if(a < b) {
// return 1;
// } else if(a > b) {
// return -1;
// } else {
// return 0;
// }
// }, swap = function (items, firstIndex, secondIndex){
// var temp = items[firstIndex];
// items[firstIndex] = items[secondIndex];
// items[secondIndex] = temp;
// }, partition = function(items, left, right) {
// var pivot = items[Math.floor((right + left) / 2)],
// i = left,
// j = right;
// while (i <= j) {
// while (compFunction(items[i], pivot) > 0) {
// i++;
// }
// while (compFunction(items[j], pivot) < 0) {
// j--;
// }
// if (i <= j) {
// swap(items, i, j);
// i++;
// j--;
// }
// }
// return i;
// }, quickSort = function (items, left, right) {
// var index;
// if (items.length > 1) {
// index = partition(items, left, right);
// if (left < index - 1) {
// quickSort(items, left, index - 1);
// }
// if (index < right) {
// quickSort(items, index, right);
// }
// }
// return items;
// };
//
// return quickSort(elements, 0, size - 1);
// },
sort: function (compFunction) {
var returnValue = false;
if (compFunction) {
elements.sort(compFunction);
returnValue = true;
}
return returnValue;
},
/**
* Bubble sort function in array
* @param {*} compFunction
* @return {Array}
*/
bubbleSort: function (compFunction) {
var inputArr = elements,
i,
tmp,
len = inputArr.length,
swapped;
do {
swapped = false;
for (i = 0; i < len; i++) {
if (inputArr[i] && inputArr[i + 1] && compFunction(inputArr[i], inputArr[i + 1])) {
tmp = inputArr[i];
inputArr[i] = inputArr[i + 1];
inputArr[i + 1] = tmp;
swapped = true;
}
}
} while (swapped);
elements = inputArr;
return elements;
},
/**
* Returns the list as an array
* @return {Array}
*/
asArray: function () {
return elements.slice(0);
},
/**
* Swaps the position of two elements
* @chainable
*/
swap: function (index1, index2) {
var aux;
if (index1 < size && index1 >= 0 && index2 < size && index2 >= 0) {
aux = elements[index1];
elements[index1] = elements[index2];
elements[index2] = aux;
}
return this;
},
/**
* Returns the first element of the list
* @return {Object}
*/
getFirst: function () {
return elements[0];
},
/**
* Returns the last element of the list
* @return {Object}
*/
getLast: function () {
return elements[size - 1];
},
/**
* Returns the last element of the list and deletes it from the list
* @return {Object}
*/
popLast: function () {
var lastElement;
size -= 1;
lastElement = elements[size];
elements.splice(size, 1);
return lastElement;
},
/**
* Returns an array with the objects that determine the minimum size
* the container should have
* The array values are in this order TOP, RIGHT, BOTTOM AND LEFT
* @return {Array}
*/
getDimensionLimit: function () {
var result = [100000, -1, -1, 100000],
objects = [undefined, undefined, undefined, undefined];
//number of pixels we want the inner shapes to be
//apart from the border
for (i = 0; i < size; i += 1) {
if (result[0] > elements[i].y) {
result[0] = elements[i].y;
objects[0] = elements[i];
}
if (result[1] < elements[i].x + elements[i].width) {
result[1] = elements[i].x + elements[i].width;
objects[1] = elements[i];
}
if (result[2] < elements[i].y + elements[i].height) {
result[2] = elements[i].y + elements[i].height;
objects[2] = elements[i];
}
if (result[3] > elements[i].x) {
result[3] = elements[i].x;
objects[3] = elements[i];
}
}
return result;
},
/**
* Clears the content of the arrayList
* @chainable
*/
clear: function () {
if (size !== 0) {
elements = [];
size = 0;
}
return this;
},
/**
* Sets the elements for the object.
* @param {Array|null} items Array with the items to set.
* @chainable
*/
set: function (items) {
if (!(items === null || jQuery.isArray(items))) {
throw new Error("set(): The parameter must be an array or null.");
}
elements = (items && items.slice(0)) || [];
size = elements.length;
return this;
}
};
};
PMUI.extendNamespace('PMUI.util.ArrayList', ArrayList);
if (typeof exports !== "undefined") {
module.exports = ArrayList;
}
}());
(function () {
/**
* @class PMUI.util.Style
* Class that represent the style of a an object.
*
* // i.e
* // Let's assume that 'shape' is a CustomShape
* var style = new Style({
* cssClasses: [
* 'sprite-class', 'marker-class', ...
* ],
* cssProperties: {
* border: 1px solid black,
* background-color: grey,
* ...
* },
* belongsTo: shape
* })
*
* @constructor Creates a new instance of this class
* @param {Object} options
* @cfg {Array} [cssClasses=[]] the classes that `this.belongsTo` has
* @cfg {Object} [cssProperties={}] the css properties that `this.belongsTo` has
* @cfg {Object} [belongsTo=null] a pointer to the owner of this instance
*/
var Style = function (options) {
/**
* JSON Object used to map each of the css properties of the object,
* this object has the same syntax as the object passed to jQuery.css()
* cssProperties: {
* background-color: [value],
* border: [value],
* ...
* }
* @property {Object}
*/
this.cssProperties = null;
/**
* Array of all the classes of this object
* cssClasses = [
* 'class_1',
* 'class_2',
* ...
* ]
* @property {Array}
*/
this.cssClasses = null;
/**
* Pointer to the object to whom this style belongs to
* @property {Object}
*/
this.belongsTo = null;
Style.prototype.initObject.call(this, options);
};
/**
* The type of this class
* @property {String}
*/
Style.prototype.type = "Style";
/**
* Constant for the max z-index
* @property {number} [MAX_ZINDEX=100]
*/
Style.MAX_ZINDEX = 100;
/**
* Instance initializer which uses options to extend the config options to
* initialize the instance
* @private
* @param {Object} options
*/
Style.prototype.initObject = function (options) {
var defaults = {
cssClasses: [],
cssProperties: {},
belongsTo: null
};
jQuery.extend(true, defaults, options);
this.cssClasses = defaults.cssClasses;
this.cssProperties = defaults.cssProperties;
this.belongsTo = defaults.belongsTo;
};
/**
* Applies cssProperties and cssClasses to `this.belongsTo`
* @chainable
*/
Style.prototype.applyStyle = function () {
var i,
t,
class_i;
if (!this.belongsTo.html) {
throw new Error("applyStyle(): can't apply style to an" +
" object with no html.");
}
// apply the cssProperties
jQuery(this.belongsTo.html).css(this.cssProperties);
//adding default classes
t = this.belongsTo.type.toLowerCase();
if (this.cssClasses.indexOf('pmui-' + t) === -1) {
this.cssClasses.unshift('pmui-' + t);
}
if (this.cssClasses.indexOf('pmui') === -1) {
this.cssClasses.unshift('pmui');
}
// apply saved classes
for (i = 0; i < this.cssClasses.length; i += 1) {
class_i = this.cssClasses[i];
if (!$(this.belongsTo.html).hasClass(class_i)) {
jQuery(this.belongsTo.html).addClass(class_i);
}
}
return this;
};
Style.prototype.unapplyStyle = function () {
var t,
property;
if (!this.belongsTo.html) {
throw new Error("unapplyStyle(): can't unapply style to an object with no html.");
}
t = this.belongsTo.type.toLowerCase();
jQuery(this.belongsTo.html).removeClass("pmui-" + t);
for (property in this.cssProperties) {
jQuery(this.belongsTo.html).css(property, "");
}
};
/**
* Extends the property `cssProperties` with a new object and also applies those new properties
* @param {Object} properties
* @chainable
*/
Style.prototype.addProperties = function (properties) {
jQuery.extend(true, this.cssProperties, properties);
jQuery(this.belongsTo && this.belongsTo.html).css(properties);
return this;
};
/**
* Gets a property from `this.cssProperties` using jQuery or `window.getComputedStyle()`
* @param {String} property
* @return {String}
*/
Style.prototype.getProperty = function (property) {
return this.cssProperties[property] ||
jQuery(this.belongsTo.html).css(property) ||
(this.belongsTo.html && window.getComputedStyle(this.belongsTo.html, null)
.getPropertyValue(property)) || "";
};
/**
* Returns all the style's css properties set explicitly.
* @return {Object} An object literal with the properties.
*/
Style.prototype.getProperties = function () {
return this.cssProperties;
};
/**
* Removes ´properties´ from the ´this.cssProperties´, also disables those properties from
* the HTMLElement
* @param {Array} properties An array in which each element is th name of the cssProperty to be removed.
* @chainable
*/
Style.prototype.removeProperties = function (properties) {
var property,
i;
for (i = 0; i < properties.length; i += 1) {
property = properties[i];
if (this.cssProperties.hasOwnProperty(property)) { // JS Code Convention
jQuery(this.belongsTo.html).css(property, ""); // reset inline style
delete this.cssProperties[property];
}
}
return this;
};
/**
* Removes all properties from the object.
* @chainable
*/
Style.prototype.removeAllProperties = function () {
var key;
if (this.belongsTo) {
for (key in this.cssProperties) {
jQuery(this.belongsTo.html).css(key, "");
}
}
this.cssProperties = {};
return this;
};
/**
* Adds new classes to ´this.cssClasses´ array
* @param {Array} cssClasses
* @chainable
*/
Style.prototype.addClasses = function (cssClasses) {
var i,
cssClass;
if (cssClasses && cssClasses instanceof Array) {
for (i = 0; i < cssClasses.length; i += 1) {
cssClass = cssClasses[i];
if (typeof cssClass === "string") {
if (this.cssClasses.indexOf(cssClass) === -1) {
this.cssClasses.push(cssClass);
jQuery(this.belongsTo && this.belongsTo.html).addClass(cssClass);
}
} else {
throw new Error("addClasses(): array element is not of type string");
}
}
} else {
throw new Error("addClasses(): parameter must be of type Array");
}
return this;
};
/**
* Removes classes from ´this.cssClasses´ array, also removes those classes from
* the HTMLElement
* @param {Array} cssClasses
* @chainable
*/
Style.prototype.removeClasses = function (cssClasses) {
var i,
index,
cssClass;
if (cssClasses && cssClasses instanceof Array) {
for (i = 0; i < cssClasses.length; i += 1) {
cssClass = cssClasses[i];
if (typeof cssClass === "string") {
index = this.cssClasses.indexOf(cssClass);
if (index !== -1) {
jQuery(this.belongsTo.html).removeClass(this.cssClasses[index]);
this.cssClasses.splice(index, 1);
}
} else {
throw new Error("removeClasses(): array element is not of " +
"type string");
}
}
} else {
throw new Error("removeClasses(): parameter must be of type Array");
}
return this;
};
/**
* Removes all the classes from ´this.cssClasses´ array
* @param {Array} cssClasses
* @chainable
*/
Style.prototype.removeAllClasses = function () {
while (this.cssClasses.length) {
jQuery(this.belongsTo && this.belongsTo.html).removeClass(this.cssClasses.pop());
}
return this;
};
/**
* Checks if the class is a class stored in ´this.cssClasses´
* @param cssClass
* @return {boolean}
*/
Style.prototype.containsClass = function (cssClass) {
return this.cssClasses.indexOf(cssClass) !== -1;
};
/**
* Returns an array with all the classes of ´this.belongsTo´
* @return {Array}
*/
Style.prototype.getClasses = function () {
return this.cssClasses.slice(0);
};
/**
* Clears all the css properties and classes.
* @chainable
*/
Style.prototype.clear = function () {
return this.removeAllClasses().removeAllProperties();
}
/**
* Serializes this instance
* @return {Object}
* @return {Array} return.cssClasses
*/
Style.prototype.stringify = function () {
return {
cssClasses: this.cssClasses
};
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Style;
}
PMUI.extendNamespace('PMUI.util.Style', Style);
}());
(function () {
/**
* @class PMUI.util.Factory
* This class encapsulate the way to construct object using the product definition inside
*
* @constructor
* This method creates a new instance of this object
* @param {Object} settings Constructor setiings
*/
var Factory = function (settings) {
/**
* Defines the products can be make by the factory
* @type {Object}
*/
this.products = null;
/**
* Defines the default product to make
* @type {String}
*/
this.defaultProduct = null;
Factory.prototype.init.call(this, settings);
};
/**
* Defines the object type
* @type {String}
*/
Factory.prototype.type = "Factory";
/**
* Defines the object family
* @type {String}
*/
Factory.prototype.family = "Factory";
/**
* Initializes the object with the default values
* @param {Object} options Contructor options
*/
Factory.prototype.init = function (options) {
var defaults;
if (!options) {
options = {};
}
defaults = {
defaultProduct: options.defaultProduct || "element",
products: options.products || {"element": PMUI.core.Element}
};
this.setDefaultProduct(defaults.defaultProduct)
.setProducts(defaults.products);
};
/**
* Sets the default product property
* @param {String} def Default value
*/
Factory.prototype.setDefaultProduct = function (def) {
this.defaultProduct = def;
return this;
};
/**
* Sets the product object
* @param {Object} products Products object
*/
Factory.prototype.setProducts = function (products) {
this.products = products;
return this;
};
/**
* Removes a product from the factory.
* @param {String|Object} product The product to be removed, it can be:
*
* - a string: the product referenced with that pmType will be removed from factory.
* - a object: the constructor, all the products with that constructor will be removed.
* @return {[type]} [description]
*/
Factory.prototype.removeProduct = function (product) {
var key,
products = this.products;
if (typeof product === 'string') {
delete products[product];
} else {
for (key in products) {
if (products[key] === product) {
delete products[key];
}
}
}
return this;
};
/**
* Removes all the productrs from the factory.
* @chainable
*/
Factory.prototype.clearProducts = function () {
var key;
for (key in this.products) {
this.removeProduct(key);
}
return this;
};
/**
* Register a new product into the products object
* @param {String} name Product type
* @param {Object} classObj Product Class
* @chainable
*/
Factory.prototype.register = function (name, classObj) {
var aux = this.products || {};
aux[name] = classObj;
this.products = aux;
return this;
};
/**
* Retuns a new instance (product)
* @param {String} type Product type
* @param {Object} options Settings object
* @return {Object} Instance of the object
*/
Factory.prototype.build = function (type, options) {
var Constructor,
instance;
if (this.isValidName(type)) {
Constructor = this.products[type];
instance = new Constructor(options);
} else {
throw new Error('The type "' + type + '" has not valid constructor or is undefined.');
}
return instance;
};
/**
* Retuns true if the type is valid into the products object
* @param {String} name Product Name
* @return {Boolean}
*/
Factory.prototype.isValidName = function (name) {
var test = this.products[name];
return !!test;
};
/**
* Returns true if the class input is instance of one class into the product object
* @param {Object} className [description]
* @return {Boolean} [description]
*/
Factory.prototype.isValidClass = function (className) {
var valid = false;
jQuery.each(this.products, function (type, classProduct) {
if (className instanceof classProduct) {
valid = true;
}
});
return valid;
};
/**
* Comprobes the obj in and returns the instance of the object
* @param {Object} obj Input can be an isntance or an object with the pmType property or simply a JSON
* @return {Object} Returns an instance made from the products object.
*/
Factory.prototype.make = function (obj) {
var product,
productType = obj.pmType || '';
if (this.isValidClass(obj)) {
product = obj;
} else if (this.isValidName(productType)) {
product = this.build.call(this, productType, obj);
} else {
product = this.build.call(this, this.defaultProduct, obj);
}
return product;
};
//Create a namespace for Factory class
PMUI.extendNamespace('PMUI.util.Factory', Factory);
// Publish to NodeJS environment
if (typeof exports !== 'undefined') {
module.exports = Factory;
}
}());
(function () {
/**
* @class PMUI.util.Color
* This class holds the representation and operations of RGBa representation of color,
* it's very useful if we want to save color constants as an instance and later get the representation
* in CSS.
*
* //e.g.
* var color = new PMUI.util.Color(
* 128, // red
* 128, // green
* 128, // blue
* 1 // opacity
* )
*
* @constructor Creates an instance of this class.
* @param {number} red
* @param {number} green
* @param {number} blue
* @param {number} opacity
* @return {PMUI.util.Color}
*/
var Color = function (red, green, blue, opacity) {
/**
* Red value of the RGB Color
* @property {number} [red=0]
*/
this.red = (!red) ? 0 : red;
/**
* Green value of the RGB Color
* @property {number} [green=0]
*/
this.green = (!green) ? 0 : green;
/**
* Blue value of the RGB Color
* @property {number} [blue=0]
*/
this.blue = (!blue) ? 0 : blue;
/**
* Opacity of the RGB Color
* @property {number} [opacity=1]
*/
this.opacity = (!opacity) ? 1 : opacity;
};
/**
* Type of this class
* @property {String}
*/
Color.prototype.type = "Color";
/**
* Constant for the color grey
* @property {PMUI.util.Color} [GREY=new Color(192, 192, 192, 1)]
*/
Color.GREY = new Color(192, 192, 192, 1);
/**
* Returns the red value of the RGB Color
* @returns {number}
*/
Color.prototype.getRed = function () {
return this.red;
};
/**
* Returns the green value of the RGB Color
* @returns {number}
*/
Color.prototype.getGreen = function () {
return this.green;
};
/**
* Returns the blue value of the RGB Color
* @returns {number}
*/
Color.prototype.getBlue = function () {
return this.blue;
};
/**
* Returns the opacity of the RGB Color
* @returns {number}
*/
Color.prototype.getOpacity = function () {
return this.opacity;
};
/**
* Sets the red value of the RGB Color
* @param {number} newRed
* @chainable
*/
Color.prototype.setRed = function (newRed) {
if (typeof newRed === "number" && newRed >= 0 && newRed <= 255) {
this.red = newRed;
}
return this;
};
/**
* Sets the green value of the RGB Color
* @param {number} newRed
* @chainable
*/
Color.prototype.setGreen = function (newGreen) {
if (typeof newGreen === "number" && newGreen >= 0 && newGreen <= 255) {
this.green = newGreen;
}
return this;
};
/**
* Sets the blue value of the RGB Color
* @param {number} newBlue
* @chainable
*/
Color.prototype.setBlue = function (newBlue) {
if (typeof newBlue === "number" && newBlue >= 0 && newBlue <= 255) {
this.blue = newBlue;
}
return this;
};
/**
* Sets the opacity of the RGB Color
* @param {number} newOpacity
* @chainable
*/
Color.prototype.setOpacity = function (newOpacity) {
if (typeof newOpacity === "number" && newOpacity >= 0 && newOpacity <= 255) {
this.opacity = newOpacity;
}
return this;
};
/**
* Returns the css representation of the RGB color
* //e.g.
* var color = new PMUI.util.Color(10, 20, 30, 0.1);
* color.getCSS(); // "rgba(10, 20, 30, 0.1)"
* @returns {String}
*/
Color.prototype.getCSS = function () {
var css = "rgba(" + this.red + "," + this.green + "," + this.blue +
"," + this.opacity + ")";
return css;
};
PMUI.extendNamespace('PMUI.util.Color', Color);
}());
(function () {
/**
* @class PMUI.util.Point
* Class to represent points in the PMDraw library
*
* // e.g.
* var p = new PMUI.util.Point(100, 100);
*
* @constructor Creates an instance of this class
* @param {number} xCoordinate x-coordinate of the point
* @param {number} yCoordinate y-coordinate of the point
* @return {PMUI.util.Point}
*/
var Point = function (xCoordinate, yCoordinate) {
/**
* x coordinate of the point in the plane
*/
this.x = xCoordinate;
/**
* y coordinate of the point in the plane
*/
this.y = yCoordinate;
};
/**
* Type of this class
* @property {String}
*/
Point.prototype.type = "Point";
/**
* Returns the X coordinate
* @property {number}
**/
Point.prototype.getX = function () {
return this.x;
};
/**
* Returns the Y coordinate
* @property {number}
**/
Point.prototype.getY = function () {
return this.y;
};
/**
* Adds `other` point to `this` point and returns a new point with those coordinates.
*
* // e.g.
* var p1 = new PMUI.util.Point(3, 5),
* p2 = new PMUI.util.Point(2, 3);
* p1.add(p2); // new Point(5, 8)
*
* @param {PMUI.util.Point} other Point to be added to the current point
* @returns {PMUI.util.Point}
*/
Point.prototype.add = function (other) {
return new Point(this.x + other.x, this.y + other.y);
};
/**
* Subtracts the other point to the one that called the function.
*
* // e.g.
* var p1 = new PMUI.util.Point(3, 5),
* p2 = new PMUI.util.Point(2, 3);
* p1.subtract(p2); // new Point(1, 2)
*
* @param {PMUI.util.Point} other Point to be added to the current point
* @returns {PMUI.util.Point}
*/
Point.prototype.subtract = function (other) {
return new Point(this.x - other.x, this.y - other.y);
};
/**
* Multiplies the point with a scalar k.
*
* // e.g.
* var p1 = new PMUI.util.Point(3, 5),
* k = 3;
* p1.multiply(k); // new Point(9, 15)
*
* @param {number} k
* @return {PMUI.util.Point}
*/
Point.prototype.multiply = function (k) {
return new Point(this.x * k, this.y * k);
};
/**
* Determine if the points are equal.
*
* // e.g.
* var p1 = new PMUI.util.Point(3, 5),
* p2 = new PMUI.util.Point(2, 3),
* p3 = new PMUI.util.Point(3, 5);
* p1.equals(p2); // false
* p1.equals(p3); // true
* p1.equals(p1); // true
*
* @param {PMUI.util.Point} other Point to be compared with the current point
* @returns {boolean}
*/
Point.prototype.equals = function (other) {
return (Math.abs(this.x - other.x) < PMUI.draw.Geometry.eps) &&
(Math.abs(this.y - other.y) < PMUI.draw.Geometry.eps);
};
/**
* Determine the distance between two Points
*
* // e.g.
* // distance = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))
* var p1 = new PMUI.util.Point(3, 5),
* p2 = new PMUI.util.Point(2, 3);
* p1.getDistance(p2); // sqrt(1 + 4)
*
* @param {PMUI.util.Point} other Point to be calculated from current point
* @returns {number}
**/
Point.prototype.getDistance = function (other) {
return Math.sqrt(
(this.x - other.x) * (this.x - other.x) +
(this.y - other.y) * (this.y - other.y)
);
};
/**
* Determine the squared distance between two Points
*
* // e.g.
* // distance = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))
* // but since it's the squared distance then
* // distance = pow(distance, 2)
* var p1 = new PMUI.util.Point(3, 5),
* p2 = new PMUI.util.Point(2, 3);
* p1.getSquaredDistance(p2); // (1 + 4)
*
* @param {PMUI.util.Point} other Point to be calculated from current point
* @returns {number}
**/
Point.prototype.getSquaredDistance = function (other) {
return (this.x - other.x) * (this.x - other.x) +
(this.y - other.y) * (this.y - other.y);
};
/**
* Determine the manhattan distance between two Points
*
* // e.g.
* var p1 = new PMUI.util.Point(3, 5),
* p2 = new PMUI.util.Point(2, 3);
* p1.getManhattanDistance(p2); // (1 + 2)
*
* @param {PMUI.util.Point} other Point to be calculated from current point
* @returns {number}
**/
Point.prototype.getManhattanDistance = function (other) {
return Math.abs(this.x - other.x) + Math.abs(this.y - other.y);
};
/**
* Makes a clone of this
*
* // e.g.
* var p1 = new PMUI.util.Point(3, 5),
* cloneP1;
* cloneP1 = p1.clone(); // cloneP1 is Point(3, 5)
*
* @returns {PMUI.util.Point} This point
*/
Point.prototype.clone = function () {
return new Point(this.x, this.y);
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== 'undefined') {
module.exports = Point;
}
// extend namespace
PMUI.extendNamespace('PMUI.util.Point', Point);
}());
(function () {
/**
* @class PMUI.draw.Graphics
* Class Graphics is the HTMLElement drawing engine used to draw lines (as HTMLElement divs),
* arcs (using HTMLElement divs) and ovals.
* Currently some elements in the designer are completely represented with the drawing engine:
*
* - Connection => a set of segments (regular, segmented, dotted)
* - Port => oval
* - Intersection => arc
*
* Some important notes:
*
* - Currently this class acts as an interface between the library **PMDraw** and the HTMLElement
* drawing engine wz_jsGraphics
* - The drawing engine constructor needs the HTMLElement where it will go as a parameter **(this
* HTMLElement must exist in the DOM)**
*
* @constructor
* Creates an instance of this class (currently it's an interface of the wz_graphics framework)
* @param {Object} html This parameter can be either an id, or an html object
*/
var Graphics = function (html) {
if (!html) {
return null;
}
/**
* Create an instance of the class JSGraphics (this.graphics is an interface)
* @type {Object}
*/
this.graphics = new JSGraphics(html);
/**
* Creates an instance of the class Color (color black)
* @type {PMUI.util.Color}
*/
this.color = new PMUI.util.Color(0, 0, 0);
};
/**
* Draws a line of a given type between two points.
*
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {string} type the type of line we wish to draw
* @param {PMUI.util.Color} color The color of the segment
* @param {number} segLength the segment length for segmented and segmentdot type of line
* @param {number} spaceLength the space length for segmented and segmentdot type of line
* @param {boolean} [doNotErasePrevious] if set to true then it won't clear the elements drew with this instance
*/
Graphics.prototype.drawLine = function (x1, y1, x2, y2, type, color, segLength, spaceLength, doNotErasePrevious) {
if (!doNotErasePrevious) {
this.graphics.clear();
}
if (!type) {
type = "regular";
}
switch (type) {
case "dotted":
this.graphics.setStroke(-1);
break;
case "segmented":
this.graphics.setStroke(1);
this.graphics.drawLine = this.makeSegmentedLine;
break;
case "segmentdot":
this.graphics.setStroke(1);
this.graphics.drawLine = this.makeSegmentDotLine;
break;
default:
this.graphics.setStroke(1);
}
this.graphics.setColor(color.getCSS());
this.graphics.drawLine(x1, y1, x2, y2, segLength, spaceLength);
this.graphics.paint();
};
/**
* Returns the color that was being used for drawing
* @returns {PMUI.util.Color}
*/
Graphics.prototype.getColor = function () {
return this.color;
};
/**
* Sets the color to be used for drawing
* @param newColor
* @chainable
*/
Graphics.prototype.setColor = function (newColor) {
if (newColor.type === "Color") {
this.color = newColor;
}
return this;
};
/**
* This function will make a segmented line between two points in the same axis,
* if points in different axis are provided the method would simple return
*
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} segmentLength the segment length for segmented and
* segmentdot type of line
* @param {number} spLength the space length for segmented and
* segmentdot type of line
*/
Graphics.prototype.makeSegmentedLine = function (x1, y1, x2, y2, segmentLength, spLength) {
var dx,
dy,
aux,
segLength = 4,
spaceLength = 3,
diff = 0,
x,
y;
//not same axis so just return
if ((x2 !== x1 && y2 !== y1)) {
return;
}
if (x2 === x1) {
//same point just return
if (y2 === y1) {
return;
}
dx = 0;
//swap
if (y2 < y1) {
aux = y2;
y2 = y1;
y1 = aux;
}
dy = diff = y2 - y1;
} else {
dy = 0;
if (x2 < x1) {
aux = x2;
x2 = x1;
x1 = aux;
}
dx = diff = x2 - x1;
}
x = x1;
y = y1;
if (diff < 7) {
segLength = 2;
spaceLength = 1;
}
segLength = (!segmentLength) ? segLength : segmentLength;
spaceLength = (!spLength) ? spaceLength : spLength;
if (dy === 0) {
while (dx > 0) {
if (dx >= segLength) {
this._mkDiv(x, y, segLength, 1);
x += segLength + spaceLength;
dx -= (segLength + spaceLength);
} else {
this._mkDiv(x, y, dx, 1);
dx = 0;
}
}
} else {
while (dy > 0) {
if (dy >= segLength) {
this._mkDiv(x, y, 1, segLength);
y += segLength + spaceLength;
dy -= (segLength + spaceLength);
} else {
this._mkDiv(x, y, 1, dy);
dy = 0;
}
}
}
};
/**
* This function will make a segment between two points in the same axis with
* the following structure segment-dot-segment
* if points in different axis are provided the function will simply return.
*
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} segmentLength the segment length for segmented and
* segmentdot type of line
* @param {number} spLength the space length for segmented and
* segmentdot type of line
*/
Graphics.prototype.makeSegmentDotLine = function (x1, y1, x2, y2, segmentLength, spLength) {
var dx,
dy,
aux,
segLength = 7,
spaceLength = 4,
dotLength = 1,
diff = 0,
x,
y;
//not same axis so just return
if ((x2 !== x1 && y2 !== y1)) {
return;
}
if (x2 === x1) {
//same point just return
if (y2 === y1) {
return;
}
dx = 0;
//swap
if (y2 < y1) {
aux = y2;
y2 = y1;
y1 = aux;
}
dy = y2 - y1;
diff = dy;
} else {
dy = 0;
if (x2 < x1) {
aux = x2;
x2 = x1;
x1 = aux;
}
dx = x2 - x1;
diff = dx;
}
x = x1;
y = y1;
segLength = (!segmentLength) ? segLength : segmentLength;
spaceLength = (!spLength) ? spaceLength : spLength;
if (dy === 0) {
while (dx > 0) {
if (dx >= segLength) {
this._mkDiv(x, y, segLength, 1);
dx -= (segLength + spaceLength);
x += segLength + spaceLength;
if (dx > 0) {
this._mkDiv(x, y, dotLength, 1);
dx -= (dotLength + spaceLength);
x += dotLength + spaceLength;
}
} else {
this._mkDiv(x, y, dx, 1);
dx = 0;
}
}
} else {
while (dy > 0) {
if (dy >= segLength) {
this._mkDiv(x, y, 1, segLength);
dy -= (segLength + spaceLength);
y += segLength + spaceLength;
if (dy > 0) {
this._mkDiv(x, y, 1, dotLength);
dy -= (dotLength + spaceLength);
y += dotLength + spaceLength;
}
} else {
this._mkDiv(x, y, 1, dy);
dy = 0;
}
}
}
};
/**
* Draws an arc with the center `[cx, cy]`, with a radius equal to `radius` from `startAngle` to `endAngle`
* and drawing a line every `step` steps.
* Logic:
*
* 1. Let's assume that we have a circle with center `[cx, cy]` and with a radius equal to `radius`
* 2. We want to draw only a portion of the circle (from `startAngle` to `endAngle`)
* 3. Given any angle of the circle `0 <= angle < 360` we can get its `x` and `y` coordinates using
* Pythagoras triangle rectangle laws.
* - We know that `hyp^2 = dx^2 + dy^2` and that `hyp = radius`
* - We know that `cos(angle) = dx / radius` so `dx = radius * cos(angle)`
* - We know that `sin(angle) = dy / radius` so `dx = radius * cos(angle)`
* 4. Finally let's use the given center of the circle to move the triangle
*
* @param {number} cx
* @param {number} cy
* @param {number} radius
* @param {number} startAngle
* @param {number} endAngle
* @param {number} step
*/
Graphics.prototype.drawArc = function (cx, cy, radius, startAngle, endAngle, step) {
var x,
y,
angle = startAngle;
if (!step) {
step = 10;
}
while (Math.abs(angle - endAngle) > 1e-5) {
angle = (angle + step) % 360;
x = cx + radius * Math.cos(angle * PMUI.draw.Geometry.pi / 180.0);
y = cy + radius * Math.sin(angle * PMUI.draw.Geometry.pi / 180.0);
this.graphics.drawLine(x, y, x, y);
}
};
PMUI.extendNamespace('PMUI.draw.Graphics', Graphics);
}());
(function () {
/**
* @class PMUI.draw.Geometry
* A little object that encapsulates most geometry functions used in the designer, most of the examples
* are in 'spec/draw/geometry.spec.js'
*
* @singleton
*/
var Geometry = {
/**
* The number pi
* @property {number} [pi=Math.acos(-1)]
*/
pi: Math.acos(-1),
/**
* Epsilon used for the correctness in comparison of float numbers
* @property {number} [eps=1e-8]
*/
eps: 1e-8,
/**
* Calculates the cross product of 2-dimensional vectors
* @param {PMUI.util.Point} p1
* @param {PMUI.util.Point} p2
* @return {number}
*/
cross: function (p1, p2) {
return p1.x * p2.y - p1.y * p2.x;
},
/**
* Calculates the SIGNED area of a parallelogram given three points, these three points are the points
* that conforms the triangle that is half of the parallelogram, so. the area of the triangle
* defined with these points is half the returned number (this method can return negative values)
*
* // e.g.
* var p1 = new PMUI.util.Point(0, 0),
* p2 = new PMUI.util.Point(0, 1),
* p3 = new PMUI.util.Point(1, 0),
* parallelogramArea,
* triangleArea;
*
* parallelogramArea = Geometry.area(p1, p2, p3) // -1 (area of the parallelogram)
* triangleArea = parallelogramArea / 2 // -0.5 (area of the triangle)
*
* @param {PMUI.util.Point} p1
* @param {PMUI.util.Point} p2
* @param {PMUI.util.Point} p3
* @return {number}
*/
area: function (p1, p2, p3) {
var auxP2 = p2.clone(),
auxP3 = p3.clone();
return this.cross(auxP2.subtract(p1), auxP3.subtract(p1));
},
/**
* Determines if the point P is on segment AB
* @param {PMUI.util.Point} P
* @param {PMUI.util.Point} A
* @param {PMUI.util.Point} B
* @return {boolean}
*/
onSegment: function (P, A, B) {
return (Math.abs(this.area(A, B, P)) < this.eps &&
P.x >= Math.min(A.x, B.x) && P.x <= Math.max(A.x, B.x) &&
P.y >= Math.min(A.y, B.y) && P.y <= Math.max(A.y, B.y));
},
/**
* Checks if two perpendicular segments intersect, if so it returns the intersection point,
* (this method only allows the perpendicular segment to be parallel to the x and y axis)
* @param {PMUI.util.Point} A
* @param {PMUI.util.Point} B
* @param {PMUI.util.Point} C
* @param {PMUI.util.Point} D
* @return {Object}
*/
perpendicularSegmentIntersection: function (A, B, C, D) {
var clone,
returnValue = null;
// swap the segments if possible
if (A.x > B.x || A.y > B.y) {
clone = A.clone();
A = B.clone();
B = clone;
}
if (C.x > D.x || C.y > D.y) {
clone = C.clone();
C = D.clone();
D = clone;
}
if (A.x === B.x) {
if (C.y === D.y && C.x < A.x && A.x < D.x &&
A.y < C.y && C.y < B.y) {
returnValue = new PMUI.util.Point(A.x, C.y);
}
} else if (A.y === B.y) {
if (C.x === D.x && A.x < C.x && C.x < B.x &&
C.y < A.y && A.y < D.y) {
returnValue = new PMUI.util.Point(C.x, A.y);
}
}
return returnValue;
},
/**
* Determines if segment AB intersects with segment CD (won't check infinite intersections),
* if `strict` is set to `true` then it'll consider the case when one end of a segment is right in the
* other segment
* @param {PMUI.util.Point} A
* @param {PMUI.util.Point} B
* @param {PMUI.util.Point} C
* @param {PMUI.util.Point} D
* @param {boolean} [strict]
* @return {boolean}
*/
segmentIntersection: function (A, B, C, D, strict) {
var area1 = this.area(C, D, A),
area2 = this.area(C, D, B),
area3 = this.area(A, B, C),
area4 = this.area(A, B, D),
returnValue;
if (((area1 > 0 && area2 < 0) || (area1 < 0 && area2 > 0)) &&
((area3 > 0 && area4 < 0) || (area3 < 0 && area4 > 0))) {
return true;
}
returnValue = false;
if (strict) {
if (area1 === 0 && this.onSegment(A, C, D)) {
returnValue = true;
} else if (area2 === 0 && this.onSegment(B, C, D)) {
returnValue = true;
} else if (area3 === 0 && this.onSegment(C, A, B)) {
returnValue = true;
} else if (area4 === 0 && this.onSegment(D, A, B)) {
returnValue = true;
}
}
return returnValue;
},
/**
* Checks if two segments intersect, if so it returns the intersection point
* @param {PMUI.util.Point} A
* @param {PMUI.util.Point} B
* @param {PMUI.util.Point} C
* @param {PMUI.util.Point} D
* @return {PMUI.util.Point}
*/
segmentIntersectionPoint: function (A, B, C, D) {
return A.add((B.subtract(A))
.multiply(this.cross(C.subtract(A), D.subtract(A)) /
this.cross(B.subtract(A), D.subtract(C))));
},
/**
* Determines whether a point is in a given rectangle or not given its
* upperLeft and bottomRight corner (consider that a rectangle is turned in the y-axis)
* @param {PMUI.util.Point} point
* @param {PMUI.util.Point} upperLeft
* @param {PMUI.util.Point} bottomRight
* @return {boolean}
*/
pointInRectangle: function (point, upperLeft, bottomRight) {
return (point.x >= upperLeft.x && point.x <= bottomRight.x &&
point.y >= upperLeft.y && point.y <= bottomRight.y);
},
/**
* Determines whether a point is in a circle or not given its center and
* radius
* @param {PMUI.util.Point} point
* @param {PMUI.util.Point} center
* @param {number} radius
* @returns {boolean}
*/
pointInCircle: function (point, center, radius) {
return center.getDistance(point) <= radius;
},
/**
* Determine whether a point is inside a rhombus or not given its center
* and its points in clockwise order
* @param {PMUI.util.Point} point
* @param {Array} rhombus
* @param {PMUI.util.Point} center
* @return {boolean}
*/
pointInRhombus: function (point, rhombus, center) {
var i,
j = rhombus.length - 1;
for (i = 0; i < rhombus.length; j = i, i += 1) {
if (this.segmentIntersection(center, point,
rhombus[j], rhombus[i], true) &&
this.onSegment(point, rhombus[j], rhombus[i]) === false) {
return false;
}
}
return true;
}
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== 'undefined') {
module.exports = Geometry;
}
// extend the namespace
PMUI.extendNamespace('PMUI.draw.Geometry', Geometry);
}());
(function () {
/**
* @class PMUI.event.Action
* Handles the action for buttons, menues, toolbars
*
* Example:
*
* var action = new PMUI.event.Action({
* text: 'Save Document',
* icon: 'pm-icon-save',
* disabled: false,
* handler: function () {
* alert('Action has been called!');
* }
* });
*
* Using action as parameter to create another objects:
*
* var button = new PMUI.ui.Button(action);
*
* var form = new PMUI.form.Form({
* ...
* buttons: [
* action,
* {
* text: 'Refresh',
* handler: function() {
* obj.refresh() //this object is fake used for sample purposes only
* }
* }
* ]
* ...
* });
*
* Actions are created to handle user functionality you can extend or use over several places or components
*
*
* @constructor
* Creates a new instance of object
* @param {Object} options
*/
var Action = function (options) {
/**
* Defines the text of the action
* @type {String}
*/
this.actionText = null;
/**
* Defines the icon asssociated with the action
* @type {String}
*/
this.actionIcon = null;
/**
* Defines the state of the action
* @type {Boolean}
*/
this.disabled = false;
/**
* Defines the action to be executed
* @type {Function}
*/
this.handler = null;
Action.prototype.init.call(this, options);
};
/**
* Defines the object's type
* @type {String}
*/
Action.prototype.type = 'Action';
/**
* Defines the object's family
* @type {String}
*/
Action.prototype.family = 'Action';
/**
* Defines the action flag
* @type {Boolean}
*/
Action.prototype.isAction = true;
/**
* @private
* Initializes the object with the default values
* @param {Object} options Constructor options
*/
Action.prototype.init = function (options) {
var defaults;
defaults = {
icon: null,
text: null,
disabled: false,
handler: function () {
}
};
jQuery.extend(true, defaults, options);
this.setActionIcon(defaults.icon)
.setActionText(defaults.text)
.setDisable(defaults.disabled)
.setHandler(defaults.handler);
};
/**
* Sets the action Icon
* @param {String} icon Icon URL or class
*/
Action.prototype.setActionIcon = function (icon) {
this.actionIcon = icon;
return this;
};
/**
* Sets the action's text
* @param {String} text Actions's name
*/
Action.prototype.setActionText = function (text) {
this.actionText = text;
return this;
};
/**
* Sets the disabled value
* @param {Boolean} value
*/
Action.prototype.setDisable = function (value) {
if (typeof(value) === 'boolean') {
this.disabled = value;
}
return this;
};
/**
* Returns the icon url or class
* @return {String} [description]
*/
Action.prototype.getIcon = function () {
return this.actionIcon;
};
/**
* Returns the action name
* @return {String} [description]
*/
Action.prototype.getText = function () {
return this.actionText;
};
/**
* Sets the handler function
* @param {Function} fn
*/
Action.prototype.setHandler = function (fn) {
if (typeof(fn) === 'function') {
this.handler = fn;
}
return this;
};
/**
* Enables the action functionality
*/
Action.prototype.enable = function () {
this.setDisable(false);
};
/**
* Disables the action functionality
*/
Action.prototype.disable = function () {
this.setDisable(true);
};
/**
* Returns if the action is enabled
* @return {Boolean}
*/
Action.prototype.isEnabled = function () {
return !this.disabled;
};
/**
* Executes the action if is enabled and has a valid handler
*/
Action.prototype.execute = function () {
if (!this.disabled && typeof this.handler === 'function') {
this.handler();
}
};
if (typeof exports !== "undefined") {
module.exports = Action;
}
PMUI.extendNamespace('PMUI.event.Action', Action);
}());
(function () {
/**
* @class PMUI.core.Base
* Base class for any other class in PMUI, this class is an abstract so it shouldn't be instantiated.
* @param {Object} settings All the settings the the class requires
*
* Usage example:
*
* var anObject = new PMUI.core.Base({
* id: "myUniqueId"
* });
*
* @constructor
* Creates a new instance of the class 'Base'.
* @param {Object} [options={}] The config options for the object.
*
* @cfg {String} [id=[an autogenerated unique id]] The unique identifier for the object. If it is not specified
* then one is created an assigned to it.
*/
var Base = function (settings) {
/**
* Unique ID
* @type {String}
*/
this.id = null;
Base.prototype.init.call(this, settings);
};
/**
* Class type
* @type {String}
*/
Base.prototype.type = 'Base';
/**
* Class family
* @type {String}
*/
Base.prototype.family = 'Core';
/**
* Initialize the object
* @param {Object} settings Settings for Base Class
* @return {String} the generated id
* @private
*/
Base.prototype.init = function (settings) {
var defaults = {
id: 'pmui-' + PMUI.generateUniqueId()
};
jQuery.extend(true, defaults, settings);
this.setID(defaults.id);
};
/**
* Set the id property for the object
* @param {String} id
* @chainable
*/
Base.prototype.setID = function (id) {
this.id = id;
return this;
};
/**
* Returns the id property from the object
* @return {String} [description]
*/
Base.prototype.getID = function () {
return this.id;
};
/**
* Returns the object's type
* @return {String}
*/
Base.prototype.getType = function () {
return this.type;
};
/**
* Return's the object's family name
* @return {String}
*/
Base.prototype.getFamily = function () {
return this.family;
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Base;
}
PMUI.extendNamespace('PMUI.core.Base', Base);
}());
(function () {
/**
* @class PMUI.core.Element
* Base class to handle objects that have a visual representation (HTML representation).
* @extends PMUI.core.Base
*
* Usage example:
*
* @example
* var element;
*
* element = new PMUI.core.Element({
* x: 10,
* y: 20,
* positionMode: "absolute",
* width: 200,
* height: 200,
* style: {
* cssProperties: {
* "background-color": "red"
* },
* cssClasses: [".square"]
* }
* });
*
* document.body.appendChild(element.getHTML());
*
* @constructor
* Create a new instace of the class 'Element'
* @param {Object} options
*
* @cfg {String} [elementTag="div"] The html tag for the object's HTML element.
* @cfg {String} [positionMode="relative"] The css position for the object's HTML element. It can take one of the
* following values "static", "absolute", "fixed", "relative", "inherit" or "". Please read about the css "position"
* property to know about how each value impacts in the html.
* @cfg {Object} [style={cssProperties:{}, cssClasses:[]}] A JSON object which will contain the css properties for
* the object's html.
*
* This object can have optionally two properties:
*
* - cssProperties: another JSON object in which each property can be a css property (if the css property has any
* special character like "-" enclose the property name between quotes), the value can be any valid css value for
* the property.
* - cssClasses: An array where each element is a String, this String is the css class name.
*
* @cfg {Number} [x=0] The position coordinate in the x axis for the object's HTML, please note that the visual
* position will depend on the {@link PMUI.core.Element#cfg-positionMode positionMode option}.
* @cfg {Number} [y=0] The position coordinate in the y axis for the object's HTML, please note that the visual
* position will depend on the {@link PMUI.core.Element#cfg-positionMode positionMode option}.
* @cfg {Number|String} [width="auto"] The width for the object's HTML, it can be a Number or an String. In the
* latter case the value must be "auto" or "inherit" or have one of the following formats:
*
* - "##px".
* - "##%".
* - "##em"
*
* Note that ## is a number.
* @cfg {Number|String} [height="auto"] The height for the object's HTML, the values that can take are the same
* specified in the {@link PMUI.core.Element#cfg-width}.
* @cfg {Number|String} [zOrder="auto"] The position in the z axis for the object's HTML, it can be a Number or a
* String, in the latter case the value only can be "auto" or "inherit".
* @cfg {Boolean} [visible=true] If the object will be visible or not.
* @cfg {Number} [proportion=1] A numeric value that specifies the final width of the object's HTML in case to be
* contained by a {@link PMUI.core.Container Container} which is applying a {@link PMUI.layout.Layout Layout}.
* @cfg {String} [display=""] The display property specifies the type of box used for an HTML element.
* find out more about the css display property
* @cfg {Function} [onBeforeContextMenu=null] description here.
*/
var Element = function (settings) {
Element.superclass.call(this, settings);
/**
* HTML element.
* @type {HTMLElement}
* @readonly
*/
this.html = null;
/**
* @property {Object|PMUI.util.Style} [style={
cssProperties: {},
cssClasses: []
}]
* A {@link PMUI.util.Style Style} object or a JSON object with the settings to create a new
* {@link PMUI.util.Style Style} object for the current's HTML element.
* @readonly
*/
this.style = null;
/**
* X coordinate for the HTML element.
* @type {Number}
* @readonly
*/
this.x = null;
/**
* @property {Number} [y=0]
* Y Coordinate for the HTML element.
* @readonly
*/
this.y = null;
/**
* Width for the HTML element, it can be a number or a string with the following format:
##px when ## is a number.
* @type {Number|String}
* @readonly
*/
this.width = null;
/**
* Height for the HTML element, it can be a number or a string with the following format:
##px when ## is a number.
* @type {Number|String}
* @readonly
*/
this.height = null;
/**
* A boolean value that indicates if the HTML element is visible or not.
* @type {Boolean}
* @readonly
*/
this.visible = null;
/**
* A Number that indicates the HTML element's position on the Z axis.
* @type {Number}
* @readonly
*/
this.zOrder = null;
/**
* @property {String} [elementTag="div"]
* Tag name for the element to be created, it defaults to "div".
* @readonly
*/
this.elementTag = null;
/**
* @property {String} [positionMode="relative"]
* Position for the object's html element, it must be a valid value for the "position" CSS property.
* @readonly
*/
this.positionMode = null;
/**
* Defines the proportion of the html.
* @type {Number}
*/
this.proportion = null;
/**
* @property {String} [display=""] The display mode for the element. It is set by the
* {@link #cfg-display display} config option and the {@link #method-setDisplay setDisplay} method.
* @readonly
*/
this.display = null;
/**
* Defines the menu component for the element
* @type {PMUI.ui.Menu}
*/
this.menu = null;
/**
* Defines an object to handle/register events.
* @type {Object}
*/
this.events = {};
/**
* @property {Boolean} eventsDefined If the events were defined or not
*/
this.eventsDefined = false;
/**
* [onContextMenu description]
* @type {Boolean}
*/
this.onContextMenu = false;
/**
* [onBeforeContextMenu description]
* @type {Boolean}
*/
this.onBeforeContextMenu = false;
/**
* The element's parent Container.
* @type {PMUI.core.Element}
*/
this.parent = null;
/**
* The HTML area in which some activities, actions can be performed (like display the context menu).
* @type {HTMLElement}
* @private
*/
this.actionHTML = null;
Element.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Base', Element);
/**
* The Object's type.
* @type {String}
*/
Element.prototype.type = 'Element';
/**
* Initialize the object.
* @param {Object} settings A JSON object with the settings to create the new class instance.
* @private
*/
Element.prototype.init = function (settings) {
var defaults = {
elementTag: "div",
positionMode: "relative",
menu: null,
style: {
cssProperties: {},
cssClasses: []
},
x: 0,
y: 0,
width: "auto",
height: "auto",
zOrder: "auto",
display: "",
visible: true,
proportion: 1,
onContextMenu: null,
onBeforeContextMenu: null
};
jQuery.extend(true, defaults, settings);
this.setElementTag(defaults.elementTag)
.setStyle(defaults.style)
.setPositionMode(defaults.positionMode)
.setDisplay(defaults.display)
.setX(defaults.x)
.setY(defaults.y)
.setWidth(defaults.width)
.setHeight(defaults.height)
.setZOrder(defaults.zOrder)
.setVisible(defaults.visible)
.setProportion(defaults.proportion)
.setContextMenu(defaults.menu)
.setOnBeforeContextMenu(defaults.onBeforeContextMenu)
.setOnContextMenuHandler(defaults.onContextMenu);
};
/**
* [setOnBeforeContextMenu description]
*/
Element.prototype.setOnBeforeContextMenu = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnBeforeContextMenu(): The parameter must be a function or null.");
}
this.onBeforeContextMenu = handler;
return this;
};
/**
* [setOnContextMenuHandler description]
* @param {[type]} handler [description]
*/
Element.prototype.setOnContextMenuHandler = function (handler) {
if (typeof handler === 'function' || null) {
this.onContextMenu = handler;
}
return this;
};
/**
* Sets the display mode for the Element.
* @param {String} display It can take one of the following values:
*
* - ""
* - "block"
* - "inline"
* - "inline-block"
* - "none"
*
* @chainable
*/
Element.prototype.setDisplay = function (display) {
if (display === "" || display === 'block' || display === 'inline'
|| display === 'inline-block' || display === 'none') {
this.display = display;
this.applyStyle();
} else {
throw new Error('The setDisplay() method only accepts one od the following options: ' +
' "", "block", "inline", "inline-block", "none"');
}
return this;
};
/**
* Sets the position mode for the Element.
* @param {String} position It can take one ot the following values:
*
* - "static"
* - "asolute"
* - "fixed"
* - "relative"
* - "inherit"
*
* @chainable
*/
Element.prototype.setPositionMode = function (position) {
if (position === 'static' || position === 'absolute' || position === 'fixed' || position === 'relative' ||
position === 'inherit' || position === "") {
this.positionMode = position;
this.applyStyle();
} else {
throw new Error('The setPosition() method only accepts one of the following options:' +
' "static", "absolute", "fixed", "relative", "inherit" or an empty string.');
}
return this;
};
/**
* Set the HTML tag for the HTML element to be created, note that it'll only work when its html property
is still not set.
* @param {String} tag a HTML tag
* @chainable
*/
Element.prototype.setElementTag = function (tag) {
if (!this.html && typeof tag === 'string') {
this.elementTag = tag;
}
return this;
};
/**
* [addCSSClasses description]
* @param {[type]} classes [description]
*/
Element.prototype.addCSSClasses = function (classes) {
this.style.addClasses(classes);
return this;
};
/**
* [addCSSProperties description]
* @param {[type]} properties [description]
*/
Element.prototype.addCSSProperties = function (properties) {
this.style.addProperties(properties);
return this;
};
/**
* [removeCSSClasses description]
* @param {[type]} classes [description]
* @return {[type]} [description]
*/
Element.prototype.removeCSSClasses = function (classes) {
this.style.removeClasses(classes);
return this;
};
/**
* [removeCSSProperties description]
* @param {[type]} properties [description]
* @return {[type]} [description]
*/
Element.prototype.removeCSSProperties = function (properties) {
this.style.removeProperties(properties);
return this;
};
/**
* Set the style properties for the HTML element.
* @param {Object|null} style an JSON structure with attributes cssProperties (another object) and cssClasses
* (array). It also can be null, in this case no custom styles will be applied to the the element.
* @chainable
*/
Element.prototype.setStyle = function (style) {
style = style || {};
if (this.style && this.html) {
this.style.unapplyStyle();
}
if (style instanceof PMUI.util.Style) {
this.style = style;
style.belongsTo = this;
} else if (typeof style === 'object') {
style.belongsTo = this;
this.style = new PMUI.util.Style(style);
}
this.applyStyle();
return this;
};
/**
* Set the x position coordinate for the HTML element.
* @chainable
* @param {Number} x
* @chainable
*/
Element.prototype.setX = function (x) {
if (typeof x === 'number') {
this.x = x;
} else if (/^\d+(\.\d+)?px$/.test(x)) {
this.x = parseInt(x, 10);
} else {
throw new Error('setX: x param is not a number');
}
this.style.addProperties({left: this.x});
return this;
};
/**
* Returns the x position coordinate for the HTML element
* @return {Number}
*/
Element.prototype.getX = function () {
return this.x;
};
/**
* Set the y position coordinate for the HTML element.
* @param {Number} y
* @chainable
*/
Element.prototype.setY = function (y) {
if (typeof y === 'number') {
this.y = y;
} else if (/^\d+(\.\d+)?px$/.test(y)) {
this.y = parseInt(y, 10);
} else {
throw new Error('setY: y param is not a number');
}
this.style.addProperties({top: this.y});
return this;
};
/**
* Returns the y position coordinate for the HTML element
* @return {Number}
*/
Element.prototype.getY = function () {
return this.y;
};
/**
* Set the width for the HTML element
* @param {Number|String} width height it can be a number or a string.
In case of using a String you only can use 'auto' or ##px or ##% or ##em when ## is a number.
* @chainable
*/
Element.prototype.setWidth = function (width) {
if (typeof width === 'number') {
this.width = width;
} else if (/^\d+(\.\d+)?px$/.test(width)) {
this.width = parseInt(width, 10);
} else if (/^\d+(\.\d+)?%$/.test(width)) {
this.width = width;
} else if (/^\d+(\.\d+)?em$/.test(width)) {
this.width = width;
} else if (width === 'auto') {
this.width = width;
} else {
throw new Error('setWidth: width param is not a number');
}
this.style.addProperties({width: this.width});
return this;
};
/**
* Returns the HTML element's width
* @return {Number}
*/
Element.prototype.getWidth = function () {
return this.width;
};
/**
* Set the height for the HTML element
* @param {Number|String} height it can be a number or a string.
In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
* @chainable
*/
Element.prototype.setHeight = function (height) {
if (typeof height === 'number') {
this.height = height;
} else if (/^\d+(\.\d+)?px$/.test(height)) {
this.height = parseInt(height, 10);
} else if (/^\d+(\.\d+)?%$/.test(height)) {
this.height = height;
} else if (/^\d+(\.\d+)?em$/.test(height)) {
this.height = height;
} else if (height === 'auto' || height === 'inherit') {
this.height = height;
} else {
throw new Error('setHeight: height param is not a number');
}
this.style.addProperties({height: this.height});
return this;
};
/**
* Returns the HTML element's height.
* @return {Number}
*/
Element.prototype.getHeight = function () {
return this.height;
};
/**
* Set the position index om the z axis for the object's HTML element.
* @param {Number|String} zOrder it can be a Number or a String.
In case of using a String you only can use 'auto' or 'inherit'.
* @chainable
*/
Element.prototype.setZOrder = function (zOrder) {
if (typeof zOrder === 'number') {
this.zOrder = parseInt(zOrder, 10);
} else if (zOrder === 'auto' || zOrder === 'inherit') {
this.zOrder = zOrder;
} else {
throw new Error('setZOrder: zOrder param is not a number');
}
if (this.html) {
this.style.addProperties({"z-index": this.zOrder});
}
return this;
};
/**
* Returns the HTML element's zOrder.
* @return {Number}
*/
Element.prototype.getZOrder = function () {
return this.zOrder;
};
/**
* Set if the HTML element is visible or not.
* @param {Boolean} visible
* @chainable
*/
Element.prototype.setVisible = function (visible) {
visible = !!visible;
this.visible = visible;
if (this.html) {
this.style.addProperties({"display": this.visible ? this.display : "none"});
}
return this;
};
/**
* Returns a boolean value that indicates if the HTML element is visible or not.
* @return {Boolean} [description]
*/
Element.prototype.isVisible = function () {
return this.visible;
};
/**
* Set the HTML element's position.
* @param {Object} position an object with x and y properties, both of them are Numbers
* @chainable
*/
Element.prototype.setPosition = function (position) {
this.setX(position.x);
this.setY(position.y);
return this;
};
/**
* Returns and object which contains the x and y positions from the HTML element.
* @return {Object}
*/
Element.prototype.getPosition = function () {
return {
x: this.getX(),
y: this.getY()
};
};
/**
* Set the HTML element's dimension (width and height).
* @param {Object} dimension
* @chainable
*/
Element.prototype.setDimension = function (dimension) {
this.setWidth(dimension.width);
this.setHeight(dimension.height);
return this;
};
/**
* Returns the HTML element's dimension.
* @return {Object} and object with width and height properties.
*/
Element.prototype.getDimension = function () {
return {
width: this.getWidth(),
height: this.getHeight()
};
};
/**
* Applies the css classes and ptoperties to the element's html which the object is related.
* @chainable
*/
Element.prototype.applyStyle = function () {
if (this.html) {
this.style.applyStyle();
this.style.addProperties({
display: this.visible ? this.display : "none",
position: this.positionMode,
left: this.x,
top: this.y,
width: this.width,
height: this.height,
zIndex: this.zOrder
});
}
return this;
};
/**
* Creates the object's HTML element.
* @return {HTMLElement} returns a HTML element
*/
Element.prototype.createHTML = function () {
var html;
if (this.html) {
return this.html;
}
html = PMUI.createHTMLElement(this.elementTag || "div");
html.id = this.id;
PMUI.linkToPMUIObject(html, this);
this.html = html;
this.actionHTML = html;
this.applyStyle();
if (this.eventsDefined) {
this.defineEvents();
}
return this.html;
};
/**
* Returns the object's HTML element, if it doesn't exist it's created then it is returned.
* @return {HTMLElement}
*/
Element.prototype.getHTML = function () {
if (!this.html) {
this.html = this.createHTML();
}
return this.html;
};
/**
* Set the proportion of the html element.
* @param {Number} p Proportion value.
* @chainable
*/
Element.prototype.setProportion = function (p) {
this.proportion = p;
return this;
};
/**
* Creates and Instanciate an event.
* @param {String} type Event type.
* @param {String} [alias] Registered Name.
* @return {PMUI.event.Event}
*/
Element.prototype.addEvent = function (type, alias) {
var factory = new PMUI.event.EventFactory(),
newEvent = factory.make(type),
registerName = alias || PMUI.generateUniqueId();
this.events[registerName] = newEvent;
return newEvent;
};
/**
* @abstract
* Defines the events associated with the element.
* @chainable
* @abstract
*/
Element.prototype.defineEvents = function () {
var that = this;
this.removeEvents();
this.addEvent('contextmenu').listen(this.actionHTML, function (e) {
if (typeof that.onBeforeContextMenu === 'function') {
that.onBeforeContextMenu(that);
}
if (typeof that.onContextMenu === 'function') {
that.onContextMenu(that);
}
if (that.menu) {
e.stopPropagation();
e.preventDefault();
that.menu.show(e.pageX, e.pageY);
}
});
this.eventsDefined = true;
return this;
};
/**
* Remove a specific event attached to the object.
* @param {String} identifier The id or alias used in the moment of event addition.
* @chainable
*/
Element.prototype.removeEvent = function (identifier) {
if (this.events[identifier]) {
this.events[identifier].unlisten();
delete this.events[identifier];
}
return this;
};
/**
* Removes events attached to the object.
* @chainable
*/
Element.prototype.removeEvents = function () {
var k;
for (k in this.events) {
this.removeEvent(k);
}
;
return this;
};
/**
* Calculates the width size of the container.
*
* @param {String} text
* @param {String} font
* @chainable
*/
Element.prototype.calculateWidth = function (text, font) {
//TODO Improve the div creation (maybe we can use a singleton for this)
var f = font || '12px arial',
$o = $(PMUI.createHTMLElement('div')), w;
$o.text(text)
.css({
'position': 'absolute',
'float': 'left',
'white-space': 'nowrap',
'visibility': 'hidden',
'font': f
})
.appendTo($('body'));
w = $o.width();
$o.remove();
return w;
};
/**
* Sets the context menu for the component and the menu will be contained on the {@link PMUI.core.Element#menu menu} property.
*
* If the settings parameter is not a valid json the {@link PMUI.core.Element#menu menu} property will be an simple empty object.
*
* The example below shows the appropriate way to send a basic settings parameter.
*
* component = new Class();
* component.setContextMenu({
* id: "id", //optional
* items: [
* {
* text: "Option 1"
* },
* {
* text: "Option 2"
* }
* ]
* });
*
* For more details about the context menu, the documentation is on the {@link PMUI.ui.Menu Menu} class.
*
* @param {Object} settings Configuration or config options for the context {@link PMUI.ui.Menu menu}.
* @return {Object} this Represents the component of the class.
*/
Element.prototype.setContextMenu = function (menuItems) {
var contextMenu;
this.removeContextMenu();
if (menuItems !== null) {
if (jQuery.isArray(menuItems)) {
contextMenu = new PMUI.menu.Menu({items: menuItems});
} else if (menuItems instanceof PMUI.menu.Menu) {
contextMenu = menuItems;
} else if (typeof menuItems === 'object') {
contextMenu = new PMUI.menu.Menu(menuItems);
}
if (contextMenu) {
this.menu = contextMenu.setTargetElement(this);
}
}
return this;
};
/**
* [removeContextMenu description]
* @return {[type]} [description]
*/
Element.prototype.removeContextMenu = function () {
if (this.menu) {
this.menu.setTargetElement(null);
this.menu = null;
}
return this;
};
/**
* Returns the elemnent's parent container.
* @return {PMUI.core.Element|null} The parent container.
*/
Element.prototype.getParent = function () {
return this.parent;
};
/**
* Shows the context menu for the element provided that the element contains an object that represent a
* {@link PMUI.ui.Menu Menu}.
*
* If the {@link PMUI.core.Element#menu menu} property is an empty object the context menu will
* never appear on the page.
*
* By default the context menu will appear after to do right click on the element.
*/
Element.prototype.showContextMenu = function () {
if (this.menu) {
this.menu.show();
}
return this;
};
/**
* Return true if the object's events were defined, otherwise it returns false.
* @return {Boolean}
*/
Element.prototype.eventsAreDefined = function () {
return this.eventsDefined;
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Element;
}
PMUI.extendNamespace('PMUI.core.Element', Element);
}());
(function () {
/**
* @class PMUI.core.Container
* Handles HTML elements that have a container behavior (they can be contain {Element} objects)
* @abstract
* @extend PMUI.core.Element
* * Usage example (only for subclasses since this is an abstract class):
*
* @example
* //Remember, this is an abstract class so it shouldn't be instantiate,
* //anyway we are instantiating it just for this example
* c = new PMUI.core.Container({
* x: 10,
* y: 20,
* positionMode: "absolute",
* width: 300,
* height: 100,
* style: {
* cssProperties: {
* "background-color": "yellow"
* },
* cssClasses: [".square"]
* },
* items : [
* new PMUI.core.Element({
* y : 0,
* width: 100,
* height: 100,
* style: {
* cssProperties: {
* "background-color": "red"
* },
* cssClasses: [".square"]
* }
* }),
* new PMUI.core.Element({
* y : 100,
* width: 100,
* height: 100,
* style: {
* cssProperties: {
* "background-color": "yellow"
* },
* cssClasses: [".square"]
* }
* })
* ]
* });
* document.body.appendChild(c.getHTML());
*
* @constructor
* Creates a new instance
* @param {Object} settings
*
* @cfg {Array} [items=[]] An array with the items to be added to tyhe container. If the
* {@link PMUI.core.Container#cfg-dataItems dataItems config option} is specified then this config option will be
* ignored.
*
* Each array's item can be:
*
* - A JSON object with the config options required by an {@link PMUI.core.Element Element object}.
* - An instance of the {@link PMUI.core.Element Element class}.
*
* @cfg {String} [behavior='nobehavior'] A string that specifies the behavior for the container and its items.
* The accepted values are the same ones that the specified ones for the
* {@link PMUI.core.Container#method-setBehavior setBehavior() method}.
*
* @cfg {Array} [dataItems=null] An array in which each element is the data for the items that will be contained by
* the container. If this config option is specfied the {@link PMUI.core.Container#cfg-items items config option}
* will be ignored.
*
* @cfg {Function} [onDrop=null] The callback function to be executed everytime a draggable item is dropped on the
* container. This callback can return a boolean value or not return anything. If it returns "false" then the drop
* action will be cancelled. For info about the parameters sent to the callback function please read the
* {@link PMUI.core.Container#event-onDrop onDrop} event documentation.
*
* @cfg {Function} [onSort=null] The callback function to be executed everytime a container's item order changes.
* For info about the parameters sent to the callback function please read the
* {@link PMUI.core.Container#event-onSort onSort} event documentation.
*
* @cfg {String} [sortableItems=null] Specifies which items inside the element should be sortable in case a
* sort behavior is applied. It must be a jQuery selector.
*
* @cfg {Boolean} [disabled=false] If the interactive functions like the drag, drop, sort behaviors will be
* disabled or not.
*
* @cfg {Function|null} [onBeforeDrop=null] The callback to be executed before a draggable item that was dropped
* on the container is added.
*
* @cfg {Function|null} [onDropOut=null] The callback to be executed everytime a draggable item is drop out from
* the container to another one. To know the parameters sent to the callback function read the documentation for
* the {@link #event-onDropOut onDropOut} event.
*
* @cfg {Function|null} [onDragStart=null] The callback function to be executed everytime any of its draggable items
* starts to be dragged. For info about the parameters sent to the callback function read the documentation for
* the {@link #event-onDropOut onDropOut} event.
*
* @cfg {Function|null} [onAdd=null] The callback function be executed eveytime the {@link #event-onAdd onAdd event}.
*/
var Container = function (settings) {
Container.superclass.call(this, settings);
/**
* @property {PMUI.util.ArrayList} items
* An ArrayList object that contains all the child Items
*/
this.items = null;
/**
* @property {PMUI.util.Factory} factory
* X
*/
this.factory = null;
/**
* @property {PMUI.behavior.ContainerItemBehavior} behaviorObject The container's behavior object.
* @private
*/
this.behaviorObject = null;
/**
* @property {String} behavior The behavior's pmType being used by the object.
* @readonly
*/
this.behavior = null;
/**
* @property {HTMLElement} containmentArea The HTML area in which the items HTML will be append
* @readonly
*/
this.containmentArea = null;
/**
* @event onDragStart
* Fired when the dragging is starting on any ot its items.
* @param {PMUI.core.Container} container The container from which the draggable is starting to be dragged.
* @param {PMUI.core.Element} draggableItem The item thais starting to be dragged.
*/
this.onDragStart = null;
/**
* @event onDrop
* Fired everytime a draggable item is dropped on the container.
* @param {PMUI.core.Container} container The container on which the draggable item was dropped.
* @param {PMUI.core.Element} draggableItem The item that was dropped on the container.
* @param {Number} index The order index in which the new element was dropped.
*/
this.onDrop = null;
/**
* @event onDropOut
* Fired when a draggable item is drop out from the container to another one.
* @param {PMUI.core.Element} draggable The draggable PMUI object involved in the action.
* @param {PMUI.core.Container} origin The container the draggable item was drag from.
* @param {PMUI.core.Container} destiny The container the draggable was dropped on.
*/
this.onDropOut = null;
/**
* @event
* Fired before an item is added to the container by dropping.
* @param {PMUI.core.Element} draggableItem The container.
* @param {PMUI.core.Element} draggableItem The item that was dropped and is ready to be appended to the
* container.
* @param {Number} index The new element's order index in which it will be added.
*/
this.onBeforeDrop = null;
/**
* @event onSort
* Fired everytime a container's item order changes.
* @param {PMUI.core.Container} container The container on which the sort occur.
* @param {PMUI.core.Element} orderedElement The element which was ordered.
* @param {Number} index The new element's order index.
*/
this.onSort = null;
/**
* Specifies which items inside the element should be sortable in case a drag/sort behavior is applied.
* @type {String}
*/
this.sortableItems = null;
/**
* If the container is disabled or not. Set by the {@link #cfg-disabled disabled} config option and the
* {@link #method-disable disable()} method.
* @type {Boolean}
* @readonly
*/
this.disabled = null;
/**
* If the container's behavior is disabled or not. Set by the {@link #cfg-disabledBehavio disabledBehavior} config option and the
* {@link #method-disableBehavior disableBehavior()} method.
* @type {Boolean}
*/
this.disabledBehavior = null;
/**
* A flag that indicates if actually a massive action is being performed.
* @private
* @type {Boolean}
*/
this.massiveAction = false;
/**
* @event onAdd
* Fired everytime an item is added to the object.
* @param {PMUI.core.Container} container The container in which the item was added to.
* @param {PMUI.core.Element} item The item that was added.
* @param {Number} index The index in which the item was added.
*/
this.onAdd = null;
/**
* @property {String} dragDropBehaviorScope A draggable/droppable container with the same scope value as
* another droppable/draggable container's item will be accepted by the behavior.
*/
this.dragDropBehaviorScope = null;
this.onSortStart = null;
this.onStopDrag = null;
/**
* Restricts dragging/sorting from starting unless the mousedown occurs on the specified element(s). Only
* elements that descend from the draggable element are permitted.
* @type {String}
*/
this.dragHandler = null;
Container.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', Container);
Container.prototype.type = 'Container';
/**
* Initializes the object.
* @param {Object} settings A JSON object with the config options.
* @private
*/
Container.prototype.init = function (settings) {
var defaults = {
items: [],
behavior: 'nobehavior',
dataItems: null,
onDragStart: null,
onBeforeDrop: null,
onDrop: null,
onDropOut: null,
onSort: null,
sortableItems: '> *',
disabled: false,
onAdd: null,
onSortStart: null,
onStopDrag: null,
dragDropBehaviorScope: 'pmui-containeritem-behavior',
dragHandler: null
};
jQuery.extend(true, defaults, settings);
this.items = new PMUI.util.ArrayList();
this.setFactory(defaults.factory)
.setOnAddHandler(defaults.onAdd);
if (jQuery.isArray(defaults.dataItems)) {
this.setDataItems(defaults.dataItems);
} else {
this.setItems(defaults.items);
}
this.setSortableItems(defaults.sortableItems)
.setDragHandler(defaults.dragHandler)
.setDragDropBehaviorScope(defaults.dragDropBehaviorScope)
.setBehavior(defaults.behavior)
.setOnDragStartHandler(defaults.onDragStart)
.setOnBeforeDropHandler(defaults.onBeforeDrop)
.setOnDropHandler(defaults.onDrop)
.setOnDropOutHandler(defaults.onDropOut)
.setOnSortHandler(defaults.onSort);
this.setOnSortStartHandler(defaults.onSortStart);
this.setOnStopDragHandler(defaults.onStopDrag);
if (this.disabled) {
this.disable();
} else {
this.enable();
}
};
/**
* If specified, restricts dragging/sorting from starting unless the mousedown occurs on the specified element(s).
* Only elements that descend from the draggable element are permitted.
* @param {String} handler The selector for the handler.
* @chainable
*/
Container.prototype.setDragHandler = function (handler) {
if (!(typeof handler === 'string' || handler === null)) {
throw new Error("The parameter must be a string or null");
}
this.dragHandler = handler;
this.setBehavior(this.behavior);
return this;
};
/**
* Sets the {@link #property-dragDropBehaviorScope dragDropBehaviorScope} property.
* @param {String} scope Any string that represents a scope.
* @chainable
*/
Container.prototype.setDragDropBehaviorScope = function (scope) {
if (typeof scope !== 'string') {
throw new Error("setDragDropBehaviorScope(): The parameter must be a string.");
}
this.dragDropBehaviorScope = scope;
this.setBehavior(this.behavior);
return this;
};
/**
* Sets the handler to be executed everytime the {@link #event-onAdd onAdd} event is fired.
* @param {Function|null} handler The handler to execute, it also can be null.
* @chainable
*/
Container.prototype.setOnAddHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error('setOnAddHandler(): The parameter ust be a function or null.');
}
this.onAdd = handler;
return this;
};
/**
* Sets the callback function to be executed everytime any of its draggable items stop to be dragged.
* @param {Function|null} handler It can be a function or null.
*/
Container.prototype.setOnStopDragHandler = function (handler) {
if (handler !== null && typeof handler !== 'function') {
throw new Error('setOnStopDragHandler(): the parameter must be a function or null.');
}
this.onStopDrag = handler;
return this;
};
/**
* Sets the callback function to be executed everytime any of its draggable items starts to be dragged. For info
* about the parameters sent to the callback function read the documentation for
* the {@link #event-onDropOut onDropOut} event.
* @param {Function|null} handler It can be a function or null.
*/
Container.prototype.setOnDragStartHandler = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error('setOnDragStartHandler(): the parameter must be a function or null.');
}
this.onDragStart = handler;
return this;
};
/**
* Sets the callback function to be executed when a draggable item is dragged from the container and dropped on
* another container. This callback can return a boolean value or not return anything. If it returns "false" then
* the drop action will be cancelled. For info about the parameters sent to the callback function please read the
* {@link PMUI.core.Container#event-onDrop onDrop} event documentation.
* @param {Function|null} handler
* @chainable
*/
Container.prototype.setOnDropOutHandler = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error('setOnDropOutHandler(): the parameter must be a function or null.');
}
this.onDropOut = handler;
return this;
};
/**
* Disables the container's behavior.
* @chainable
*/
Container.prototype.disableBehavior = function () {
this.disabledBehavior = true;
this.behaviorObject.disable();
return this;
};
/**
* Enables the container's behavior.
* @chainable
*/
Container.prototype.enableBehavior = function () {
this.disabledBehavior = false;
this.behaviorObject.enable();
this.disabled = false;
this.style.removeClasses(['pmui-disabled']);
return this;
};
/**
* Disables interactive functions like the drag, drop, sort behaviors.
* @chainable
*/
Container.prototype.disable = function () {
this.disabled = true;
this.style.addClasses(['pmui-disabled']);
return this.disableBehavior();
};
/**
* Enables interactive functions like the drag, drop, sort behaviors.
* @chainable
*/
Container.prototype.enable = function () {
this.disabled = false;
this.style.removeClasses(['pmui-disabled']);
return this.enableBehavior();
};
/**
* Specifies which items inside the element should be sortable in case a drag/sort behavior is applied.
* @param {String} selector A jQuery selector.
* @chainable
*/
Container.prototype.setSortableItems = function (selector) {
this.sortableItems = selector;
return this.setBehavior(this.behavior);
};
/**
* Sets the callback function to be executed everytime a container's item order changes.
* @param {Function} handler It can be a function or null. For info about the parameters sent to the callback
* function please read the {@link PMUI.core.Container#event-onSort onSort} event documentation.
*/
Container.prototype.setOnSortHandler = function (handler) {
if (handler !== null && typeof handler !== 'function') {
throw new Error('setOnSortHandler(): the parameter must be a function or null.');
}
this.onSort = handler;
return this;
};
Container.prototype.setOnSortStartHandler = function (handler) {
if (handler !== null && typeof handler !== 'function') {
throw new Error('setOnSortStartHandler(): the parameter must be a function or null.');
}
this.onSortStart = handler;
return this;
};
/**
* Sets the callback function to be executed before a draggable item is added to the container by dropping.
* @param {Function} handler It can be a function or null. For info about the parameters sent to the callback
* function please read the {@link PMUI.core.Container#event-onBeforeDrop onBeforeDrop} event documentation.
*/
Container.prototype.setOnBeforeDropHandler = function (handler) {
if (handler !== null && typeof handler !== 'function') {
throw new Error('setOnDropHandler(): the parameter must be a function or null.');
}
this.onBeforeDrop = handler;
return this;
};
/**
* Sets the callback function to be executed everytime a draggable item is dropped on the container.
* @param {Function} handler It can be a function or null. For info about the parameters sent to the callback
* function please read the {@link PMUI.core.Container#event-onDrop onDrop} event documentation.
*/
Container.prototype.setOnDropHandler = function (handler) {
if (handler !== null && typeof handler !== 'function') {
throw new Error('setOnDropHandler(): the parameter must be a function or null.');
}
this.onDrop = handler;
return this;
};
/**
* Adds a new item but using raw data to create it.
* @param {Object} dataItem A JSON object with the data to create the new object.
* @param {Number} [index] The position index in which the data item will be inserted.
*/
Container.prototype.addDataItem = function (dataItem, index) {
var args,
i;
if (typeof dataItem !== 'object') {
throw new Error('addDataItem(): The parameter must be an object.');
}
args = [{
data: dataItem
}];
for (i = 1; i < arguments.length; i += 1) {
args.push(arguments[i]);
}
this.addItem.apply(this, args);
return this;
};
/**
* Sets the data toi be used to create every item to be contained by the object.
* @param {Array} dataItems An array in which each element is an object with the data for each of the items.
*/
Container.prototype.setDataItems = function (dataItems) {
var i;
if (jQuery.isArray(dataItems)) {
this.clearItems();
this.massiveAction = true;
for (i = 0; i < dataItems.length; i += 1) {
this.addDataItem(dataItems[i]);
}
this.massiveAction = false;
this.paintItems();
}
return this;
};
/**
* Returns the html element in which the items are appended.
* @return {HTMLElement}
*/
Container.prototype.getContainmentArea = function () {
this.getHTML();
return this.containmentArea;
};
/**
* Returns all the container's items the behavior will be applied on.
* @return {Array} An array of items.
* @private
*/
Container.prototype.getBehavioralItems = function () {
return this.getItems();
};
/**
* Enables/disables one, two, all of the following behaviors:
*
* - "drag", allows to drag items from the object, also them can be dropped on another containers that have enables
* the "drop" behavior.
* - "drop", allows to drop draggable items from another container on the object.
* - "sort", allows reorder the items in the object. Combined with "drop" behavior makes the object capable to
* receive objects (drop) in a specific position.
* @param {String} behavior The behavior to be set, the accepted values are:
*
* - "drag", enables the dragging functionality.
* - "dragclone", enables the clone-dragging functionality.
* - "drop", enables the dropping functionality.
* - "sort", enabled the sorting functionality.
* - "dragdrop", enables both dragging and dropping functionality.
* - "dragsort", enables both dragging and sorting functionality.
* - "dropsort", enables both dropping and sorting functionality.
* - "dragdropsort", enables the three behaviors: dragging, dropping and sorting.
* - "nobehavior", disables all the behaviors.
* @chainable
*/
Container.prototype.setBehavior = function (behavior, index) {
var obj,
behaviorObject,
factory;
if (typeof behavior === 'string') {
factory = new PMUI.behavior.ContainerItemBehaviorFactory();
obj = {
pmType: behavior,
targetObject: this,
scope: this.dragDropBehaviorScope,
handle: this.dragHandler
};
if (this.behaviorObject) {
this.behaviorObject.detachBehavior();
}
behaviorObject = factory.make(obj);
if (behaviorObject) {
this.behaviorObject = behaviorObject;
this.behavior = behavior;
}
if (this.html) {
if (index !== undefined) {
this.behaviorObject.attachBehavior(index);
} else {
this.behaviorObject.attachBehavior();
}
}
}
return this;
};
Container.prototype.addingBehaviorsElement = function (behavior, index) {
var obj,
behaviorObject,
factory;
if (typeof behavior === 'string') {
factory = new PMUI.behavior.ContainerItemBehaviorFactory();
obj = {
pmType: behavior,
targetObject: this,
scope: this.dragDropBehaviorScope,
handle: this.dragHandler
};
if (this.behaviorObject) {
this.behaviorObject.detachBehavior();
}
behaviorObject = factory.make(obj);
if (behaviorObject) {
this.behaviorObject = behaviorObject;
this.behavior = behavior;
}
if (this.html) {
if (index !== undefined) {
this.behaviorObject.attachBehavior(index);
} else {
this.behaviorObject.attachBehavior();
}
}
}
return this;
};
/**
* Sets the "factory" property with a {PMUI.util.Factory} object
* @param {Object} factory
* @chainable
* @private
*/
Container.prototype.setFactory = function (factory) {
if (factory instanceof PMUI.util.Factory) {
this.factory = factory;
} else {
this.factory = new PMUI.util.Factory(factory);
}
return this;
};
/**
* Removes a child element from the object
* @param {PMUI.core.Element|String|Number} item It can be a string (id of the child to remove),
a number (index of the child to remove) or a {Element} object.
* @chainable
*/
Container.prototype.removeItem = function (item) {
var itemToRemove,
parent;
if (item instanceof PMUI.core.Element) {
itemToRemove = item;
} else {
if (typeof item === 'string') {
itemToRemove = this.items.find("id", item.id);
} else if (typeof item === 'number') {
itemToRemove = this.items.get(item);
}
}
if (itemToRemove) {
parent = itemToRemove.parent;
if (parent) {
parent.items.remove(itemToRemove);
} else {
this.items.remove(itemToRemove);
}
itemToRemove.parent = null;
if (this.html) {
jQuery(itemToRemove.html).detach();
}
}
if (!this.items.getSize()) {
this.style.addClasses(['pmui-empty']);
}
if (this.massiveAction) {
return this;
}
return this;
};
/**
* Removes all the child items
* @chainable
*/
Container.prototype.clearItems = function () {
this.massiveAction = true;
while (this.items.getSize() > 0) {
this.removeItem(0);
}
this.massiveAction = false;
return this;
};
/**
* Returns true if the item, used as the unique method parameter, is a direct child of the current Container object,
otherwise returns false
* @param {PMUI.core.Element} item The target child object
* @return {Boolean}
*/
Container.prototype.isDirectParentOf = function (item) {
return this.items.indexOf(item) >= 0;
};
/**
* Returns true if the supplied argument can be accepted as an item of the container.
* @param {PMUI.core.Element} obj The element to be evaluated.
* @return {Boolean}
*/
Container.prototype.canContain = function (obj) {
return obj instanceof PMUI.core.Element ? this.factory.isValidClass(obj) : false;
};
/**
* Paint a container's item (puts its HTML into the container's HTML).
* @param {PMUI.core.Element} item The item to add.
* @chainable
* @private
*/
Container.prototype.paintItem = function (item, index) {
var referenceItem;
if (this.html) {
if (index !== null && index !== undefined) {
referenceItem = this.items.get(index + 1);
if (referenceItem) {
this.containmentArea.insertBefore(item.getHTML(), referenceItem.getHTML());
} else {
this.containmentArea.appendChild(item.getHTML());
}
} else {
this.containmentArea.appendChild(item.getHTML());
}
if (this.eventsDefined) {
item.defineEvents();
}
index = this.items.indexOf(item);
this.setBehavior(this.behavior, index);
}
return this;
};
/**
* Paint all the container items.
* @chainable
* @private
*/
Container.prototype.paintItems = function () {
var items,
i;
if (this.containmentArea) {
items = this.items.asArray()
for (i = 0; i < items.length; i += 1) {
this.paintItem(items[i]);
}
}
return this;
};
/**
* Adds an child item to the object.
* @param {PMUI.core.Element|Object} item It can be one of the following data types:
* - {PMUI.core.Element} the object to add.
* - {Object} a JSON object with the settings for the Container to be added.
* @param {Number} [index] An index in which the item will be added.
* @chainable
*/
Container.prototype.addItem = function (item, index) {
var itemToBeAdded;
if (this.factory) {
itemToBeAdded = this.factory.make(item);
}
if (itemToBeAdded && !this.isDirectParentOf(itemToBeAdded)) {
if (index !== null && index !== undefined) {
this.items.insertAt(itemToBeAdded, index);
} else {
this.items.insert(itemToBeAdded);
}
itemToBeAdded.parent = this;
if (!this.massiveAction) {
this.paintItem(itemToBeAdded, index);
}
if (typeof this.onAdd === 'function') {
index = typeof index === 'number' ? index : this.items.getSize() - 1;
this.onAdd(this, this.getItem(index), index);
}
}
return this;
};
/**
* Clear all the object's current child items and add new ones
* @param {Array} items An array where each element can be a {Element} object or a JSON object
where is specified the setting for the child item to be added
* @chainable
*/
Container.prototype.setItems = function (items) {
var i;
if (jQuery.isArray(items)) {
this.clearItems();
this.massiveAction = false;
for (i = 0; i < items.length; i += 1) {
this.addItem(items[i]);
}
}
return this;
};
/**
* Returns an array with all the child elements
* @return {Array}
*/
Container.prototype.getItems = function () {
return this.items.asArray();
};
/**
* Returns one single item based on the id or index position
* @param {String|Number} id If the parameter is a string then
it will be take as the id for the element to find and return,
but if the element is a Number it returns the object iwth that
index position
* @return {PMUI.core.Element}
*/
Container.prototype.getItem = function (i) {
var t;
if (typeof i === 'number') {
t = this.items.get(i);
} else if (typeof i === 'string') {
t = this.items.find('id', i);
} else if (this.items.indexOf(i) >= 0) {
t = i;
}
return t;
};
/**
* Returns the index for the specified item.
* @param {PMUI.core.Element|String} item It can be:
*
* - {@link PMUI.core.Element}
* - String, it will be interpreted as the items's id.
* @return {Number}
*/
Container.prototype.getItemIndex = function (item) {
var evaluatedItem;
if (item instanceof PMUI.core.Element) {
evaluatedItem = item;
} else if (typeof item === 'number') {
evaluatedItem = this.items.get(item);
} else if (typeof item === 'string') {
evaluatedItem = this.items.find("id", item);
}
return this.items.indexOf(evaluatedItem);
};
/**
* Creates the object's HTML element
* @return {HTMLElement}
*/
Container.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
Container.superclass.prototype.createHTML.call(this);
this.containmentArea = this.html;
this.paintItems();
this.style.applyStyle();
this.setBehavior(this.behavior);
return this.html;
};
/**
* Moves an object's item from a position to another.
* @param {PMUI.core.Element} item The item to move.
* @param {Number} index The index position in which the item will be moved.
* @chainable
*/
Container.prototype.moveItem = function (item, index) {
var items = this.items,
currentIndex,
referenceObject;
item = this.getItem(item);
if (item instanceof PMUI.core.Element && this.isDirectParentOf(item)) {
currentIndex = items.indexOf(item);
items = items.asArray();
referenceObject = this.items.get(index + (currentIndex < index ? 1 : 0));
item = items.splice(currentIndex, 1)[0];
this.items.remove(item);
this.items.insertAt(item, index);
if (this.html) {
if (index === this.items.getSize() - 1) {
this.containmentArea.appendChild(item.getHTML());
} else {
this.containmentArea.insertBefore(item.getHTML(), referenceObject.getHTML());
}
}
}
return this;
};
/**
* Returns the function to be executed when an accepted draggable is dragged over the droppable area.
* @return {Function}
*/
Container.prototype.onDragOver = function () {
return function () {
};
};
/**
* Returns the function to be executed when the items order has been changed.
* @return {Function}
* @private
*/
Container.prototype.onSortingChange = function () {
var that = this;
return function (e, ui) {
var itemHTML = ui.item.get(0),
item = PMUI.getPMUIObject(itemHTML),
newIndex;
newIndex = jQuery(that.getContainmentArea()).find('>*').index(itemHTML);
that.moveItem(item, newIndex);
if (typeof that.onSort === 'function') {
that.onSort(that, item, newIndex);
}
};
};
/**
* Handler to be executed everytime the mouse enter a position over any container's draggable/sortable item.
* @param {PMUI.core.Element} draggable The draggable PMUI object.
* @chainable
*/
Container.prototype.onDraggableMouseOver = function (draggable) {
return this;
};
/**
* Handler to be executed everytime the mouse leaves a position over any container's draggable/sortable item.
* @param {PMUI.core.Element} draggable The draggable PMUI object.
* @chainable
*/
Container.prototype.onDraggableMouseOut = function (draggable) {
return this;
};
/**
* Executes children events defined
* @return {PMUI.core.Container}
* @chainable
*/
Container.prototype.defineEvents = function () {
var j,
children,
that = this;
Container.superclass.prototype.defineEvents.call(this);
if (that.items.getSize() > 0) {
children = that.getItems();
for (j = 0; j < children.length; j += 1) {
children[j].defineEvents();
}
}
return this;
};
PMUI.extendNamespace('PMUI.core.Container', Container);
// Publish to NodeJS environment
if (typeof exports !== 'undefined') {
module.exports = Container;
}
}());
(function () {
/**
* @class PMUI.core.Item
* @extends PMUI.core.Container
* Create a items container
*
* @constructor
* Creates a new instance of the class
* @param {Object} options Contructor object
*/
var Item = function (options) {
Item.superclass.call(this, options);
/**
* Parent object associated to this item
* @type {Object}
*/
this.parent = null;
Item.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.core.Container', Item);
/**
* Defines the object's type
* @type {String}
*/
Item.prototype.type = "Item";
/**
* Defines the object's family
* @type {String}
*/
Item.prototype.family = "Item";
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
Item.prototype.init = function (options) {
var defaults = {
parent: null
};
jQuery.extend(true, defaults, options);
this.setParent(defaults.parent);
};
/**
* Sets the parent object of the current item
* @param {Object} parent Parent object
*/
Item.prototype.setParent = function (parent) {
this.parent = parent;
return this;
};
/**
* Returns the parent object pointer
* @return {Object}
*/
Item.prototype.getParent = function () {
return this.parent;
};
/**
* Removes the item from its parent container.
* @chainable
*/
Item.prototype.remove = function () {
if (this.parent) {
this.parent.removeItem(this);
}
return this;
};
PMUI.extendNamespace('PMUI.core.Item', Item);
if (typeof exports !== 'undefined') {
module.exports = Item;
}
}());
(function () {
/**
* @class PMUI.core.Panel
* Handles panels to be inserted into containers, it is composed by three main elements: header, body and footer
* @abstract
* @extends {PMUI.core.Container}
*
* //Remember, this is an abstract class so it shouldn't be instantiate,
* //anyway we are instantiating it just for this example
* var p1, p2, panel;
* p1 = new PMUI.core.Panel({
* height : 140,
* width : 90,
* display : 'inline-block',
* style : {
* cssProperties : {
* 'background-color':'red'
* }
* }
* });
* p2 = new PMUI.core.Panel({
* height : 140,
* width : 90,
* display : 'inline-block',
* style : {
* cssProperties : {
* 'background-color':'yellow'
* }
* }
* });
* panel = new PMUI.core.Panel({
* height : 150,
* width : 200,
* display : 'block',
* items : [
* p1,
* p2
* ],
* layout : 'vbox',
* padding : "5px",
* borderWidth : "1px",
* border : true
* });
* document.body.appendChild(panel.getHTML());
*
* @constructor
* Creates a new instacne of the object
* @param {Object} settings
*
* @cfg {Object|PMUI.core.Panel|null} [panel=null] The panel to be appended into the current Panel. It can be an
* object literal with the {@link PMUI.core.Panel config options for Panel} or an instance of
* {@link PMUI.core.Panel Panel} or null.
* @cfg {PMUI.core.Container} [parent=null] The panel's parent element.
* @cfg {String|PMUI.layout.Layout} [layout='box'] The layout to be applied to the panel, the layout determines the
* render position for the panel's items. The possible layouts are:
*
* - box: this applies no layout to the panel. To set the layout to box if you use a string as value for the config
* option use "box", in the other case, if you use an object, use an instance of {@link PMUI.layout.Box Box}.
* - hbox: this applies a layout that renders the panel's items side by side using the items's
* {@link PMUI.core.Element#property-propotion proportion property} to calculate the width for each item. To set
* this layout to the panel, if you are using a string as the value for the config option, use "hbox", in the other
* case, if you are using an object use an instance of {@link PMUI.layout.HBox HBox}.
* - vbox: this applies a layout that renders the panel's items in vertical position using the items's
* {@link PMUI.core.Element#property-propotion proportion property} to calculate the height for each item. To set
* this layout to the panel, if you are using a string as the value for the config option, use "vbox", in the other
* case, if you are using an object use an instance of {@link PMUI.layout.VBox VBox}.
* @cfg {String|Number} [padding=""] Sets the css padding property for the panel. It can be a number or and string
* with the format ##px or an empty string. If you use an empty string as the parameter, the padding will be set
* dinamically from the used css file.
* @cfg {String|Number} [borderWidth=""] Sets the border width for the panel. It can be a number or a string with
* the format '##px' or an empty string. If you use an empty string as the parameter, the border width will be set
* dinamically from the used css file.
*/
var Panel = function (settings) {
Panel.superclass.call(this, settings);
/**
* The child {Panel} object
* @type {PMUI.core.Panel}
*/
this.panel = null;
/**
* The Panel's parent object
* @type {PMUI.core.Container}
*/
this.parent = null;
/**
* A {Layout} object which handles the position layout for the object's direct child elements
* @property {PMUI.layout.Layout}
*/
this.layout = null;
/**
* @property {String} [topPadding=""]
* The value for the css padding-top property to be applied to the object's html.
* @readonly
*/
this.topPadding = null;
/**
* @property {String} [leftPadding=""]
* The value for the css padding-left property to be applied to the object's html.
* @readonly
*/
this.leftPadding = null;
/**
* @property {String} [bottomPadding=""]
* The value for the css padding-bottom property to be applied to the object's html.
* @readonly
*/
this.bottomPadding = null;
/**
* @property {String} [rightPadding=""]
* The value for the css padding-right property to be applied to the object's html.
* @readonly
*/
this.rightPadding = null;
/**
* @property {String} [borderWidth=""] A string with the format ##px (where ## is a number).
* It determines the panel's border width.
* @readonly
*/
this.borderWidth = null;
/**
* @property {Boolean} [border=false] If the border is being shown.
* @readonly
*/
this.border = null;
Panel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Container', Panel);
/**
* Defines the object's type
* @type {String}
*/
Panel.prototype.type = 'Panel';
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
Panel.prototype.init = function (settings) {
var defaults = {
panel: null,
parent: null,
layout: 'box',
padding: "",
borderWidth: ""
};
jQuery.extend(true, defaults, settings);
this.setPanel(defaults.panel)
.setParent(defaults.parent)
.setLayout(defaults.layout)
.setPadding(defaults.padding)
.setBorderWidth(defaults.borderWidth);
};
/**
* Applies the css classes and ptoperties to the element's html which the object is related
* @chainable
*/
Panel.prototype.applyStyle = function () {
if (this.html) {
this.style.applyStyle();
this.style.addProperties({
display: this.visible ? this.display : "none",
position: this.positionMode,
left: this.x,
top: this.y,
width: this.width,
height: this.height,
zIndex: this.zOrder,
"padding-top": this.topPadding,
"padding-right": this.rightPadding,
"padding-bottom": this.bottomPadding,
"padding-left": this.leftPadding,
"border-width": this.borderWidth,
"border-style": this.borderWidth !== "" ? "solid" : "",
"box-sizing": "border-box"
});
}
return this;
};
/**
* Sets the border width for the panel
* @param {String|Number} width It can be a number or a string with the format '##px' or an empty string.
* If you use an empty string as the parameter, the border width will be set dinamically from the used css file.
*/
Panel.prototype.setBorderWidth = function (width) {
var error = false;
if (typeof width === 'number') {
this.borderWidth = width + "px";
} else if (typeof width === 'string') {
width = jQuery.trim(width);
if (/^\d+(\.\d+)?px$/.test(width) || width === "") {
this.borderWidth = width;
} else {
error = true;
}
} else {
error = false;
}
if (error) {
throw new Error("setBorderWidth(): invalid parameter.");
} else {
this.applyStyle();
if (this.layout) {
this.layout.applyLayout();
}
}
return this;
};
/**
* Returns the border width from the object
* @return {String}
*/
Panel.prototype.getBorderWidth = function () {
return this.borderWidth || this.style.getProperty("border-width");
};
/**
* Sets the padding values for the panel.
* @param {String|Number} padding It can be a number or and string with the format ##px or an empty string.
* If you use an empty string as the parameter, the padding will be set dinamically from the used css file.
*/
Panel.prototype.setPadding = function (padding) {
var error = false;
if (typeof padding === 'string') {
padding = jQuery.trim(padding);
padding = padding.split(/\s+/).join(" ");
if (/^(\d+(\.\d+)?(px|%)\s)+$/.test(padding + " ")) {
padding = padding.match(/\d+(\.\d+)?(px|%)/g);
switch (padding.length) {
case 4:
this.topPadding = padding[0];
this.rightPadding = padding[1];
this.bottomPadding = padding[2];
this.leftPadding = padding[3];
break;
case 3:
this.topPadding = padding[0];
this.rightPadding = this.leftPadding = padding[1];
this.bottomPadding = padding[2];
break;
case 2:
this.topPadding = this.bottomPadding = padding[0];
this.rightPadding = this.leftPadding = padding[1];
break;
case 1:
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = padding[0];
break;
}
} else if (padding === "") {
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = "";
} else {
error = true;
}
} else if (typeof padding === 'number') {
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = padding + "px";
} else {
error = true;
}
if (error) {
throw new Error("setPadding(): Invalid parameter.");
} else {
this.applyStyle();
if (this.layout) {
this.layout.applyLayout();
}
}
return this;
};
/**
* Returns an array with the padding values in the following order: [top, right, bottom, left].
* @return {Array}
*/
Panel.prototype.getPadding = function () {
return [
this.topPadding || this.style.getProperty("padding-top"),
this.rightPadding || this.style.getProperty("padding-right"),
this.bottomPadding || this.style.getProperty("padding-bottom"),
this.leftPadding || this.style.getProperty("padding-left")
];
};
/**
* Sets the panel object
* @param {Object} panel Panel object
*/
Panel.prototype.setPanel = function (panel) {
if (panel) {
if (panel instanceof Panel) {
this.panel = panel;
} else if (typeof panel === 'object') {
this.panel = new Panel(panel);
}
if (this.html) {
jQuery(this.html).empty().append(panel.getHTML());
}
}
return this;
};
/**
* Returns the object's html usable width.
* @return {Number|String} It can return a Number or an String with a css property like "auto".
*/
Panel.prototype.getUsableWidth = function () {
var padding = this.getPadding(), border = parseInt(this.getBorderWidth(), 10) * 2;
if (isNaN(this.getWidth())) {
return this.getWidth();
}
return this.getWidth() - (parseInt(padding[1], 10) || 0) - (parseInt(padding[3], 10) || 0) - (border || 0);
};
/**
* Returns the object's html usable height
* @return {Number|String} It can return a Number or an String with a css property like "auto".
*/
Panel.prototype.getUsableHeight = function () {
var padding = this.getPadding(), border = parseInt(this.getBorderWidth(), 10) * 2;
if (isNaN(this.getHeight())) {
return this.getHeight();
}
return this.getHeight() - (parseInt(padding[0], 10) || 0) - (parseInt(padding[2], 10) || 0) - (border || 0);
};
/**
* Sets the parent object
* @param {Object} parent
*/
Panel.prototype.setParent = function (parent) {
this.parent = parent;
return this;
};
/**
* Sets the layout for the panel.
* @param {Object|String} layout Layout object
*/
Panel.prototype.setLayout = function (layout) {
var factory = new PMUI.layout.LayoutFactory();
if (!(typeof layout === "string")) {
throw new Error("setLayout(): Property values sent an invalid, expected 'box' , 'vbox' or 'hbox'");
}
this.layout = factory.make(layout);
this.layout.setContainer(this);
if (this.html) {
this.layout.applyLayout();
}
return this;
};
/**
* Add an item to the panel.
* @param {PMUI.core.Element|Object} item.
* @param {Number} [index] The index position in which the item will be appended.
* It can be a valid JSON object or an object that inherits from {@link PMUI.core.Element Element}.
* @chainable
*/
Panel.prototype.addItem = function (item, index) {
Panel.superclass.prototype.addItem.call(this, item, index);
if (this.layout) {
this.layout.applyLayout();
}
return this;
};
/**
* Sets the width for the Panel's HTML element
* @param {Number|String} width It can be a number or a string.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
* @chainable
*/
Panel.prototype.setWidth = function (width) {
Panel.superclass.prototype.setWidth.call(this, width);
if (this.layout) {
this.layout.applyLayout();
}
return this;
};
/**
* Sets the height for the Panel's HTML element
* @param {Number|String} height It can be a number or a string.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
* @chainable
*/
Panel.prototype.setHeight = function (height) {
Panel.superclass.prototype.setHeight.call(this, height);
if (this.layout) {
this.layout.applyLayout();
}
return this;
};
/**
* Returns the object pointed to the parent
* @return {Object}
*/
Panel.prototype.getParent = function () {
return this.parent;
};
/**
* Creates the HTML element for the Panel
* @return {HTMLElement}
*/
Panel.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
Panel.superclass.prototype.createHTML.call(this);
this.applyStyle();
if (this.layout) {
this.layout.applyLayout();
}
return this.html;
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Panel;
}
PMUI.extendNamespace('PMUI.core.Panel', Panel);
}());
(function () {
/**
* @class PMUI.control.Control
* Class that encapsulates the basic bahavior for a form control.
* Since this class is abstract it shouldn't be instantiated.
* @extends PMUI.core.Element
* @abstract
*
* Usage example (only for subclasses since this is an abstract class):
*
* //Remember, this is an abstract class so it shouldn't be instantiate,
* //anyway we are instantiating it just for this example
* var myControl = new PMUI.control.Control({
* name: "phrase",
* value: "Sometimes the sun goes down!",
* disabled: false,
* onChange: function(currentValue, previousValue) {
* console.log(this.name + " has changed!");
* console.log("Its previous value was: " + previousValue);
* console.log("Its current value is: " + currentValue);
* }
* });
*
* @constructor
* While it is true that this class must not be instantiated,
* it is useful to mention the settings parameter for the constructor
* function (which will be used for the non abstract subclasses).
* The parameter for the constructor will be a JSON object whose properties are specified in the
* {@link PMUI.control.Control#cfg-disabled Config Options section}.
*
* @cfg {String} [name=The object's ID] The name for the control. If it is not specified then the object's id is
* used.
* @cfg {String} [value=""] The initial value to be set to the control.
* @cfg {PMUI.form.Field} [field=null] The parent {@link PMUI.form.Field Field} object for the control.
* @cfg {Boolean} [disabled=false] A boolean value which determines if the control will be enabled or not.
* @cfg {Function} [onChange=null] A callback function to be invoked when the control changes. For info about the
* received parameters please read the {@link PMUI.control.Control#event-onChange onChange event} section.
* @cfg {Function} [onBeforeChange=null] A callback function to be invoked after the control's value changes. The
* function can return false to avoid the value changing. For info about the received parameter please read the
* {@link #event-onBeforeChange onBeforeChange event} documentation.
*/
var Control = function (settings) {
Control.superclass.call(this, settings);
/**
* @property {String} [name=id] The control's name, it defaults to null.
* @readonly
*/
this.name = null;
/**
* @property {String} [value=""] The control's value.
* @readonly
*/
this.value = null;
/**
* @property {PMUI.form.Field} [field=null] The {@link PMUI.form.Field Field}
object the current object belongs to.
* @readonly
*/
this.field = null;
/**
* @property {Boolean} [disabled=false] If the control is disabled or not.
* @readonly
*/
this.disabled = null;
/**
* @event onChange
* Fired when the control's value is changed.
* @param {String} newValue The new control's value.
* @param {String} prevValue The previous control's value.
*/
this.onChange = null;
/**
* @event onBeforeChange
* Fired after the control's value changes.
* @param {String} newValue The new control's value.
* @param {String} prevValue The previous vontrol's value.
*/
this.onBeforeChange = null;
Control.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', Control);
Control.prototype.type = "Control";
/**
* Initialize the object.
* @param {Object} settings The config options.
*/
Control.prototype.init = function (settings) {
var defaults = {
name: this.id,
value: "",
field: null,
disabled: false,
onBeforeChange: null,
onChange: null
};
$.extend(true, defaults, settings);
this.setName(defaults.name)
.setValue(defaults.value)
.setField(defaults.field)
.disable(defaults.disabled)
.setOnBeforeChangeHandler(defaults.onBeforeChange)
.setOnChangeHandler(defaults.onChange);
};
/**
* Sets the callback function to be executed when the {@link #event-onBeforeChange onBeforeChange event} fires.
* @param {Function|null} handler
* @chainable
*/
Control.prototype.setOnBeforeChangeHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error('setOnBeforeChangeHandler(): The parameter must be a function or null.');
}
this.onBeforeChange = handler;
return this;
};
/**
* Sets the control's name.
* @param {String} name
* @chainable
*/
Control.prototype.setName = function (name) {
if (typeof name === "string" || typeof name === "number") {
this.name = name.toString();
if (this.html) {
this.html.setAttribute("name", name);
}
} else {
throw new Error("The setName() method only accepts string or number values");
}
return this;
};
/**
* Returns the control's name.
* @return {String}
*/
Control.prototype.getName = function () {
return this.name;
};
/**
* Sets the control's value.
* @param {String} value
* @chainable
*/
Control.prototype.setValue = function (value) {
if (typeof value !== 'undefined') {
this.value = value.toString();
} else {
throw new Error("setValue(): a parameter is required.");
}
return this;
};
/**
* Returns the control's value.
* @return {String}
*/
Control.prototype.getValue = function () {
return this.value;
};
/**
* Sets the control's parent {@link PMUI.form.Field Field} object.
* @param {PMUI.form.Field}
* @chainable
*/
Control.prototype.setField = function (field) {
if (field instanceof PMUI.form.Field) {
this.field = field;
}
return this;
};
/**
* Returns the control's parent {@link PMUI.form.Field Field} object.
* @return {PMUI.form.Field}
*/
Control.prototype.getField = function () {
return this.field;
};
/**
* Disables/enables the control.
* @param {Boolean} disable If the value is evaluated as true then the control is disabled,
otherwise the control is enabled.
* @chainable
*/
Control.prototype.disable = function (disable) {
this.disabled = !!disable;
return this;
};
/**
* Returns true if the control is enabled, if it don't then it returns false
* @return {Boolean}
*/
Control.prototype.isEnabled = function () {
return !this.disabled;
};
/**
* Sets the callback function which will be executed everytime the control changes (using the interface).
*
* The callback function will receive two parameters:
*
* - first argument: the current control's value.
* - second argument: the previous control's value.
*
*
* //Remember, this is an abstract class so it shouldn't be instantiate,
* //anyway we are instantiating it just for this example
* var myControl = new PMUI.control.Control({
* name: "phrase",
* value: "Sometimes the sun goes down!",
* disabled: false
* });
*
* myControl.setOnChangeHandler(function(currentValue, previousValue) {
* console.log(this.name + " has changed!");
* console.log("Its previous value was: " + previousValue);
* console.log("Its current value is: " + currentValue);
* });
*
* @chainable
*/
Control.prototype.setOnChangeHandler = function (handler) {
if (typeof handler === 'function') {
this.onChange = handler;
}
return this;
};
/**
* Returns the control's value directly from the control's HTML element.
*
* This method is used internally by the object, so in most of the cases you won't need to invocated.
* To get the control's value please use the {@link PMUI.control.Control#getValue getValue()} method.
*
* Since this is an abstract method, it must be implemented in its non-abstract subclasses
* @return {String}
* @abstract
*/
Control.prototype.getValueFromRawElement = function () {
throw new Error("Calling getValueFromRawElement() from PMUI.control.Control: this is an abstract method!");
};
/**
* A method which is called everytime the control changes.
*
* This method is used internally by the object, so in most of the cases you won't need to invocated.
* To execute instructions when the control changes, please use the
{@link PMUI.control.Control#setOnChangeHandler setOnChangeHandler()} method.
*
* @chainable
* @private
*/
Control.prototype.onChangeHandler = function () {
var prevValue = this.value, newValue = this.getValueFromRawElement(), resCallback;
if (typeof this.onBeforeChange === 'function' && newValue !== prevValue) {
resCallback = this.onBeforeChange(newValue, prevValue);
if (resCallback === false) {
newValue = prevValue;
this.setValue(newValue);
}
}
this.value = newValue;
if (typeof this.onChange === 'function' && this.value !== prevValue) {
this.onChange(this.value, prevValue);
}
return this;
};
/**
* Attach the event listeners for the control's HTML element.
*
* Since this is an abstract method, it must be implemented in its non-abstract subclasses
* @abstract
* @chainable
*/
Control.prototype.attachListeners = function () {
throw new Error("Calling attachListeners() from PMUI.control.Control: this is an abstract method!");
};
/**
* Creates the HTML element for the control.
*
* Since this is an abstract method, it must be implemented in its non-abstract subclasses
* @return {HTMLElement}
* @abstract
*/
Control.prototype.createHTML = function () {
Control.superclass.prototype.createHTML.call(this);
return this.html;
};
/**
* Returns the HTML element for the control
* @return {HTMLElement}
*/
Control.prototype.getHTML = function () {
if (!this.html) {
this.html = this.createHTML();
}
return this.html;
};
/**
* @method setFocus
* set the focus on control
* @chainable
*/
Control.prototype.setFocus = function () {
if (this.html && this.html.focus) {
this.html.focus();
}
};
PMUI.extendNamespace('PMUI.control.Control', Control);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Control;
}
}());
(function () {
/**
* @class PMUI.control.HTMLControl
* Class that encapsulates the HTML native control's behavior.
* Since this class is abstract it shouldn't be instantiated.
* @extends PMUI.control.Control
* @abstract
*
* @constructor
* While it is true that this class must not be instantiated,
* it is useful to mention that the settings parameter for the constructor function
* has the same structure that the one for the superclass
* (for more info see the {@link PMUI.control.Control#constructor constructor} method for Control class).
* @param {Object} [settings=null] A JSON object with the config options.
*/
var HTMLControl = function (settings) {
HTMLControl.superclass.call(this, settings);
/**
* @property {String} [elementTag='input'] The tag for the HTML element to be created.
* @private
*/
this.elementTag = 'input';
};
PMUI.inheritFrom('PMUI.control.Control', HTMLControl);
HTMLControl.prototype.type = "HTMLControl";
/**
* Sets the value for the control
* @param {String} value
* @chainable
*/
HTMLControl.prototype.setValue = function (value) {
HTMLControl.superclass.prototype.setValue.call(this, value);
if (this.html && this.html.value !== this.value) {
this.html.value = this.value;
}
return this;
};
/**
* Disables/enables the control
* @param {Boolean} disable If the value is evaluated as true then the control
is disabled, otherwise the control is enabled.
* @chainable
*/
HTMLControl.prototype.disable = function (disable) {
HTMLControl.superclass.prototype.disable.call(this, disable);
if (this.html) {
this.html.disabled = this.disabled;
}
return this;
};
/**
* Returns the control's value directly from the control's HTML element.
*
* This method is used internally by the object, so in most of the cases you won't need to invocated.
* To get the control's value please use the {@link PMUI.control.Control#getValue getValue()} method.
* @return {String}
*/
HTMLControl.prototype.getValueFromRawElement = function () {
return this.html.value;
};
/**
* Attach the event listeners for the control's HTML element
* @chainable
*/
HTMLControl.prototype.defineEvents = function () {
var that = this;
HTMLControl.superclass.prototype.defineEvents.call(this);
if (this.html) {
this.addEvent('blur').listen(this.html, function () {
that.onChangeHandler();
});
this.addEvent('keydown').listen(this.html, function (e) {
if (e.which === PMUI.keyCodeF5) {
this.blur();
e.preventDefault();
window.location.reload(true);
}
});
}
return this;
};
/**
* Creates the HTML element for the control.
* @return {HTMLElement}
*/
HTMLControl.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
HTMLControl.superclass.prototype.createHTML.call(this);
this.setName(this.name)
.setValue(this.value)
.disable(this.disabled);
return this.html;
};
PMUI.extendNamespace('PMUI.control.HTMLControl', HTMLControl);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = HTMLControl;
}
}());
(function () {
/**
* @class PMUI.control.TextControl
* Class for handle the HTML native text input control.
* @extends PMUI.control.HTMLControl
*
* Quick usage example:
*
* @example
* myTextbox = new PMUI.control.TextControl({
* name: "my_name",
* value: "John Doe",
* maxLength: 12,
* placeholder: "insert your name here",
* disabled: false,
* onChange: function(currentValue, previousValue) {
* if(previousValue !== "") {
* alert("Your name is not \"" + previousValue + "\" anymore.\nNow it's \"" + currentValue + "\"");
* } else {
* alert("Now your name is " + currentValue);
* }
* }
* });
*
* document.body.appendChild(myTextbox.getHTML());
*
* @constructor
* Creates a new instance of the TextControl object.
* @param {Object} [settings=null] A JSON object with the config options.
*
* @cfg {String} [placeholder=""] A string to be used as the control's placeholder.
* @cfg {Number} [maxLength=524288] A number which specifies the maximum character length the control can accept.
* @cfg {Function} [onKeyUp=null] A callback function to be called every time a pressed key is released on the
* control. For info about the callback parameters please read the
* {@link PMUI.control.TextControl#event-onKeyUp onKeyUp event} section.
*/
var TextControl = function (settings) {
TextControl.superclass.call(this, settings);
/**
* @property {String} [placeholder=""]
* The control's placeholder (the text to be shown inside the control when there is not any text in it).
* @readonly
*/
this.placeholder = null;
/**
* @property {Number} [maxLength=524288] The maximum character length the control accepts.
* @readonly
*/
this.maxLength = null;
/**
* @event onKeyUp
* Fired when a pressed key is released.
* @param {Object} event The event object.
*/
this.onKeyUp = null;
TextControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', TextControl);
TextControl.prototype.type = "TextControl";
/**
* Initializes the object.
* @param {Object} [settings=null] A JSON object with the config options.
* @private
*/
TextControl.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 524288,
onKeyUp: null,
height: 30
};
$.extend(true, defaults, settings);
this.setPlaceholder(defaults.placeholder)
.setMaxLength(defaults.maxLength)
.setOnKeyUpHandler(defaults.onKeyUp)
.setHeight(defaults.height);
};
/**
* Sets the callback function to be called when the keyup event occurs.
* @chainable
*/
TextControl.prototype.setOnKeyUpHandler = function (handler) {
if (typeof handler === 'function') {
this.onKeyUp = handler;
}
return this;
};
/**
* Sets the placeholder test to show in the control when there's not ant value in it.
* @param {String} placeholder
* @chainable
*/
TextControl.prototype.setPlaceholder = function (placeholder) {
if (typeof placeholder === 'string') {
this.placeholder = placeholder;
if (this.html) {
this.html.placeholder = placeholder;
}
}
return this;
};
/**
* Sets the maximun character number to be accepted in the control.
* @param {Number} maxLength The number must be an integer.
* If the value is minor or equal to 0 then the maxLength property is set to the default (524288).
* @chainable>
*/
TextControl.prototype.setMaxLength = function (maxLength) {
if (typeof maxLength === 'number' && maxLength % 1 === 0) {
this.maxLength = maxLength;
if (this.html) {
this.html.maxLength = maxLength > 0 ? maxLength : 524288;
}
} else {
throw new Error("method setMaxLength() only accepts integer values.");
}
return this;
};
/**
* Set a value in the parameter disabled.
* @param {Boolean} value
*/
TextControl.prototype.setDisabled = function (value) {
if (typeof value === 'boolean') {
this.disabled = value;
if (this.html) {
this.html.disabled = value;
}
} else {
throw new Error("method setDisabled() only accepts boolean values.");
}
};
/**
* Set the events for the object.
* @chainable
*/
TextControl.prototype.defineEvents = function () {
var that = this;
TextControl.superclass.prototype.defineEvents.call(this);
if (this.html) {
this.addEvent('keyup').listen(this.html, function (e) {
if (typeof that.onKeyUp === 'function') {
that.onKeyUp(e);
}
//if(e.which == 13){
// that.onChangeHandler();
//}
});
}
return this;
};
/**
* Creates the HTML element for the object
* @return {HTMLElement}
*/
TextControl.prototype.createHTML = function () {
TextControl.superclass.prototype.createHTML.call(this);
this.html.type = "text";
this.setPlaceholder(this.placeholder)
.setMaxLength(this.maxLength)
.setReadOnly(this.readonly);
return this.html;
};
/**
* Sets if the control will be enabled for read only.
* @param {Boolean}
* @chainable
*/
TextControl.prototype.setReadOnly = function (readonly) {
if (typeof readonly !== 'undefined') {
this.readonly = !!readonly;
if (this.html) {
this.html.readOnly = this.readonly;
}
}
return this;
};
/**
* Returns true if the control is enabled for read only.
* @return {Boolean}
*/
TextControl.prototype.isReadOnly = function () {
return this.readonly;
};
TextControl.prototype.getMaxLength = function () {
return this.maxLength;
}
PMUI.extendNamespace('PMUI.control.TextControl', TextControl);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = TextControl;
}
}());
(function () {
/**
* @class PMUI.control.PasswordControl
* Class for handle the HTML native password input control.
* @extends PMUI.control.HTMLControl
*
* Quick usage example:
*
* @example
* myTextbox = new PMUI.control.PasswordControl({
* name: "my_name",
* value: "John Doe",
* maxLength: 12,
* disabled: false,
* onChange: function(currentValue, previousValue) {
* if(previousValue !== "") {
* alert("Your name is not \"" + previousValue + "\" anymore.\nNow it's \"" + currentValue + "\"");
* } else {
* alert("Now your name is " + currentValue);
* }
* }
* });
*
* document.body.appendChild(myTextbox.getHTML());
*
* @constructor
* Creates a new instance of the PasswordControl object.
* @param {Object} [settings=null] A JSON object with the config options.
*
* @cfg {Number} [maxLength=524288] A number which specifies the maximum character length the control can accept.
* @cfg {Function} [onKeyUp=null] A callback function to be called every time a pressed key is released on the
* control. For info about the callback parameters please read the
* {@link PMUI.control.PasswordControl#event-onKeyUp onKeyUp event} section.
*/
var PasswordControl = function (settings) {
PasswordControl.superclass.call(this, settings);
// /**
// * @property {String} [placeholder=""]
// * The control's placeholder (the text to be shown inside the control when there is not any text in it).
// * @readonly
// */
// this.placeholder = null;
/**
* @property {Number} [maxLength=524288] The maximum character length the control accepts.
* @readonly
*/
this.maxLength = null;
/**
* @event onKeyUp
* Fired when a pressed key is released.
* @param {Object} event The event object.
*/
this.onKeyUp = null;
PasswordControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', PasswordControl);
PasswordControl.prototype.type = "PasswordControl";
/**
* Initializes the object.
* @param {Object} [settings=null] A JSON object with the config options.
* @private
*/
PasswordControl.prototype.init = function (settings) {
var defaults = {
maxLength: 524288,
onKeyUp: null,
height: 30
};
$.extend(true, defaults, settings);
this.setMaxLength(defaults.maxLength)
.setOnKeyUpHandler(defaults.onKeyUp)
.setHeight(defaults.height);
};
/**
* Sets the callback function to be called when the keyup event occurs.
* @chainable
*/
PasswordControl.prototype.setOnKeyUpHandler = function (handler) {
if (typeof handler === 'function') {
this.onKeyUp = handler;
}
return this;
};
/**
* Sets the maximun character number to be accepted in the control.
* @param {Number} maxLength The number must be an integer.
* If the value is minor or equal to 0 then the maxLength property is set to the default (524288).
* @chainable>
*/
PasswordControl.prototype.setMaxLength = function (maxLength) {
if (typeof maxLength === 'number' && maxLength % 1 === 0) {
this.maxLength = maxLength;
if (this.html) {
this.html.maxLength = maxLength > 0 ? maxLength : 524288;
}
} else {
throw new Error("method setMaxLength() only accepts integer values.");
}
return this;
};
/**
* Set the events for the object.
* @chainable
*/
PasswordControl.prototype.defineEvents = function () {
var that = this;
PasswordControl.superclass.prototype.defineEvents.call(this);
this.addEvent('keyup').listen(this.html, function (e) {
if (typeof that.onKeyUp === 'function') {
that.onKeyUp(e);
}
});
return this;
};
/**
* Creates the HTML element for the object
* @return {HTMLElement}
*/
PasswordControl.prototype.createHTML = function () {
PasswordControl.superclass.prototype.createHTML.call(this);
this.html.type = "password";
this.setMaxLength(this.maxLength);
return this.html;
};
PMUI.extendNamespace('PMUI.control.PasswordControl', PasswordControl);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = PasswordControl;
}
}());
(function () {
/**
* @class PMUI.control.DropDownListControl
* @extends PMUI.control.HTMLControl
* Class to handle the Select HTML form element.
*
* Example usage:
*
* @example
* var control;
* $(function() {
* var settings = {
* name: "myList",
* options: [
* {
* label: "one",
* value: 1
* },
* {
* label: "two",
* value: 2,
* disabled: true
* },
* {
* label: "three",
* value: 3,
* selected: true
* },
* {
* label: "Letters",
* options: [
* {
* value: "A"
* },
* {
* value: "B"
* }
* ]
* },
* {
* label: "months",
* disabled: true,
* options: [
* {
* value: "january"
* },
* {
* value: "february"
* }
* ]
* }
* ],
* value: 2
* };
* control = new PMUI.control.DropDownListControl(settings);
* document.body.appendChild(control.getHTML());
* });
*
* @constructor
* Creates a new instance of the DropDownListControl.
* @param {Object} [settings=null] An JSON object with the config options.
*/
/**
* @cfg {Array} [options=[]]
* An array with all the options to be contained by the control.
*
* Each element in the array is a JSON object, this JSON object can represent an option group
* or an option item.
*
* In case to represent an option item it can contain the next properties:
*
* - value {String} (required): the value for the option.
* - label {String} (optional): the label for the option, if isn't specified the value is used instead.
* - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
* "value" propery set then this "selected" property will be
* - disabled {Boolean} (optional): if the option is disabled or not.
*
* On the other hand, in case to represent an option group, it can contain the next properties:
*
* - label {String} (required): The name for the option group.
* - disabled {Boolean} (optional): If the group is disabled or not.
* - options {Array} (required): An array in which each element is a JSON object representing an option item,
* so every item must have the structure explained above (for represent option items). #Note. This propery makes
* the difference between an option and a option group. If the "options" property is not specified or if it isn't
* an array then it will treated like a option item.
* @cfg {String|Number} [value=null] The value of the option that is wanted to be selected. It must be one of the values
* of the list options, otherwise it will be set to "".
*/
var DropDownListControl = function (settings) {
DropDownListControl.superclass.call(this, settings);
/**
* @property {Array} [options] An array with all the options/option groups form the control.
* @private
*/
this.options = [];
/**
* @property {String} [elementTag='input'] The tag for the HTML element to be created.
* @private
*/
this.elementTag = 'select';
DropDownListControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', DropDownListControl);
DropDownListControl.prototype.type = "DropDownListControl";
/**
* Initializes the object.
* @param {Oject} [settings=null] A JSON opbject with the config options.
* @private
*/
DropDownListControl.prototype.init = function (settings) {
var defaults = {
options: [],
value: null,
height: 30
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options);
this.setHeight(defaults.height);
if (defaults.value !== null) {
this.setValue(defaults.value);
}
};
/**
* Clear all the options from the control.
* @chainable
*/
DropDownListControl.prototype.clearOptions = function () {
this.options = [];
if (this.html) {
jQuery(this.html).empty();
}
this.value = "";
return this;
};
/**
* Enables or disables one or more options/option groups.
* @param {Boolean} disabled If the function will disable (use true) or enable (use false)
* the options/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be enabled/disabled the options that match the string in its value and the
* option groups that match the string in its label. In this case more than one single item can be
* enabled/disabled.
* - In case to be a Number, it will be enabled/disabled the option/option group which index position matches the
* number. Obviously, in this case only one item will be enabled/disabled.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group label.
* Using this parameter, the elements to be match by the second parameter will be search only in the option groups
* that match this parameter in its label.
* @chainable
* @private
*/
DropDownListControl.prototype.enableDisableOption = function (disabled, option, group) {
var dataGroupTarget = this,
htmlGroupTarget = jQuery(this.html),
i,
j,
suboptions,
objectDefaults = {
applyTo: 'all'
};
disabled = !!disabled;
if (group) {
for (i = 0; i < this.options.length; i += 1) {
if (this.options[i].isGroup && this.options[i].label === group) {
dataGroupTarget = this.options[i];
break;
}
}
if (dataGroupTarget === this.options) {
throw new Error('disableOption(): the group "' + group + '" wasn\'t found.');
}
htmlGroupTarget = jQuery(this.html).find('>optgroup[label="' + group + '"]');
}
if (typeof option === 'number') {
dataGroupTarget.options[option].disabled = disabled;
htmlGroupTarget.find(">*").eq(option).attr("disabled", disabled);
} else if (typeof option === 'string') {
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) {
dataGroupTarget.options[i].disabled = disabled;
} else if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option) {
dataGroupTarget.options[i].disabled = disabled;
suboptions = dataGroupTarget.options[i].options;
for (j = 0; j < suboptions.length; j += 1) {
if (suboptions[j].value === option) {
suboptions[j].disabled = true;
}
}
}
}
jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
.attr("disabled", disabled);
} else if (typeof option === 'object') {
jQuery.extend(true, objectDefaults, option);
if (objectDefaults.applyTo === 'groups') {
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option.criteria) {
dataGroupTarget.options[i].disabled = disabled;
}
}
jQuery(htmlGroupTarget).find('optgroup[label="' + option.criteria + '"]').attr("disabled", disabled);
} else if (objectDefaults.applyTo === 'options') {
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option.criteria) {
dataGroupTarget.options[i].disabled = disabled;
}
}
jQuery(htmlGroupTarget).find('option[value="' + option.criteria + '"]')
.attr("disabled", disabled.criteria);
} else {
option = option.criteria;
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) {
dataGroupTarget.options[i].disabled = disabled;
} else if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option) {
dataGroupTarget.options[i].disabled = disabled;
suboptions = dataGroupTarget.options[i].options;
for (j = 0; j < suboptions.length; j += 1) {
if (suboptions[j].value === option) {
suboptions[j].disabled = true;
}
}
}
}
jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
.attr("disabled", disabled);
}
} else {
throw new Error('disableOption(): the first parameter must be a Number or a String.');
}
return this;
};
/**
* Disables one or more options/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be disabled the options that match the string in its value and the option
* groups which match the string in its label. In this case more than one single item can be disabled.
* - In case to be a Number, it will be disabled the option/option group which index position matches the number.
* Obviously, in this case only one item will be disabled.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group label.
* Using this parameter, the elements to be match by the first parameter will be search only in the option groups
* that match this parameter in its label.
* @chainable
*/
DropDownListControl.prototype.disableOption = function (option, group) {
return this.enableDisableOption(true, option, group);
};
/**
* Enables one or more options/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be enabled the options that match the string in its value and the option
* groups which match the string in its label. In this case more than one single item can be enabled.
* - In case to be a Number, it will be enabled the option/option group which index position matches the number.
* Obviously, in this case only one item will be enabled.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group label.
* Using this parameter, the elements to be match by the first parameter will be search only in the option groups
* that match this parameter in its label.
* @chainable
*/
DropDownListControl.prototype.enableOption = function (option, group) {
return this.enableDisableOption(false, option, group);
};
/**
* Removes one or more option/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be removed the options that match the string in its value and the option
* groups which match the string in its label. In this case more than one single item can be removed.
* - In case to be a Number, it will be removed the option/option group which index position matches the number.
* Obviously, in this case only one item will be removed.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group label.
* Using this parameter, the elements to be match by the first parameter will be search only in the option groups
* that match this parameter in its label.
*
* ##Note. Removing an option group implies removing all its child options.
* @chainable
*/
DropDownListControl.prototype.removeOption = function (option, group) {
var dataGroupTarget = this, htmlGroupTarget = jQuery(this.html), i, j, suboptions,
objectDefaults = {
applyTo: 'all'
};
if (group) {
for (i = 0; i < this.options.length; i += 1) {
if (this.options[i].isGroup && this.options[i].label === group) {
dataGroupTarget = this.options[i];
break;
}
}
if (dataGroupTarget === this.options) {
throw new Error('disableOption(): the group "' + group + '" wasn\'t found.');
}
htmlGroupTarget = jQuery(this.html).find('>optgroup[label="' + group + '"]');
}
if (typeof option === 'number') {
dataGroupTarget.options.splice(option, 1);
htmlGroupTarget.find(">*").eq(option).remove();
} else if (typeof option === 'string') {
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) {
dataGroupTarget.options.splice(i, 1);
i -= 1;
} else if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option) {
suboptions = dataGroupTarget.options[i].options;
for (j = 0; j < suboptions.length; j += 1) {
if (suboptions[j].value === option) {
suboptions.splice(j, 1);
j -= 1;
}
}
dataGroupTarget.options.splice(i, 1);
i -= 1;
}
}
jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
.remove();
} else if (typeof option === 'object') {
jQuery.extend(true, objectDefaults, option);
if (objectDefaults.applyTo === 'groups') {
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option.criteria) {
dataGroupTarget.options.splice(i, 1);
i -= 1;
}
}
jQuery(htmlGroupTarget).find('optgroup[label="' + option.criteria + '"]').remove();
} else if (objectDefaults.applyTo === 'options') {
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option.criteria) {
dataGroupTarget.options.splice(i, 1);
i -= 1;
}
}
jQuery(htmlGroupTarget).find('option[value="' + option.criteria + '"]').remove();
} else {
option = option.criteria;
for (i = 0; i < dataGroupTarget.options.length; i += 1) {
if ((!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) ||
(dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option)) {
dataGroupTarget.options.splice(i, 1);
i -= 1;
}
}
jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
.remove();
}
} else {
throw new Error('disableOption(): the first parameter must be a Number or a String.');
}
return this;
};
/**
* Adds a new option group to the control
* @param {Object} optionGroup A JSON object with the following properties:
*
* - label {String} (required): the label for the option group.
* - disabled {Boolean}(optional): if the option group will be disabled or not.
* it defaults to false.
* - options {Array} (optional): An array of JSON object, each one represents an option and
* should have the same structure than the "option" paremeter for the
* {@link PMUI.control.DropDownListControl#addOption addOption() method}.
* @chainable
*/
DropDownListControl.prototype.addOptionGroup = function (optionGroup) {
var newOptionGroup = {},
optionGroupHTML,
i;
if (!optionGroup.label) {
throw new Error("addOptionGroup(): a label for the new option group is required!");
}
newOptionGroup.label = optionGroup.label;
newOptionGroup.disabled = !!optionGroup.disabled;
newOptionGroup.isGroup = true;
newOptionGroup.options = [];
this.options.push(newOptionGroup);
if (this.html) {
optionGroupHTML = PMUI.createHTMLElement('optgroup');
optionGroupHTML.label = newOptionGroup.label;
optionGroupHTML.disabled = newOptionGroup.disabled;
this.html.appendChild(optionGroupHTML);
}
if (!jQuery.isArray(optionGroup.options)) {
optionGroup.options = [];
}
for (i = 0; i < optionGroup.options.length; i += 1) {
this.addOption(optionGroup.options[i], newOptionGroup.label);
}
return this;
};
/**
* Adds a new option to the control or to an option group.
* @param {Object} option An object with ther settings for the new option.
* this object can have the following properties:
*
* - value {String} (required): the value for the option.
* - label {String} (optional): the label for the option, if isn't specified the value is used instead.
* - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
* "value" propery set then this "selected" property will be
* - disabled {Boolean} (optional): if the option is disabled or not.
*
* @param {String} group The name of the option group in which the new option will be added. If it doesn't exist
* it will be created.
*
* @chainable
*/
DropDownListControl.prototype.addOption = function (option, group) {
var newOption = {},
optionHTML,
i,
groupHTML,
flag = false;
newOption.value = option.value !== null && option.value !== undefined && option.value.toString ? option.value : (option.label || "");
newOption.value = newOption.value.toString();
newOption.label = option.label || newOption.value;
newOption.label = newOption.label.toString();
newOption.disabled = !!option.disabled;
newOption.isGroup = false;
if (!group) {
this.options.push(newOption);
} else {
for (i = 0; i < this.options.length; i += 1) {
if (this.options[i].isGroup && this.options[i].label === group) {
this.options[i].options.push(newOption);
flag = true;
break;
}
}
if (!flag) {
this.addOptionGroup({
label: group
});
this.options[this.options.length - 1].options.push(newOption);
}
}
if (this.html) {
optionHTML = PMUI.createHTMLElement('option');
optionHTML.value = newOption.value;
optionHTML.selected = !!option.selected;
optionHTML.label = newOption.label;
optionHTML.disabled = newOption.disabled;
optionHTML.textContent = optionHTML.label;
if (group) {
groupHTML = jQuery(this.html).find('optgroup[label="' + group + '"]');
if (groupHTML.length) {
groupHTML.get(0).appendChild(optionHTML);
} else {
throw new Error("addOption(): the optiongroup \"" + group + "\" wasn't found");
}
} else {
jQuery(this.html).append(optionHTML);
}
}
if (option.selected) {
this.value = newOption.value;
}
if (this.getOptions().length == 1) {
this.value = newOption.value;
}
return this;
};
/**
* Returns the label from the option currently selected.
* @return {String}
*/
DropDownListControl.prototype.getSelectedLabel = function () {
var i;
if (this.html) {
return jQuery(this.html).find('option:selected').attr('label');
}
for (i = 0; i < this.options.length; i += 1) {
if (this.options[i].value === this.value) {
return this.options[i].label;
}
}
return "";
};
/**
* Determines if a value exists in any of the list options.
* @param {String|Number} value The value to be searched
* @return {Boolean} It returns true if the value was found in any of the list options, otherwise it returns false.
*/
DropDownListControl.prototype.valueExistsInOptions = function (value) {
var i,
j,
options,
optionsLength,
subOptions,
subOptionsLength;
optionsLength = (options = this.options || []).length;
for (i = 0; i < optionsLength; i += 1) {
if (options[i].isGroup) {
subOptionsLength = (subOptions = options[i].options || []).length;
for (j = 0; j < subOptionsLength; j += 1) {
if (subOptions[j].value == value) {
return true;
}
}
} else if (options[i].value == value) {
return true;
}
}
return false;
};
/**
* Returns the first available option in the list.
* @return {Object|null} It returns a object literal with the label and value properties from the found option.
* @private
*/
DropDownListControl.prototype.getFirstAvailableOption = function () {
var i,
j,
options,
optionsLength,
subOptions,
subOptionsLength;
optionsLength = (options = this.options || []).length;
for (i = 0; i < optionsLength; i += 1) {
if (options[i].isGroup) {
return (options[i].options.length && options[i].options[0]) || null;
} else {
return options[i];
}
}
return null;
}
/**
* Sets the options/option groups for the control.
* @param {Array} options An array with the same structure that the
* {@link PMUI.control.DropDownListControl#cfg-options "options"} property in the
* Config options section.
* @chainable
*/
DropDownListControl.prototype.setOptions = function (options) {
var i,
valueExists,
firstOption;
if (jQuery.isArray(options)) {
this.clearOptions();
for (i = 0; i < options.length; i += 1) {
if (jQuery.isArray(options[i].options)) {
this.addOptionGroup(options[i]);
} else {
this.addOption(options[i]);
}
}
if (!this.valueExistsInOptions(this.value)) {
firstOption = this.getFirstAvailableOption();
this.value = (firstOption && firstOption.value) || "";
}
}
return this;
};
/**
* Returns the options/option groups from the field
* @param {Boolean} [includeGroups=false] If it's evaluated as true then it will include
* the option groups with its child elements, otherwise it will return only the option items.
* @return {Array}
*
* example
* list.getOptions(false);
* [La Paz][Cochabamba][Santa Cruz][Buenos Aires][Santa Fe][Cordoba][Santiago][.][.][Mexico D.F]
*
* list.getOptions(true)
* [BOLIVIA
* [La Paz][Cochabamba][SantaCruz]
* ]
* [ARGENTINA
* [Buenos Aires][Santa Fe][Cordoba]
* ]
* [CHILE
* [x][y][z]
* ]
* [New York]
* [Mexico D.F.]
*/
DropDownListControl.prototype.getOptions = function (includeGroups) {
var options = [],
i,
j;
if (includeGroups) {
return this.options.slice(0);
}
for (i = 0; i < this.options.length; i += 1) {
if (!this.options[i].isGroup) {
options.push(this.options[i]);
} else {
for (j = 0; j < this.options[i].options.length; j += 1) {
options.push(this.options[i].options[j]);
}
}
}
return options;
};
/**
* Defines the events for the control
* @chainable
*/
DropDownListControl.prototype.defineEvents = function () {
var that = this;
DropDownListControl.superclass.superclass.prototype.defineEvents.call(this);
if (this.html) {
this.addEvent('change').listen(this.html, function () {
that.onChangeHandler();
});
}
return this;
};
/**
* Sets the selected option in the dropdown.
* @param {String|Number} value The value of the option that is wanted to be selected. It must be one of the values
* of the list options, otherwise it will be set to "".
*/
DropDownListControl.prototype.setValue = function (value) {
var firstOption;
if (!this.valueExistsInOptions(value)) {
firstOption = this.getFirstAvailableOption();
value = (firstOption && firstOption.value) || "";
}
return DropDownListControl.superclass.prototype.setValue.call(this, value);
};
/**
* Creates the HTML element for the control.
* @return {HTMLElement}
*/
DropDownListControl.prototype.createHTML = function () {
var value;
if (this.html) {
return this.html;
}
value = this.value;
DropDownListControl.superclass.prototype.createHTML.call(this);
this.setOptions(this.options.slice(0))
.setValue(value);
return this.html;
};
PMUI.extendNamespace('PMUI.control.DropDownListControl', DropDownListControl);
}());
(function () {
/**
* @class PMUI.control.TextAreaControl
* Class to handle a HTML TextArea control.
* @extends PMUI.control.TextControl
*
* Usage example:
*
* @example
* var myTextArea;
* $(function() {
* myTextArea = new PMUI.control.TextAreaControl({
* name: "some_text",
* value: "John Doe",
* maxLength: 12,
* readonly: false,
* width: 300,
* height: 200,
* placeholder: "insert some text",
* disabled: false,
* onChange: function(currentValue, previousValue) {
* if(previousValue !== "") {
* alert("the text is not \"" + previousValue + "\" anymore.\nNow it's \""
* + currentValue + "\"");
* } else {
* alert("Now your name is " + currentValue);
* }
* }
* });
*
* document.body.appendChild(myTextArea.getHTML());
*
* myTextArea.defineEvents();
* });
*
* @constructor
* Creates a new instance
* @param {Object} [settings] A JSON object that can contain the properties specified in the
* config options section.
*
* @cfg {Boolean} [readonly=false] If the control will be readonly.
*/
var TextAreaControl = function (settings) {
TextAreaControl.superclass.call(this, settings);
/**
* @property {Boolean} readonly A Boolean that specifies if the control is enabled for read only.
* @readonly
*/
this.readonly = null;
/**
* @property {String} [elementTag='input'] The tag for the HTML element to be created.
* @private
*/
this.elementTag = 'textarea';
TextAreaControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.TextControl', TextAreaControl);
TextAreaControl.prototype.type = "TextAreaControl";
/**
* Initializes the object.
* @param {Object} [settings=null] A JSON object with the config options.
* @private
*/
TextAreaControl.prototype.init = function (settings) {
var defaults = {
readonly: false
};
jQuery.extend(true, defaults, settings);
this.setReadOnly(defaults.readonly);
};
/**
* Creates the HTML element for the object
* @return {HTMLElement}
*/
TextAreaControl.prototype.createHTML = function () {
TextAreaControl.superclass.prototype.createHTML.call(this);
this.setReadOnly(this.readonly);
return this.html;
};
/**
* Sets the maximun character number to be accepted in the control.
* @param {Number} maxLength The number must be an integer.
* If the value is minor or equal to 0 then the maxLength property is set to the default (524288).
* @chainable>
*/
TextAreaControl.prototype.setMaxLength = function (maxLength) {
var that = this;
if (typeof maxLength === 'number' && maxLength % 1 === 0) {
this.maxLength = maxLength;
if (this.html) {
this.html.maxLength = maxLength > 0 ? maxLength : 524288;
}
} else {
throw new Error("method setMaxLength() only accepts integer values.");
}
return this;
};
PMUI.extendNamespace('PMUI.control.TextAreaControl', TextAreaControl);
if (typeof exports !== "undefined") {
module.exports = TextAreaControl;
}
}());
(function () {
/**
* @class PMUI.control.SelectableControl
* A checkbox is a graphical component that can be in either an "on" (true) or "off" (false) state.
* Clicking on a check box changes its state from "on" to "off," or from "off" to "on."
* @extends PMUI.control.HTMLControl
*
* Usage example:
*
* @example
* var a;
* $(function() {
* a = new PMUI.control.SelectableControl({
* name: "music",
* label: "Do you like music?",
* value: true,
* mode: 'checkbox', //it also can be "radio"
* onSelect: function() {
* console.log("checked");
* },
* onDeselect: function() {
* console.log("unchecked");
* }
* });
* document.body.appendChild(a.getHTML());
* a.defineEvents();
* });
*
* @constructor
* Creates a new instance of the SelectableControl class.
* @param {Object} [settings=null] A JSON object with the config options.
*
* @cfg {Boolean} [selected=false] If the control will be selected initially.
* @cfg {String} [label=""] The label for the control.
* @cfg {String} [value=""] The value for the control.
* @cfg {Function} [onSelect=null] The function to be call when the item will be selected. For info about the
* parameters please read the {@link PMUI.control.SelectableControl#event-onSelect onSelect event} documentation.
* @cfg {Function} [onDeselect=null] The function to be call when the item will be deselected
* (only supported when the mode is set to "checkbox"). For info about the callback parameters please read the
* {@link PMUI.control.SelectableControl#event-onDeselect onDeselect event} documentation.
* @cfg {String} [mode="checkbox"] The mode for the control, it can be: "checkbox" (default)
* for a checkbox control or "radio" for a readio button.
*/
var SelectableControl = function (settings) {
SelectableControl.superclass.call(this, settings);
/**
* If the control is selected or not.
* @type {Boolean}
* @readonly
*/
this.selected = null;
/**
* The control's label.
* @type {String}
* @readonly
*/
this.label = null;
/**
* @event onSelect
* Fired when the control is selected.
*/
this.onSelect = null;
/**
* @event onDeselect
* Fired when the control is deselected.
*/
this.onDeselect = null;
/**
* The control's selection mode.
* @type {String}
* @readonly
*/
this.mode = null;
/**
* The interactive control's HTML element.
* @type {HTMLElement}
* @private
*/
this.control = null;
/**
* The control's HTML element that will contain the label text.
* @type {[type]}
*/
this.textContainer = null;
SelectableControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', SelectableControl);
/**
* The object's type.
* @type {String}
*/
SelectableControl.prototype.type = 'SelectableControl';
/**
* Initializes the object.
* @param {Object} [settings=null] A JSON object with the config options.
* @private
*/
SelectableControl.prototype.init = function (settings) {
var defaults = {
selected: false,
mode: 'checkbox',
label: '',
value: "",
onSelect: null,
onDeselect: null
};
jQuery.extend(true, defaults, settings);
if (defaults.mode === 'checkbox' || defaults.mode === 'radio') {
this.mode = defaults.mode;
} else {
throw new Error('SelectableControl: it only accepts "checkbox" and "radio" as value for the "mode"'
+ ' property');
}
this.onSelect = defaults.onSelect;
this.onDeselect = defaults.onDeselect;
this.setLabel(defaults.label);
if (defaults.selected) {
this.select();
} else {
this.deselect();
}
};
/**
* Sets the control's name
* @param {String} name
* @chainable
*/
SelectableControl.prototype.setName = function (name) {
SelectableControl.superclass.prototype.setName.call(this, name);
if (this.control) {
this.control.name = name;
}
return this;
};
/**
* Sets the value for the control
* @param {String|Boolean|Number} setValue This param value is evaluated as boolean.
* @chainable
*/
SelectableControl.prototype.setValue = function (value) {
SelectableControl.superclass.superclass.prototype.setValue.call(this, value);
if (this.control) {
this.control.value = value;
}
return this;
};
/**
* Returns a boolean that specifies if the checkbox/radio button is selected.
* @return {Boolean}
*/
SelectableControl.prototype.isSelected = function () {
if (this.html) {
this.selected = jQuery(this.control).is(':checked');
}
return this.selected;
};
/**
* Sets deselected the checkbox/radio button.
* @chainable
*/
SelectableControl.prototype.deselect = function () {
this.selected = false;
if (this.control) {
this.control.checked = false;
}
return this;
};
/**
* Sets selected the checkbox/radio button.
* @chainable
*/
SelectableControl.prototype.select = function () {
this.selected = true;
if (this.control) {
this.control.checked = true;
}
return this;
};
/**
* A method which is called everytime the checked state from the control changes.
*
* This method is used internally by the object, so in most of the cases you won't need to invocated.
* To execute instructions when the control changes, please use the
{@link PMUI.control.Control#setOnChangeHandler setOnChangeHandler()} method.
*
* @chainable
* @private
*/
SelectableControl.prototype.onChangeHandler = function () {
this.selected = $(this.control).is(":checked");
if (typeof this.onChange === 'function') {
this.onChange(this.value, this.value);
}
if (this.selected && typeof this.onSelect === 'function') {
this.onSelect();
} else if (!this.selected && typeof this.onDeselect === 'function') {
this.onDeselect();
}
return this;
};
/**
* Sets the label for the control.
* @param {String} label
* @chainable
*/
SelectableControl.prototype.setLabel = function (label) {
if (typeof label !== 'string') {
throw new Error("setLabel(): it only accepts string type values.");
}
this.label = label;
if (this.html) {
this.textContainer.textContent = label;
}
return this;
};
/**
* Disables/enables the control
* @param {Boolean} disable If the value is evaluated as true then the control
is disabled, otherwise the control is enabled.
* @chainable
*/
SelectableControl.prototype.disable = function (disable) {
SelectableControl.superclass.superclass.prototype.disable.call(this, disable);
if (this.html) {
this.control.disabled = this.disabled;
}
return this;
};
/**
* @inheritDoc
*/
SelectableControl.prototype.defineEvents = function () {
var that = this,
stopPropagation = function (e) {
e.stopPropagation();
};
this.removeEvents();
this.eventsDefined = true;
if (this.html) {
this.addEvent('change').listen(this.html, function () {
that.onChangeHandler();
});
this.addEvent('keydown').listen(this.html, function (e) {
if (e.which === PMUI.keyCodeF5) {
this.blur();
e.preventDefault();
window.location.reload(true);
}
});
}
return this;
};
/**
* Creates the HTML element for the control.
* @return {HTMLElement}
*/
SelectableControl.prototype.createHTML = function () {
var label,
textContainer,
control;
if (this.html) {
return this.html;
}
label = PMUI.createHTMLElement('label');
control = PMUI.createHTMLElement('input');
control.type = this.mode;
textContainer = PMUI.createHTMLElement('span');
textContainer.contentText = this.label;
label.appendChild(control);
label.appendChild(textContainer);
this.control = control;
this.textContainer = textContainer;
this.html = label;
this.html.id = this.id;
this.setName(this.name)
.setValue(this.value)
.disable(this.disabled)
.setLabel(this.label);
if (this.selected) {
this.select();
} else {
this.deselect();
}
this.applyStyle();
return this.html;
};
SelectableControl.prototype.setFocus = function () {
if (this.html) {
this.control.focus();
}
};
PMUI.extendNamespace('PMUI.control.SelectableControl', SelectableControl);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = SelectableControl;
}
}());
(function () {
/**
* @class PMUI.control.HiddenControl
* The HTML input tag with the type attribute set to "hidden".
* @extends PMUI.control.HTMLControl
*
* Usage example:
*
* var hiddenControl = new PMUI.control.HiddenControl();
*
* document.body.appendChild(hiddenControl.getHTML());
*
* @constructor
* Creates a new instance of the HiddenControl class.
* @param {Object} [settings=null] A JSON object with the config options.
*/
var HiddenControl = function (settings) {
HiddenControl.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', HiddenControl);
HiddenControl.prototype.type = "HiddenControl";
/**
* Creates the HTML element for the control.
* @return {HTMLElement}
*/
HiddenControl.prototype.createHTML = function () {
HiddenControl.superclass.prototype.createHTML.call(this);
this.html.type = 'hidden';
return this.html;
};
PMUI.extendNamespace('PMUI.control.HiddenControl', HiddenControl);
if (typeof exports !== "undefined") {
module.exports = HiddenControl;
}
}());
(function () {
/**
* @class PMUI.control.DateTimeControl
* A control to handle dates and times.
* @extends {PMUI.control.HTMLControl}
*
*
* Usage example:
*
* @example
* var dateTimePicker;
*
* $(function() {
* dateTimePicker = new PMUI.control.DateTimeControl({
* dateFormat: 'M dd yy',
* minDate: -90,
* maxDate: "+1y -1m -4d",
* firstDay: 1,
* months: {
* "january": {
* name: "Enero",
* shortname: "Ene"
* },
* "february": {
* name: "Febrero",
* shortname: "Feb"
* },
* "march": {
* name: "Marzo",
* shortname: "Mar"
* },
* "april": {
* name: "Abril",
* shortname: "Abr"
* },
* "may": "May",
* "june": "Junio",
* "july": "July",
* "august": "Agosto",
* "september": "Septiembre",
* "october": "Octubre",
* "november": "Noviembre",
* "december": "Diciembre"
* },
* days: {
* "sunday": {
* name: "Domingo",
* shortname: "Do"
* },
* "monday": {
* name: "Lunes",
* shortname: "Lu"
* },
* "tuesday": {
* name: "Martes",
* shortname: "Ma"
* },
* "wednesday": {
* name: "Miércoles",
* shortname: "Mi"
* },
* "thursday": {
* name: "Jueves",
* shortname: "Ju"
* },
* "friday": "Viernes",
* "saturday": "Sábado"
* }
* });
* document.body.appendChild(dateTimePicker.getHTML());
* dateTimePicker.defineEvents();
* });
*
* @cfg {Boolean} [datetime=false] If the control will have time supporting.
* @cfg {String} [dateFormat="yy-mm-dd HH:ii:ss"|"yy-mm-dd"] The format for the date to show in the text box.
* It defaults to:
*
* - "yy-mm-dd HH:ii:ss" if {@link PMUI.control.DateTimeControl#cfg-datetime datetime} is set to true.
* - "yy-mm-dd" if {@link PMUI.control.DateTimeControl#cfg-datetime datetime} is set to false.
*
* You can set a customized date format using the following wildcards:
*
* - d, day of month (no leading zero).
* - dd, day of month (two digit).
* - o, day of the year (no leading zeros).
* - oo, day of the year (three digit).
* - D, day name short.
* - DD, day name long.
* - m, month of year (no leading zero).
* - mm, month of year (two digit).
* - M, month name short.
* - MM, month name long.
* - y, year (two digit).
* - yy, year (four digit).
* - P, period (AM or PM).
* - H, hours (0-23).
* - HH, hours (00-23).
* - h, hours (1-12).
* - hh, hours (01-12).
* - i, minutes (0-59).
* - ii, minutes (00-59).
* - s, seconds (0-59).
* - ss, seconds (00-59).
* - @, Unix timestamp (ms since 01/01/1970).
* - !, Windows ticks (100ns since 01/01/0001).
* - '...', literal text.
* - '', single quote.
* - anything else, literal text.
*
* The wildcards can be used together in the same string.
*
* @cfg {Object} [months={"january": "January", "february": "February", "march": "March", "april": "April",
* "may": "May", "june": "June", "july": "July", "august": "August", "september": "September",
* "october": "October", "november": "November", "december": "December"}]
* A JSON object to set the names and shortnames for every month in year. Each property of this object can be:
*
* - A string, in this case the name for the month is set to this string, and the shortname is set using
* the first 3 characters of the string.
* - A JSON object, in this case the JSON may have two properties:
* - "name", will be used as the name for the month.
* - "shortname", will be used as the shortname for the month, if it is not specified the the shortname for
* the month will be set using the first 3 characters of the object's "name" property.
*
* @cfg {Object} [days={"sunday": "Sunday","monday": "Monday","tuesday": "Tuesday","wednesday": "Wednesday",
* "thursday": "Thursday","friday": "Friday","saturday": "Saturday"}]
* A JSON object to set the name and shortname for every day of week. Each property of this object can be:
*
* - A string, in this case the name for the day is set to this string, and the shortname is set using
* the first 3 characters of the string.
* - A JSON object, in this case the JSON may have two properties:
* - "name", will be used as the name for the day.
* - "shortname", will be used as the shortname for the day, if it is not specified the the shortname for
* the day will be set using the first 3 characters of the object's "name" property.
*
* @cfg {String|Number} [minDate=-365] A value which sets the minimum selectable date for the calendar. It can be:
*
* - a Date object.
* - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
* time units to the current date, for example the expression "+1y -2m +3d" means
* "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
* - "d" for days.
* - "w" for weeks.
* - "m" for months.
* - "y" for years.
*
* You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
* - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
* current date. This number can be positive (for add days) or negative (for substract days).
* - an Object, in this case the object must have the following structure:
*
* {
* year: 2013,
* month: 5,
* day: 4,
* hours: 20,
* minutes: 15,
* seconds: 3,
* millisenconds
* }
*
* In this case only year and month are required.
*
* @cfg {String|Number} [maxDate=365] A value which sets the maximum selectable date for the calendar. It can take
* the same type of values that the {@link PMUI.contriol.DateTimeControl#cfg-minDate minDate config option}.
*
* - a Date object.
* - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
* time units to the current date, for example the expression "+1y -2m +3d" means
* "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
* - "d" for days.
* - "w" for weeks.
* - "m" for months.
* - "y" for years.
*
* You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
* - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
* current date. This number can be positive (for add days) or negative (for substract days).
* - an Object, in this case the object must have the following structure:
*
* {
* year: 2013,
* month: 5,
* day: 4,
* hours: 20,
* minutes: 15,
* seconds: 3,
* millisenconds
* }
*
* In this case only year and month are required.
*
* @cfg {Number} [firstDay=0] Sets the first day of week. You can use numbers from 0 to 6, 0 means Sunday,
* 1 means Monday and so on.
*/
var DateTimeControl = function (settings) {
DateTimeControl.superclass.call(this, settings);
/**
* @property {Object} dom A JSON object that contains the control's DOM elements.
* @private
*/
this.dom = {};
/**
* @property {Boolean} datetime If the calendar has time supporting.
* @readonly
*/
this.datetime = null;
/**
* @property {String} dateFormat The format for the date to be shown on the control's textbox.
* @readonly
*/
this.dateFormat = null;
/**
* @property {Date} dateObject The control's date object.
* @private
*/
this.dateObject = null;
/**
* @property {Date} minDate The minimum selectable date.
* @private
*/
this.minDate = null;
/**
* @property {Date} maxDate The maximum selectable date.
* @private
*/
this.maxDate = null;
/**
* @property {Number} firstDay The first day of the week beginning with 0: Sunday and ending with 6: Saturday.
* @readonly
*/
this.firstDay = null;
DateTimeControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', DateTimeControl);
DateTimeControl.prototype.type = "DateTimeControl";
/**
* An array with the name of each day of the week.
* @type {Array}
* @private
*/
DateTimeControl.prototype.daysOrder = [
"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"
];
/**
* An array with the name fo the months of year.
* @type {Array}
* @private
*/
DateTimeControl.prototype.monthsOrder = [
"january", "february", "march", "april", "may", "june", "july", "august", "september", "october",
"november", "december"
];
/**
* An Object that contains the name, shortname and code for the days of the week.
* @type {Object}
* @private
*/
DateTimeControl.prototype.days = {
"sunday": {
value: 0
},
"monday": {
value: 1
},
"tuesday": {
value: 2
},
"wednesday": {
value: 3
},
"thursday": {
value: 4
},
"friday": {
value: 5
},
"saturday": {
value: 6
}
};
/**
* An Object that contains the name, shortname and code for the months of year.
* @type {Object}
* @private
*/
DateTimeControl.prototype.months = {
"january": {
value: 0
},
"february": {
value: 1
},
"march": {
value: 2
},
"april": {
value: 3
},
"may": {
value: 4
},
"june": {
value: 5
},
"july": {
value: 6
},
"august": {
value: 7
},
"september": {
value: 8
},
"october": {
value: 9
},
"november": {
value: 10
},
"december": {
value: 11
}
};
/**
* Initialize the object.
* @param {Object} settings An object with the config options.
* @private
*/
DateTimeControl.prototype.init = function (settings) {
var defaults = {
datetime: false,
dateFormat: settings && settings.datetime ? 'yy-mm-dd HH:ii:ss' : 'yy-mm-dd',
months: {
"january": "January",
"february": "February",
"march": "March",
"april": "April",
"may": "May",
"june": "June",
"july": "July",
"august": "August",
"september": "September",
"october": "October",
"november": "November",
"december": "December"
},
days: {
"sunday": "Sunday",
"monday": "Monday",
"tuesday": "Tuesday",
"wednesday": "Wednesday",
"thursday": "Thursday",
"friday": "Friday",
"saturday": "Saturday"
},
minDate: -365,
maxDate: 365,
firstDay: 0,
height: 30
};
jQuery.extend(true, defaults, settings);
this.setFirstDay(defaults.firstDay)
.setDateFormat(defaults.dateFormat)
.setMonths(defaults.months)
.setDays(defaults.days)
.setMinDate(defaults.minDate)
.setMaxDate(defaults.maxDate)
.visibleDateTime(defaults.datetime)
.setHeight(defaults.height);
};
/**
* @inheritdoc
*/
DateTimeControl.prototype.setID = function (id) {
DateTimeControl.superclass.prototype.setID.call(this, id);
if (this.dom) {
this.dom.calendar.id = 'pmui-datepicker-calendar-' + this.id;
}
return this;
};
/**
* Returns the day index of the first day of week.
* @return {Number} A number refering a day: 0 for Sunday, 1 for Monday and so on.
*/
DateTimeControl.prototype.getFirstDay = function () {
return this.firstDay;
};
/**
* Returns the control's minimum selectable date.
* @param {String} [format="UTC"] The format to applied to the returning date.
* @return {String} The minimum selectable date in string format.
*/
DateTimeControl.prototype.getMinDate = function (format) {
return this.formatDate(this.minDate, format || "UTC");
};
/**
* Returns the control's maximum selectable date.
* @param {String} [format="UTC"] The format to applied to the returning date.
* @return {String} The maximum selectable date in string format.
*/
DateTimeControl.prototype.getMaxDate = function (format) {
return this.formatDate(this.maxDate, format || "UTC");
};
/**
* Enables/disabled the calendar's time supporting.
* @param {Boolean} visible If it's true, then the time supporting is enabled, otherwise it's disabled.
* @chainable
*/
DateTimeControl.prototype.visibleDateTime = function (visible) {
visible = !!visible;
this.datetime = visible;
if (this.html) {
this.dom.footer.style.display = this.datetime ? 'block' : 'none';
}
return this;
};
/**
* @method setValue
* Sets the value for the Control.
* @param {Date|Number|String} value The date to set to the control, it can be:
*
* - A Date object, is used the date set in this object.
* - A Number, the number is used as the timestamp for the date (set in UTC).
* - A String, in this case the string must have one of the following formats:
*
* - "" (empty string), the value is set to empty.
* - "\d", the value is parsed to a number to be used as the timestamp for the date.
* - "\d{4}\-\d{2}\-\d{2}", this date is taken as "yyyy-mm-dd" and it's parsed.
* - "\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}[\\+\-]\d{2}\:\d{2}", the date is taken as
* "yyyy-mm-ddTHH:ii:ss+HH:ii" (the timezone offset is used).
*
* @param {Boolean} [utc=false] If the date will be set as a UTC date.
* (Only applicable if the first parameter is String and has the format "\d{4}\-\d{2}\-\d{2}").
* @chainable
*/
DateTimeControl.prototype.setValue = function (value, utc) {
var newDate, aux, aux2, y, m, d, h, i, s, hd, id, datetime = false, error = false;
if (value instanceof Date) {
newDate = value;
} else if (typeof value === 'number') {
newDate = new Date(value);
} else if (typeof value === 'string') {
if (value === "") {
this.dateObject = null;
} else if (/^\d+$/.test(value)) {
newDate = new Date(parseInt(value, 10));
} else if (/^\d{4}\-\d{2}\-\d{2}$/.test(value)
|| /^\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}[\\+\-]\d{2}\:\d{2}$/.test(value)) {
newDate = new Date();
aux = value.split(/T|\+|\-(?=\d{2}\:\d{2})/);
aux2 = aux[0].split("-");
y = parseInt(aux2[0], 10);
m = parseInt(aux2[1], 10);
d = parseInt(aux2[2], 10);
if (aux[1]) {
datetime = true;
aux2 = aux[1].split(":");
h = parseInt(aux2[0], 10);
i = parseInt(aux2[1], 10);
s = parseInt(aux2[2], 10);
aux2 = aux[2].split(":");
hd = (value[19] === '+' ? -1 : 1) * parseInt(aux2[0], 10);
id = (hd < 0 ? -1 : 1) * parseInt(aux2[1], 10);
} else {
h = i = s = hd = id = 0;
}
if (!this.isValidDateTime(y, m, d, h, i, s)) {
throw new Error("setValue(): invalid date time.");
}
newDate.setFullYear(y);
newDate.setMonth(m - 1);
newDate.setDate(d);
newDate.setHours(h);
newDate.setMinutes(i);
newDate.setSeconds(s);
newDate.setMilliseconds(0);
newDate.setHours(newDate.getHours() + hd);
newDate.setMinutes(newDate.getMinutes() + id);
if (!this.dateObject) {
this.dateObject = new Date();
}
if (datetime || utc) {
this.dateObject.setUTCFullYear(newDate.getFullYear());
this.dateObject.setUTCMonth(newDate.getMonth());
this.dateObject.setUTCDate(newDate.getDate());
this.dateObject.setUTCHours(newDate.getHours());
this.dateObject.setUTCMinutes(newDate.getMinutes());
this.dateObject.setUTCSeconds(newDate.getSeconds());
} else {
this.dateObject.setFullYear(y);
this.dateObject.setMonth(m - 1);
this.dateObject.setDate(d);
this.dateObject.setHours(h);
this.dateObject.setMinutes(i);
this.dateObject.setSeconds(s);
this.dateObject.setMilliseconds(0);
}
newDate = null;
} else {
error = true;
}
} else {
error = true;
}
if (error) {
throw new Error("setValue(): Invalid parameter format/data type.");
}
if (value !== "") {
if (!this.dateObject) {
this.dateObject = new Date();
}
if (newDate) {
this.dateObject.setFullYear(newDate.getFullYear());
this.dateObject.setMonth(newDate.getMonth());
this.dateObject.setDate(newDate.getDate());
this.dateObject.setHours(newDate.getHours());
this.dateObject.setMinutes(newDate.getMinutes());
this.dateObject.setSeconds(newDate.getSeconds());
}
}
if (this.html) {
if (this.dateObject) {
this.html.value = this.formatDate(this.dateObject, this.dateFormat);
this.dom.hoursInput.value = this.dateObject.getHours();
this.dom.minutesInput.value = this.dateObject.getMinutes();
this.dom.secondsInput.value = this.dateObject.getSeconds();
} else {
this.html.value = "";
}
this.fillYearSelector();
this.fillMonthSelector();
this.buildDaysTable();
}
this.value = this.getValue('UTC');
return this;
};
/**
* Sets the first day of week.
* @param {Number} day Use 0 for Sunday, 1 for Monday, 2 for Tuesday and so on!.
* @chainable
*/
DateTimeControl.prototype.setFirstDay = function (day) {
if (typeof day === 'number') {
if (day >= 0 && day < 7) {
this.firstDay = Math.floor(day);
if (this.html) {
this.setDays(this.days);
this.buildDaysTable();
}
return this;
}
}
throw new Error("setFirstDay(): The parameter must be a number between 0 and 6.");
};
/**
* Sets the format for the date to be displayed in the control's textbox.
* @param {String} dateFormat A string that contains wildcards that represent a date part (day, month, etc.).
* The valid wildcards are the same ones that are specified in the
* {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
* @chainable
*/
DateTimeControl.prototype.setDateFormat = function (dateFormat) {
if (typeof dateFormat === 'string') {
this.dateFormat = dateFormat;
this.updateValue();
} else {
throw new Error("setDateFormat(): The parameter must be a string.");
}
return this;
};
/**
* Returns the date format to be displayed in the control's textbox.
* @return {String}
*/
DateTimeControl.prototype.getDateFormat = function () {
return this.dateFormat;
};
/**
* Returns the date using the format set by {@link PMUI.control.DateTimeControl#setDateFormat setDateFormat()}
* method.
* @return {String}
*/
DateTimeControl.prototype.getFormatedDate = function () {
return this.html.value;
};
/**
* Returns the selected date.
* @param {String} format Specifies the date format that will be used for the returning date.
* The valid values are:
*
* - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
* - "@" or "timestamp", returns a date in timestamp format.
* - [any other valid format string], will return the date using the string as the dateformat. This string can
* contain any of the wilcards specified in the
* {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
* @return {String}
*/
DateTimeControl.prototype.getValue = function (returningFormat) {
var res;
returningFormat = returningFormat || "UTC";
if (this.dateObject) {
switch (returningFormat) {
case 'timestamp':
case '@':
res = this.formatDate(this.dateObject, '@');
break;
default:
res = this.formatDate(this.dateObject, returningFormat);
}
} else {
res = "";
}
return res;
};
/**
* Returns true if the parameter is a leap year, otherwise returns false.
* @param {Number} year The year to be evaluated.
* @return {Boolean}
* @private
*/
DateTimeControl.prototype.isLeapYear = function (year) {
if (year % 400 === 0) {
return true;
}
if (year % 100 === 0) {
return false;
}
if (year % 4 === 0) {
return true;
}
return false;
};
/**
* Returns true if the bunch of arguments represents a valid date otherwise it returns false.
* @param {Number} year
* @param {Number} month The month number (a number from 1 to 12)
* @param {Number} day
* @param {Number} [hours]
* @param {Number} [minutes]
* @param {Number} [seconds]
* @param {Number} [milliseconds]
* @return {Boolean}
* @private
*/
DateTimeControl.prototype.isValidDateTime = function (year, month, day, hours, minutes, seconds, milliseconds) {
if (!(typeof year === 'number' && typeof month === 'number' && typeof day === 'number')) {
return false;
}
hours = hours || 0;
minutes = minutes || 0;
seconds = seconds || 0;
milliseconds = milliseconds || 0;
if (!(typeof hours === 'number' && typeof minutes === 'number' && typeof seconds === 'number'
&& typeof milliseconds === 'number')) {
return false;
}
if (!(hours >= 0 && hours <= 23)) {
return false;
}
if (!(minutes >= 0 && minutes <= 59)) {
return false;
}
if (!(seconds >= 0 && seconds <= 59)) {
return false;
}
if (!(milliseconds >= 0 && milliseconds <= 999)) {
return false;
}
if (day < 1 || day > 31 || month < 1 || month > 12) {
return false;
}
switch (month) {
case 4:
case 6:
case 9:
case 11:
if (day > 30) {
return false;
}
break;
case 2:
if (this.isLeapYear(year)) {
return day <= 29;
}
return day <= 28;
}
return true;
};
/**
* It parses the argument into a date.
* @param {String|Date} date It can be a String or a Date:
*
* - Date, in this case the argument isn't parse and it is returned.
* - String, in this case the string must have the following format: "[-+]\d+[dwmy]". For example "+1y -4m +3d"
* means that te returning date will be 1 year minus 4 months plus 3 days from today.
* @return {Date}
* @private
*/
DateTimeControl.prototype.parseDate = function (date) {
var startDate,
aux,
i,
amount;
if (typeof date === 'number') {
startDate = new Date();
startDate.setDate(startDate.getDate() + date);
} else if (typeof date === 'string') {
startDate = new Date();
date = " " + jQuery.trim(date);
if (/^(\s[\\+|\-]\d+[y|m|d|w])+$/.test(date)) {
date = date.match(/[\\+|\-]\d+[y|m|d]/g);
for (i = 0; i < date.length; i += 1) {
aux = date[i].match(/[\-|\\+]|\d+|[m|d|y]/g);
amount = parseInt(aux[1], 10);
if (aux[0] === '-') {
amount *= -1;
}
switch (aux[2]) {
case 'd':
startDate.setDate(startDate.getDate() + amount);
break;
case 'm':
startDate.setMonth(startDate.getMonth() + amount);
break;
case 'y':
startDate.setFullYear(startDate.getFullYear() + amount);
break;
case 'w':
startDate.setDate(startDate.getDate() + (amount * 7));
break;
}
}
}
} else if (date instanceof Date) {
startDate = date;
} else if (typeof date === 'object') {
startDate = new Date(date.year, date.month - 1, date.day || 1, date.hours || 0, date.minutes || 0, date.seconds || 0,
date.milliseconds || 0);
} else {
throw new Error("parseDate(): invalid parameter.");
}
return startDate;
};
/**
* Sets the minimum date the control can accept as a selectable one.
* @param {String|Date|Number|Object} date It can be:
*
* - a Date object.
* - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
* time units to the current date, for example the expression "+1y -2m +3d" means
* "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
* - "d" for days.
* - "w" for weeks.
* - "m" for months.
* - "y" for years.
*
* You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
* - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
* current date. This number can be positive (for add days) or negative (for substract days).
* - an Object, in this case the object must have the following structure:
*
* {
* year: 2013,
* month: 5,
* day: 4,
* hours: 20,
* minutes: 15,
* seconds: 3,
* millisenconds
* }
*
* In this case only year and month are required.
*
* @chainable
*/
DateTimeControl.prototype.setMinDate = function (date) {
var minDate = this.parseDate(date), d;
if (this.maxDate) {
if (minDate > this.maxDate) {
throw new Error("setMinDate(): The min date can't be major than the max date");
}
}
this.minDate = minDate;
this.fillYearSelector();
this.fillMonthSelector();
this.buildDaysTable();
if (this.html) {
d = new Date();
this.dom.todayButton.disabled = !(d >= minDate && (this.maxDate && d <= this.maxDate));
}
return this;
};
/**
* Sets the maximum date the control can accept as a selectable one.
* @param {String|Date|Number|Object} date It can be:
*
* - a Date object.
* - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
* time units to the current date, for example the expression "+1y -2m +3d" means
* "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
* - "d" for days.
* - "w" for weeks.
* - "m" for months.
* - "y" for years.
*
* You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
* - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
* current date. This number can be positive (for add days) or negative (for substract days).
* - an Object, in this case the object must have the following structure:
*
* {
* year: 2013,
* month: 5,
* day: 4,
* hours: 20,
* minutes: 15,
* seconds: 3,
* millisenconds
* }
*
* In this case only year and month are required.
* @chainable
*/
DateTimeControl.prototype.setMaxDate = function (date) {
var maxDate = this.parseDate(date), d;
if (this.minDate) {
if (maxDate < this.minDate) {
throw new Error("setMaxDate(): The max date can't be minor than the min date");
}
}
this.maxDate = maxDate;
this.fillYearSelector();
this.fillMonthSelector();
this.buildDaysTable();
if (this.html) {
d = new Date();
this.dom.todayButton.disabled = !(d <= maxDate && (this.minDate && d >= this.minDate));
}
return this;
};
/**
* Sets the months names/shortnames to be used by the calendar.
* @param {Object} months An object with the same structure that the
* {@link PMUI.control.DateTimeControl#cfg-months months config option}.
* @chainable
*/
DateTimeControl.prototype.setMonths = function (months) {
var key,
changed;
for (key in this.months) {
if (this.months.hasOwnProperty(key)) {
if (months.hasOwnProperty(key)) {
if (typeof months[key] === 'string') {
changed = true;
this.months[key].name = months[key];
this.months[key].shortname = months[key].charAt(0).toUpperCase() + months[key].slice(1, 3);
} else if (typeof months[key] === 'object') {
this.months[key].name = months[key].name;
this.months[key].shortname = months[key].shortname;
changed = true;
}
}
}
}
if (changed) {
this.fillMonthSelector();
}
return this;
};
/**
* Fills the months selector.
* @chainable
* @private
*/
DateTimeControl.prototype.fillMonthSelector = function () {
var currentMonth,
option,
year,
i,
j;
if (this.dom.monthSelector) {
year = this.dom.yearSelector.value;
currentMonth = (this.dateObject && this.dateObject.getMonth()) || this.dom.monthSelector.value
|| (new Date()).getMonth().toString();
jQuery(this.dom.monthSelector).empty();
i = 0;
j = this.monthsOrder.length - 1;
if (year === this.minDate.getFullYear().toString()) {
i = this.minDate.getMonth();
}
if (year === this.maxDate.getFullYear().toString()) {
j = this.maxDate.getMonth();
}
for (i; i <= j; i += 1) {
option = PMUI.createHTMLElement('option');
option.label = this.months[this.monthsOrder[i]].name;
option.textContent = option.label;
option.value = this.months[this.monthsOrder[i]].value;
option.selected = currentMonth.toString() === option.value;
this.dom.monthSelector.appendChild(option);
}
}
return this;
};
/**
* Sets the name/shortnames for the days to be used in the calendar.
* @param {Object} days A JSON object with the same structure than the
* {@PMUI.control.DateTimeControl#cfg-days days config option}.
* @chainable
*/
DateTimeControl.prototype.setDays = function (days) {
var key,
changed,
i,
cell,
daysLength = this.daysOrder.length,
aux;
for (key in this.days) {
if (this.days.hasOwnProperty(key)) {
if (days.hasOwnProperty(key)) {
if (typeof days[key] === 'string') {
this.days[key].name = days[key];
this.days[key].shortname = days[key].substr(0, 3);
} else if (typeof days[key] === 'object') {
this.days[key].name = days[key].name || this.days[key].name || "";
this.days[key].shortname = (days[key].shortname && days[key].shortname.substr(0, 3))
|| (this.days[key].name && this.days[key].name.substr(0, 3)) || "";
} else {
throw new Error("setDays(): The argument supplied must be a string or an object.");
}
changed = true;
}
}
}
if (changed && this.html) {
jQuery(this.dom.daysHeader).empty();
aux = 0;
i = this.firstDay;
while (aux < daysLength) {
if (i === daysLength) {
i = 0;
}
cell = PMUI.createHTMLElement('th');
cell.textContent = this.days[this.daysOrder[i]].shortname;
cell.setAttribute("data-value", this.days[this.daysOrder[i]].value);
this.dom.daysHeader.appendChild(cell);
i += 1;
aux += 1;
}
}
return this;
};
/**
* Fill the year selector.
* @chainable
* @private
*/
DateTimeControl.prototype.fillYearSelector = function () {
var minYear,
maxYear,
option,
selectedYear;
if (this.dom.yearSelector) {
selectedYear = (this.dateObject && this.dateObject.getFullYear()) || this.dom.yearSelector.value
|| (new Date()).getFullYear();
selectedYear = selectedYear.toString();
jQuery(this.dom.yearSelector).empty();
minYear = this.minDate.getFullYear();
maxYear = this.maxDate.getFullYear();
for (minYear; minYear <= maxYear; minYear += 1) {
option = PMUI.createHTMLElement('option');
option.textContent = option.label = option.value = minYear;
if (option.value.toString() === selectedYear) {
option.selected = true;
}
this.dom.yearSelector.appendChild(option);
}
}
return this;
};
/**
* Returns the number of days in a month
* @param {Number} y The year (it's necessary to determine the days in February.)
* @param {Number} m The month
* @return {Number} The number of days.
* @private
*/
DateTimeControl.prototype.daysInMonth = function (y, m) {
if (m < 1 || m > 12) {
return 0;
}
switch (m) {
case 4:
case 6:
case 9:
case 11:
return 30;
case 2:
if (this.isLeapYear(y)) {
return 29;
}
return 28;
default:
return 31;
}
};
/**
* Returns the day of week for a date.
* @param {Number} y The year.
* @param {Number} m The month.
* @param {Number} d The day of month.
* @return {Number} A number from 0 to 6. 0 means Sunday, 1, Monday and so on.
* @private
*/
DateTimeControl.prototype.whichDay = function (y, m, d) {
var centuryCode,
monthCode,
lastTwoYearDigits = y % 100,
gregorianCenturyCodes = [6, 4, 2, 0],
i;
if (!this.isValidDateTime(y, m, d)) {
throw new Error("whichDay(): invalid date.");
}
switch (m) {
case 3:
case 11:
monthCode = 3;
break;
case 4:
case 7:
monthCode = 6;
break;
case 5:
monthCode = 1;
break;
case 6:
monthCode = 4;
break;
case 8:
monthCode = 2;
break;
case 9:
case 12:
monthCode = 5;
break;
case 10:
monthCode = 0;
break;
case 1:
if (this.isLeapYear(y)) {
monthCode = -1;
} else {
monthCode = 0;
}
break;
case 2:
if (this.isLeapYear(y)) {
monthCode = 2;
} else {
monthCode = 3;
}
break;
}
centuryCode = Math.floor(y / 100);
i = 0;
while (centuryCode % 4 !== 0) {
centuryCode += 1;
i += 1;
}
if (i === 0) {
centuryCode = gregorianCenturyCodes[0];
} else {
centuryCode = gregorianCenturyCodes[gregorianCenturyCodes.length - i];
}
return (d + monthCode + lastTwoYearDigits + Math.floor(lastTwoYearDigits / 4) + centuryCode ) % 7;
};
/**
* Build the days table for the calendar
* @chainable
* @private
*/
DateTimeControl.prototype.buildDaysTable = function () {
var dayOfTheWeek,
daysInMonth,
y,
m,
i,
day = 1,
row,
cell,
limit,
link,
selectableDate;
if (this.dom.yearSelector && this.dom.monthSelector) {
jQuery(this.dom.tableBody).empty();
y = parseInt(this.dom.yearSelector.value, 10);
m = parseInt(this.dom.monthSelector.value, 10) + 1;
dayOfTheWeek = this.whichDay(y, m, 1) - this.firstDay;
if (dayOfTheWeek < 0) {
dayOfTheWeek = 7 + dayOfTheWeek;
}
daysInMonth = this.daysInMonth(y, m);
limit = Math.ceil((dayOfTheWeek + daysInMonth) / 7) * 7;
row = PMUI.createHTMLElement('tr');
for (i = 0; i < limit; i += 1) {
cell = PMUI.createHTMLElement("td");
if (i - dayOfTheWeek >= 0 && day <= daysInMonth) {
selectableDate = true;
if ((m - 1) === this.maxDate.getMonth() && y === this.maxDate.getFullYear()) {
if (day > this.maxDate.getDate()) {
selectableDate = false;
}
} else if ((m - 1) === this.minDate.getMonth() && y === this.minDate.getFullYear()) {
if (day < this.minDate.getDate()) {
selectableDate = false;
}
}
if (selectableDate) {
link = PMUI.createHTMLElement("a");
link.href = "#";
link.setAttribute("data-date", day);
link.textContent = day;
link.className = (this.dateObject && this.dateObject.getFullYear() === y
&& this.dateObject.getMonth() + 1 === m && this.dateObject.getDate() === day) ? 'selected' : '';
cell.appendChild(link);
} else {
cell.className = 'pmui-datepicker-disabled-date';
cell.textContent = day;
}
day += 1;
}
row.appendChild(cell);
if ((i + 1) % 7 === 0) {
this.dom.tableBody.appendChild(row);
row = PMUI.createHTMLElement('tr');
}
}
this.dom.tableBody.appendChild(row);
}
return this;
};
/**
* Shows the control's calendar.
* @chainable
* @private
*/
DateTimeControl.prototype.showCalendar = function () {
var position,
inputHeight,
maxZIndex;
if (this.html) {
this.fillYearSelector();
this.fillMonthSelector();
this.buildDaysTable();
position = jQuery(this.html).offset();
inputHeight = jQuery(this.html).outerHeight();
jQuery(document.body).find(">*").each(function () {
var n;
n = parseInt(jQuery(this).css("z-index"));
if (!isNaN(n)) {
maxZIndex = n > maxZIndex ? n : maxZIndex;
}
});
document.body.appendChild(this.dom.calendar);
this.dom.calendar.style.zIndex = maxZIndex ? maxZIndex + 1 : 999;
this.dom.calendar.style.left = position.left + 'px';
this.dom.calendar.style.top = (position.top + inputHeight) + 'px';
this.dom.calendar.style.display = "";
}
return this;
};
/**
* Hide's the control's calendar.
* @return {HTMLElement} The object's html.
* @private
*/
DateTimeControl.prototype.hideCalendar = function () {
if (this.html) {
jQuery(this.dom.calendar).detach();
}
return this.html;
};
/**
* Returns the day number of a date in a whole year.
* @param {Number} y The year.
* @param {Number} m The month.
* @param {Number} d The day of month.
* @return {Number} The day of year.
* @private
*/
DateTimeControl.prototype.getDayOfYear = function (y, m, d) {
var day = 0,
month = 1;
if (!(typeof y === 'number' && typeof m === 'number' && typeof d === 'number')) {
throw new Error("getDayOfYear(): invalid parameters.");
}
if (!this.isValidDateTime(y, m, d)) {
throw new Error("getDayOfYear(): invalid date.");
}
while (month < m) {
day += this.daysInMonth(y, m);
month += 1;
}
day += d;
return day;
};
/**
* Formats a date using the specified format string.
* @param {Date} dateObject a Date object.
* @param {String} format String that contains wildcards specified in the
* {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
* @return {String} The formatted date as a string.
* @private
*/
DateTimeControl.prototype.formatDate = function (dateObject, format) {
var finalValue,
d = dateObject.getDate(),
o = this.getDayOfYear(dateObject.getFullYear(), dateObject.getMonth() + 1, dateObject.getDate()),
m = dateObject.getMonth() + 1,
h = dateObject.getHours(),
h12 = h > 12 ? h - 12 : h,
i = dateObject.getMinutes(),
s = dateObject.getSeconds(),
yy = dateObject.getFullYear(),
formatArray,
timeOffset,
timeOffsetString,
aux;
timeOffset = dateObject.getTimezoneOffset();
timeOffsetString = timeOffset < 0 ? '+' : '-';
timeOffset = Math.sqrt(timeOffset * timeOffset);
aux = Math.floor(timeOffset / 60);
timeOffset = timeOffset - (aux * 60);
timeOffsetString += (aux < 10 ? '0' : '') + aux + ':' + (timeOffset < 10 ? '0' : '') + timeOffset;
formatArray = [
{
regExp: /dd/g,
value: d < 10 ? "0" + d : d
},
{
regExp: /d/g,
value: d
},
{
regExp: /oo/g,
value: o < 10 ? "00" + o : (o < 100 ? "0" + o : o)
},
{
regExp: /o/g,
value: o
},
{
regExp: /mm/g,
value: m < 10 ? "0" + m : m
},
{
regExp: /m/g,
value: m
},
{
regExp: /yy/g,
value: yy
},
{
regExp: /y/g,
value: yy % 100
},
{
regExp: /\@/g,
value: dateObject.getTime()
},
{
regExp: /\!/g,
value: (dateObject.getTime() * 10000) + 621355968000000000
},
{
regExp: /MM/g,
value: this.months[this.monthsOrder[dateObject.getMonth()]].name
},
{
regExp: /M/g,
value: this.months[this.monthsOrder[dateObject.getMonth()]].shortname
},
{
regExp: /HH/g,
value: (h < 10 ? '0' : '') + h
},
{
regExp: /H/g,
value: h
},
{
regExp: /hh/g,
value: (h12 < 10 ? '0' : '') + h12
},
{
regExp: /h/g,
value: h12
},
{
regExp: /ii/g,
value: (i < 10 ? '0' : '') + i
},
{
regExp: /i/g,
value: i
},
{
regExp: /ss/g,
value: (s < 10 ? '0' : '') + s
},
{
regExp: /s/g,
value: s
},
{
regExp: /UTC/g,
value: yy + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d + "T"
+ (h < 10 ? '0' : '') + h + ':' + (i < 10 ? '0' : '') + i + ':' + (s < 10 ? '0' : '') + s
+ timeOffsetString
},
{
regExp: /P/g,
value: h > 12 ? 'PM' : 'AM'
},
{
regExp: /DD/g,
value: this.days[this.daysOrder[dateObject.getDay()]].name
},
{
regExp: /D/g,
value: this.days[this.daysOrder[dateObject.getDay()]].shortname
}
];
finalValue = format || this.dateFormat;
for (i = 0; i < formatArray.length; i += 1) {
finalValue = finalValue.replace(formatArray[i].regExp, formatArray[i].value);
}
return finalValue;
};
/**
* Updates the control's date text.
* @chainable
* @private
*/
DateTimeControl.prototype.updateValue = function () {
var finalValue;
if (this.dateObject) {
finalValue = this.formatDate(this.dateObject, this.dateFormat);
} else {
finalValue = "";
}
this.value = finalValue;
if (this.html) {
this.html.value = finalValue;
}
return this;
};
/**
* Gets the control's value directly from the element.
* @return {String}
*/
DateTimeControl.prototype.getValueFromRawElement = function () {
return this.getValue('UTC');
};
/**
* The handler to be executed everytime the control's value changes.
* @chainable
* @private
*/
DateTimeControl.prototype.onChangeHandler = function (hide) {
var prevValue = this.value,
newValue,
resCallback,
date,
month,
year,
auxDate,
valueChanged = false;
auxDate = new Date();
date = parseInt($(this.dom.tableBody).find("a.selected").text(), 10);
if (isNaN(date)) {
return this;
}
month = parseInt(this.dom.monthSelector.value, 10);
year = parseInt(this.dom.yearSelector.value, 10);
auxDate.setYear(year);
auxDate.setMonth(month);
auxDate.setDate(date);
if (this.datetime) {
auxDate.setMilliseconds(0);
auxDate.setSeconds(parseInt(this.dom.secondsInput.value, 10));
auxDate.setMinutes(parseInt(this.dom.minutesInput.value, 10));
auxDate.setHours(parseInt(this.dom.hoursInput.value, 10));
this.dom.doneButton.disabled = true;
} else {
if (!this.dateObject) {
this.dateObject = new Date();
}
auxDate.setMilliseconds(0);
auxDate.setSeconds(0);
auxDate.setMinutes(0);
auxDate.setHours(0);
}
if (!this.dateObject) {
this.dateObject = new Date();
}
if (auxDate.getTime() !== this.dateObject.getTime()) {
valueChanged = true;
}
if (typeof this.onBeforeChange === 'function' && valueChanged) {
resCallback = this.onBeforeChange(this.formatDate(auxDate), prevValue);
}
if (resCallback !== false) {
this.dateObject = auxDate;
} else {
if (this.dom.monthSelector.value == this.dateObject.getMonth()
&& this.dom.yearSelector.value == this.dateObject.getFullYear()) {
jQuery(this.dom.tableBody).find('a.selected').removeClass('selected')
.find('a[data-date="' + this.dateObject.getDate() + '"]').addClass('selected');
}
}
newValue = this.getValueFromRawElement();
this.updateValue();
if (hide) {
this.hideCalendar();
}
if (typeof this.onChange === 'function' && this.value !== prevValue) {
this.onChange(this.value, prevValue);
}
return this;
};
/**
* Defines the events for the control
* @chainable
*/
DateTimeControl.prototype.defineEvents = function () {
var that = this,
preselectedDate,
specialCloseHandler = function (e) {
if ((e.target !== that.html && !$(e.target).parents('#pmui-datepicker-calendar-' + that.id).length)
|| (e.type === 'keyup' && !jQuery(document.activeElement).parents('#pmui-datepicker-calendar-'
+ that.id).length)) {
that.hideCalendar();
}
},
timeInputFilter;
if (this.dom.todayButton) {
timeInputFilter = function () {
var num = parseInt(this.value, 10),
maxValue;
switch (this) {
case that.dom.hoursInput:
maxValue = 23;
break;
case that.dom.minutesInput:
case that.dom.secondsInput:
maxValue = 59;
break;
default:
throw new Error("defineEvents() at change event: No valid element.");
}
if (isNaN(num)) {
this.value = 0;
} else {
this.value = num < 0 ? 0 : Math.min(num, maxValue);
}
that.dom.doneButton.disabled = false;
};
this.addEvent('change').listen(this.dom.yearSelector, function () {
that.fillMonthSelector();
that.buildDaysTable();
});
this.addEvent('change').listen(this.dom.monthSelector, function () {
that.buildDaysTable();
});
this.addEvent('focusin click').listen(this.html, function () {
that.showCalendar();
});
this.addEvent('change').listen(this.dom.hoursInput, timeInputFilter);
this.addEvent('change').listen(this.dom.minutesInput, timeInputFilter);
this.addEvent('change').listen(this.dom.secondsInput, timeInputFilter);
this.addEvent('click').listen(this.dom.todayButton, function () {
preselectedDate = new Date();
that.dom.doneButton.disabled = false;
that.dom.yearSelector.value = preselectedDate.getFullYear();
that.fillMonthSelector();
that.dom.monthSelector.value = preselectedDate.getMonth();
that.buildDaysTable();
jQuery(that.dom.tableBody).find(".selected").removeClass('selected').end()
.find('a[data-date=' + preselectedDate.getDate() + ']').addClass("selected");
});
this.addEvent('click keyup mousedown').listen(document, specialCloseHandler)
.listen('.pmui-window', specialCloseHandler);
//In the future, if the new Event objects model is implemented the following line should be listening to an
//event close or windowClose. Currently a dirty implementation will fix he problem.
this.addEvent('click').listen('.pmui-button.pmui-window-close', function () {
that.hideCalendar();
});
this.addEvent('click').listen(this.dom.doneButton, function () {
that.onChangeHandler(true);
});
jQuery(this.dom.tableBody).on('click', 'a', function (e) {
e.preventDefault();
jQuery(that.dom.tableBody).find("a").filter(".selected").removeClass("selected");
jQuery(this).addClass("selected");
var date, month, year;
date = parseInt(this.textContent, 10);
month = parseInt(that.dom.monthSelector.value, 10);
year = parseInt(that.dom.yearSelector.value, 10);
if (that.datetime) {
that.dom.doneButton.disabled = false;
} else {
that.onChangeHandler(true);
}
});
}
return this;
};
/**
* Creates the HTML for the control.
* @return {HTMLElement}
*/
DateTimeControl.prototype.createHTML = function () {
var header,
table,
tableHeader,
monthSelector,
yearSelector,
row,
cell,
tableBody,
calendar,
footer,
button,
input;
if (this.html) {
return this.html;
}
DateTimeControl.superclass.prototype.createHTML.call(this);
this.html.type = 'text';
this.html.readOnly = true;
calendar = PMUI.createHTMLElement('div');
calendar.className = 'pmui-datepicker';
calendar.id = 'pmui-datepicker-calendar-' + this.id;
header = PMUI.createHTMLElement('div');
header.className = 'pmui-datepicker-header';
table = PMUI.createHTMLElement('table');
table.className = 'pmui-datepicker-head-table';
monthSelector = PMUI.createHTMLElement("select");
yearSelector = PMUI.createHTMLElement("select");
row = PMUI.createHTMLElement("tr");
cell = PMUI.createHTMLElement("td");
cell.className = 'pmui-datepicker-month-selector';
cell.appendChild(monthSelector);
row.appendChild(cell);
cell = PMUI.createHTMLElement("td");
cell.className = 'pmui-datepicker-year-selector';
cell.appendChild(yearSelector);
row.appendChild(cell);
table.appendChild(row);
header.appendChild(table);
this.dom.yearSelector = yearSelector;
this.dom.monthSelector = monthSelector;
table = PMUI.createHTMLElement('table');
table.className = 'pmui-datepicker-table';
tableHeader = PMUI.createHTMLElement('thead');
row = PMUI.createHTMLElement('tr');
this.dom.daysHeader = row;
tableHeader.appendChild(row);
table.appendChild(tableHeader);
tableBody = PMUI.createHTMLElement('tbody');
this.dom.tableBody = tableBody;
table.appendChild(tableBody);
footer = this.createFooterHTML();
calendar.appendChild(header);
calendar.appendChild(table);
calendar.appendChild(footer);
this.dom.calendar = calendar;
this.dom.footer = footer;
this.setMinDate(this.minDate)
.setMaxDate(this.maxDate)
.setDays(this.days)
.visibleDateTime(this.datetime);
if (this.eventsDefined) {
this.defineEvents();
}
return this.html;
};
/**
* Creates the Fotter HTML for the control.
* @return {HTMLElement}
*/
DateTimeControl.prototype.createFooterHTML = function () {
var footertr,
tableFooter,
footer,
button,
cell1,
cell2,
cell3,
cell4,
cell5,
input;
footer = PMUI.createHTMLElement('div');
tableFooter = PMUI.createHTMLElement('table');
footertr = PMUI.createHTMLElement('tr');
footer.className = 'pmui-datepicker-footer';
cell1 = PMUI.createHTMLElement('td');
cell2 = cell1.cloneNode(false);
cell3 = cell1.cloneNode(false);
cell4 = cell1.cloneNode(false);
cell5 = cell1.cloneNode(false);
button = PMUI.createHTMLElement('button');
button.textContent = 'Today';
button.className = 'pmui-datepicker-button';
this.dom.todayButton = button;
cell1.appendChild(button);
footertr.appendChild(cell1);
input = PMUI.createHTMLElement('input');
input.setAttribute("type", "number");
input.size = 2;
input.min = 0;
input.max = 23;
input.value = 0;
input.placeholder = "hrs.";
this.dom.hoursInput = input;
cell2.appendChild(input);
footertr.appendChild(cell2);
input = input.cloneNode(false);
input.max = 59;
input.placeholder = "min.";
this.dom.minutesInput = input;
cell3.appendChild(input);
footertr.appendChild(cell3);
input = input.cloneNode(false);
input.max = 59;
input.placeholder = "sec.";
this.dom.secondsInput = input;
cell4.appendChild(input);
footertr.appendChild(cell4);
button = button.cloneNode(true);
button.textContent = "Done";
button.disabled = true;
this.dom.doneButton = button;
cell5.appendChild(button);
footertr.appendChild(cell5);
tableFooter.appendChild(footertr);
footer.appendChild(tableFooter);
return footer;
};
PMUI.extendNamespace('PMUI.control.DateTimeControl', DateTimeControl);
if (typeof exports !== "undefined") {
module.exports = DateTimeControl;
}
}());
(function () {
/**
* @class PMUI.ui.Window
* @extend PMUI.core.Panel
* Usage example:
*
* @example
*
* f = new PMUI.form.Form({
* items: [
* {
* pmType: "text",
* label: "Name",
* id: "123",
* value: "",
* placeholder: "insert your name",
* name: "name",
* helper: "Introduce your name",
* required : true,
* controlsWidth: 200,
* valueType: 'string'
* },
* {
* pmType: "text",
* label: "test Number",
* id: "1234",
* value: 10,
* placeholder: "insert your number",
* name: "numberTest",
* helper: "Introduce your number",
* required : true,
* controlsWidth: 200,
* valueType: 'number'
* },
* {
* pmType: "datetime",
* visible : true,
* label: "birth date",
* name: "birthdate",
* valueType: 'date',
* required: true
* }
* ]
* });
*
* w = new PMUI.ui.Window({
* title: "Window Example",
* width: 500,
* height: 230,
* modal: true,
* buttons: [
* {
* pmType: 'button',
* text: 'Save',
* handler: function() {
* alert("Saved!");
* }
* },
* {
* pmType: 'label',
* text: " or "
* },
* {
* pmType: 'button',
* text: "Close",
* handler : function () {
* w.close();
* },
* border: 'link'
* }
* ],
* closable: true,
* footerItems: [
* {
* text: "Close"
* }
* ],
* visibleFooter: true,
* buttonPanelPosition: 'top'
* });
* w.addItem(f);
* w.open();
* w.defineEvents();
*
* Create a new instace of the Window class
*
* @cfg {String} [title = [Untitled window]] Title The title for the Window
* @cfg {Boolean} [modal = true] Defines the window property is modal or not
* @cfg {String|Number} [height = "auto"] window height can be a number or a string. height
* is the div
* Element which contains a header, body and footer. When using a string that you can only use
* 'auto' or 'inherit' or # # # # px or% or # # # # em when is a number
* @cfg {String|Number} [width = 400] The width of the main window which contains a html element
* Element 'div' header, body and footer
* @cfg {String|Number} [footerHeight = "auto"] the height of the Element div footer (px, auto,
* number)which default 'auto'
* @cfg {PMUI.util.ArrayList} [buttons = []], defines an array of objects that are buttons
* settings may in the fotter Element div.
* @deprecated This config optio will be removed soon, please use the {@link #cfg-footerItems footerItems} config
* option instead.
* @cfg {boolean} [visibleCloseButton = true], the button close are can define or not in the window.
* @cfg {Array} [footerItems=[]] Sets the elements in the window footer, this elements can be instances of Button
* and/or instances of Label. The value for this config option must be an array in which each element can be:
*
* - An object literal, in this case the object literal must have the property "pmType" with its value set to
* "button" (if you want the element be a {@link PMUI.ui.Button Button}) or "label" (if you want the element be a
* {@link PMUI.ui.TextLabel Label}). Optionally you can add the respective config options for each case.
*
* - A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of
* {@link PMUI.ui.TextLabel Label}.
*
* @cfg {String} [footerAlign="center"] Sets the horizontal alignment for the items in the wondow footer, the
* possible values are "center", "left", "right".
* @cfg {Boolean} [visibleFooter="false"] indicating whether the footer of the Window is visible or was hidden
* @cfg {String} [buttonPanelPosition="bottom"] indicates that position does the panel of buttons, you can be
* above or below body.
* @cfg {Function|null} [onOpen=null] The callback function to be executed everytime the
* {@link #event-onOpen onOpen} event.
* @cfg {Function|null} [onClose=null] The callback function to be executed everytime the
* {@link #event-onClose onClose} event.
*/
var Window = function (settings) {
Window.superclass.call(this, jQuery.extend(true, {
positionMode: 'absolute'
}, settings));
/**
* The window's header.
* @type {HTMLElement}
* @private
*/
this.header = null;
/**
* The window's body.
* @type {HTMLElement}
* @private
*/
this.body = null;
/**
* The window's footer.
* @type {PMUI.panel.ButtonPanel}
* @private
*/
this.footer = null;
/**
* @property {String} title
* The window's title. Set by the {@link #cfg-title title} config option and the
* {@link #method-setTitle setTitle()} method.
* @readonly
*/
this.title = null;
/**
* The windo's close button.
* @type {PMUI.ui.Button}
* @private
*/
this.closeButton = null;
/**
* If the window is opened or not.
* @type {Boolean}
* @readonly
*/
this.isOpen = false;
/**
* is defined if button panes Object is visible
* @type {[type]}
*/
this.visibleFooter = null;
/**
* If the window's close button is visible or not. Set by the
* {@link #cfg-visibleCloseButton visibleCloseButton} config option and the
* {@link #method-showCloseButton showCloseButton()} and {@link #method-hideCloseButton hideCloseButton()}
* methods.
* @type {Boolean}
* @readonly
*/
this.visibleCloseButton = null;
/**
* If the window will be a modal window. Set by the {@link #cfg-modal modal} config option and the
* {@link #method-setModal setModal()} method.
* @type {Boolean}
* @readonly
*/
this.modal = null;
/**
* The window's footer height.
* @type {Number|String}
*/
this.footerHeight = null;
/**
* The window's footer alignment.
* @type {String}
*/
this.footerAlign = null;
/**
* defines the position of the buttons panel on the window
* @type {String}
*/
this.buttonPanelPosition = null;
/**
* @event onOpen
* Fired everytime the window opens.
* @param {PMUI.ui.Window} window The window that was opened.
*/
this.onOpen = null;
/**
* @event onClose
* Fired everytime the window closes.
* @param {PMUI.ui.Window} window The window that was closed.
*/
this.onClose = null;
this.dom = {};
this.onBeforeClose = null;
Window.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Panel', Window);
/**
* Defines the object's type
* @type {String}
*/
Window.prototype.type = 'Window';
/**
* Defines the object's family
* @type {String}
*/
Window.prototype.family = 'ui';
Window.prototype.init = function (settings) {
var defaults = {
height: 'auto',
width: 'auto',
title: '[Untitled window]',
modal: true,
footerHeight: 'auto',
zOrder: 100,
footerItems: [],
footerAlign: 'center',
visibleCloseButton: true,
visibleFooter: false,
buttons: [],
buttonPanelPosition: 'bottom',
onOpen: null,
onClose: null,
onBeforeClose: null
};
jQuery.extend(true, defaults, settings);
this.footer = new PMUI.panel.ButtonPanel({
style: {
cssClasses: ['pmui-window-footer']
}
});
this.setTitle(defaults.title)
.setModal(defaults.modal)
.setWidth(defaults.width)
.setHeight(defaults.height)
.setFooterHeight(defaults.footerHeight)
.setFooterAlign(defaults.footerAlign)
.setZOrder(defaults.zOrder)
.setButtonPanelPosition(defaults.buttonPanelPosition)
.setFooterItems(defaults.footerItems)
.setOnOpenHandler(defaults.onOpen)
.setOnCloseHandler(defaults.onClose);
this.setOnBeforeCloseHandler(defaults.onBeforeClose);
if (defaults.visibleCloseButton) {
this.showCloseButton();
} else {
this.hideCloseButton();
}
if (defaults.visibleFooter) {
this.showFooter();
} else {
this.hideFooter();
}
//TODO tell the guys to replace its 'buttons' config option by FooterItems
if (defaults.footerItems && defaults.footerItems.length) {
this.setFooterItems(defaults.footerItems);
} else {
this.setFooterItems(defaults.buttons);
}
};
/**
* Sets the callback function to be executed everytime the {@link #event-onClose onClose} event fires.
* @param {Function|null} handler It can be a function or null.
*/
Window.prototype.setOnCloseHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnCloseHandler(): The parameter must be a function or null.");
}
this.onClose = handler;
return this;
};
Window.prototype.setOnBeforeCloseHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnCloseHandler(): The parameter must be a function or null.");
}
this.onBeforeClose = handler;
return this;
};
/**
* Sets the callback function to be executed everytime the {@link #event-onOpen onOpen} event fires.
* @param {Function|null} handler It can be a function or null.
*/
Window.prototype.setOnOpenHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnOpenHandler(): The parameter must be a function or null.");
}
this.onOpen = handler;
return this;
};
/**
* updates the dimensions of the height of the HTMLElement window, body and foot when
it is modified by the user
* @chainable
*/
Window.prototype.updateDimensionsAndPosition = function () {
var bodyHeight,
footerHeight,
headerHeight,
windowHeight = this.height,
windowWidth;
if (!this.footer || !this.html || !this.isOpen) {
return this;
}
if (this.footerHeight === 'auto') {
this.footer.setHeight('auto');
} else {
this.footer.setHeight(this.footerHeight);
}
if (windowHeight === 'auto') {
this.body.style.height = 'auto';
this.body.style.minHeight = '150px';
} else {
if (/^\d+(\.\d+)?em$/.test(windowHeight)) {
windowHeight = PMUI.emToPx(parseInt(windowHeight, 10), this.modalObject);
} else if (/^\d+(\.\d+)?%$/.test(windowHeight)) {
windowHeight = jQuery(this.html).outerHeight();
}
footerHeight = this.visibleFooter ? jQuery(this.footer.getHTML()).outerHeight() : 0;
headerHeight = jQuery(this.header).outerHeight();
bodyHeight = windowHeight - footerHeight - headerHeight;
if (bodyHeight <= 0) {
bodyHeight = 0;
}
this.body.style.minHeight = '';
this.body.style.height = bodyHeight + "px";
}
windowWidth = jQuery(this.header).width();
windowWidth = windowWidth - (this.visibleCloseButton ? jQuery(this.closeButton.getHTML()).outerWidth() : 0);
this.dom.titleContainer.style.width = windowWidth < 0 ? 0 : windowWidth + 'px';
windowWidth = jQuery(this.html).outerWidth();
windowHeight = jQuery(this.html).outerHeight();
this.addCSSProperties({
left: '50%',
"margin-left": (windowWidth / -2) + "px",
top: '50%',
"margin-top": (windowHeight / -2) + "px"
});
return this;
};
/**
* shows the footer where the buttons are located
* @chainable
*/
Window.prototype.showFooter = function () {
this.footer.setVisible(this.visibleFooter = true);
return this.updateDimensionsAndPosition();
};
/**
* hides the footer where the buttons are located
* @chainable
*/
Window.prototype.hideFooter = function () {
this.footer.setVisible(this.visibleFooter = false);
return this.updateDimensionsAndPosition();
};
/**
* @method setTitle
* Sets the window title in the header part of the window.
* @param {String} title
* @chainable
*/
Window.prototype.setTitle = function (title) {
if (typeof title === 'string') {
this.title = title;
if (this.dom.titleContainer) {
this.dom.titleContainer.textContent = title;
this.dom.titleContainer.title = title;
}
} else {
throw new Error("The setTitle() method accepts only string values.");
}
return this;
};
/**
* @method getTitle
* its title of teh window
* @return {String}
*/
Window.prototype.getTitle = function () {
return this.title;
};
/**
* @method setModal
* Set if the window is modal or not.
* @param {Boolean} modal
* @chainable
*/
Window.prototype.setModal = function (modal) {
if (typeof modal !== 'undefined') {
this.modal = !!modal;
}
return this;
};
/**
* @inheritdoc
*/
Window.prototype.setWidth = function (width) {
Window.superclass.prototype.setWidth.call(this, width);
return this.updateDimensionsAndPosition();
};
/**
* @inheritdoc
*/
Window.prototype.setHeight = function (height) {
Window.superclass.prototype.setHeight.call(this, height);
return this.updateDimensionsAndPosition();
};
/**
* @method setFooterHeight
* high fixed size for the footer of the window
* @param {Number|String} height it can be a number or a string.
In case of using a String equals to 'auto','inherit','##px','##%','##em'
* @chainable
*/
Window.prototype.setFooterHeight = function (footerHeight) {
if (typeof footerHeight === 'number') {
this.footerHeight = footerHeight;
} else if (/^\d+(\.\d+)?px$/.test(footerHeight)) {
this.footerHeight = parseInt(footerHeight, 10);
} else if (footerHeight === 'auto') {
this.footerHeight = footerHeight;
} else {
throw new Error('setFooterHeight: footerHeight param is not valid.');
}
if (this.footer) {
this.footer.style.height = this.footerHeight + 'px';
}
if (this.isOpen) {
this.updateDimensionsAndPosition();
}
return this;
};
/**
* Sets the alignment for the items on the footer.
* @param {String} align Possible values: "center", "left", "right".
* @chainable
*/
Window.prototype.setFooterAlign = function (align) {
this.footer.setAlignment(align);
this.footerAlign = align;
return this;
};
/**
* buttonPanelPosition parameter sets that have the panel of buttons, within the window
* @param {String} buttonPanelPosition Accepted values are 'top' or 'bottom'
*/
Window.prototype.setButtonPanelPosition = function (buttonPanelPosition) {
if (!(buttonPanelPosition === 'top' || buttonPanelPosition === 'bottom')) {
throw new Error('setButtonPanelPosition(): the value is not valid, should be a "top" or "bottom"');
}
this.buttonPanelPosition = buttonPanelPosition;
if (this.html) {
if (buttonPanelPosition === 'top') {
this.html.insertBefore(this.footer.html, this.body);
} else {
this.html.appendChild(this.footer.html);
}
}
return this;
};
/**
* Clear the items in the window footer.
* @chainable
*/
Window.prototype.clearFooterItems = function () {
this.footer.clearItems();
return this;
};
/**
* Adds an item to the window footer.
* @param {Object|PMUI.ui.Button|PMUI.ui.TextLabel} item This parameter can be:
*
* - An object literal, in this case the object literal must have the property "pmType" with its value set to
* "button" (if you want the element be a {@link PMUI.ui.Button Button}) or "label" (if you want the element be a
* {@link PMUI.ui.TextLabel Label}). Optionally you can add the respective config options for each case.
*
* - A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of
* {@link PMUI.ui.TextLabel Label}.
* @chainable
*/
Window.prototype.addFooterItem = function (item) {
this.footer.addItem(item);
return this;
};
/**
* Sets the items for the window's footer.
* @param {Array} items This accepted value for this parameter is the same than the one for the
* {@link #cfg-footerItems footerItems} config option.
* @chainable
*/
Window.prototype.setFooterItems = function (items) {
var i;
if (!jQuery.isArray(items)) {
throw new Error("setFooterItems(): The parameter must be an array.");
}
this.clearFooterItems();
for (i = 0; i < items.length; i += 1) {
this.addFooterItem(items[i]);
}
return this;
};
/**
* @method close
* if the window is open, sets the status to closed
* @chainable
*/
Window.prototype.close = function () {
jQuery(this.modalObject).detach();
jQuery(this.html).detach();
jQuery(this.closeButton).detach();
if (typeof this.onClose === 'function') {
this.onClose(this);
}
this.isOpen = false;
if (document.body && this.modal) {
document.body.style.overflow = "auto";
}
return this;
};
/**
* Shows the window's close button.
* @chainable
*/
Window.prototype.showCloseButton = function () {
this.visibleCloseButton = true;
if (this.closeButton) {
this.closeButton.setVisible(true);
}
return this;
};
/**
* Adds an child item to the body HTMLElement
* @param {PMUI.core.Window|Object} item It can be one of the following data types:
* - {PMUI.core.Element} the object to add
* - {Object} a JSON object with the settings for the Container to be added
* @chainable
*/
Window.prototype.addItem = function (item) {
var itemToBeAdded;
if (this.factory) {
itemToBeAdded = this.factory.make(item);
}
if (itemToBeAdded && !this.isDirectParentOf(itemToBeAdded)) {
itemToBeAdded.parent = this;
this.items.insert(itemToBeAdded);
if (this.body) {
this.body.appendChild(itemToBeAdded.getHTML());
if (this.eventsDefined) {
itemToBeAdded.defineEvents();
}
}
}
return this;
};
/**
* @method open
* If the window is closed, sets the status to open and displays the values set in the properties window, header, body and footer
* @chainable
*/
Window.prototype.open = function () {
var the_window;
if (this.isOpen) {
return this;
}
the_window = this.getHTML();
if (this.modal) {
this.modalObject.appendChild(the_window);
document.body.appendChild(this.modalObject);
jQuery(the_window).draggable({
handle: $(this.header),
containment: '#' + this.modalObject.id,
scroll: false
});
} else {
document.body.appendChild(the_window);
jQuery(this.getHTML()).draggable();
}
if (typeof this.onOpen === 'function') {
this.onOpen(this);
}
this.isOpen = true;
this.updateDimensionsAndPosition();
this.setVisible(true);
this.defineEvents();
if (document.body && this.modal) {
document.body.style.overflow = "hidden"
}
return this;
};
/**
* Hides the window's close button.
* @chainable
*/
Window.prototype.hideCloseButton = function () {
this.visibleCloseButton = false;
if (this.closeButton) {
this.closeButton.setVisible(false);
}
return this;
};
/**
* Shows the window's close button.
* @chainable
*/
Window.prototype.showCloseButton = function () {
this.visibleCloseButton = true;
if (this.closeButton) {
this.closeButton.setVisible(true);
}
return this;
};
/**
* @method updateModalDimensions
* Updates the status of modal HTMLElement and
fixed all styles required to show
*/
Window.prototype.updateModalDimensions = function () {
if (document && this.modalObject) {
this.modalObject.style.height = this.modalObject.style.width = "0px";
this.modalObject.style.width = window.innerWidth + "px";
this.modalObject.style.height = window.innerHeight + "px";
}
return this;
};
/**
* Sets the padding for the windows body.
* @param {Number|String} bodyPadding A number or a string with a pixel-unit formatted text.d
*/
Window.prototype.setBodyPadding = function (bodyPadding) {
if (typeof bodyPadding === 'number') {
this.bodyPadding = bodyPadding
} else if (/^\d+(\.\d+)?px$/.test(height)) {
this.bodyPadding = parseInt(bodyPadding, 10);
} else {
throw new Error('setHeight: height param is not valid.');
}
this.bodyPadding = bodyPadding;
if (this.html) {
this.body.style.padding = bodyPadding + 'px';
}
return this;
};
/**
* @inheritdoc
*/
Window.prototype.paintItems = function () {
if (this.body) {
Window.superclass.prototype.paintItems.call(this);
}
return this;
};
/**
* @method defineEvents
* Defines the events associated with the Window
*/
Window.prototype.defineEvents = function () {
var that = this,
html = this.html,
modal = this.modal,
stopPropagation,
updateDimensions,
cancelWheeling = function (e) {
var height = that.body.clientHeight,
scrollHeight = that.body.scrollHeight,
scrollTop = that.body.scrollTop;
e.stopPropagation();
};
Window.superclass.prototype.defineEvents.call(this);
stopPropagation = new PMUI.event.Action({
handler: function (e) {
e.stopPropagation();
}
}),
updateDimensions = new PMUI.event.Action({
handler: function () {
that.updateModalDimensions();
}
});
this.addEvent('mousedown').listen(this.body, stopPropagation);
that.addEvent('click').listen(this.html, stopPropagation);
that.addEvent('mouseover').listen(this.html, stopPropagation);
that.addEvent('mouseout').listen(this.html, stopPropagation);
that.addEvent('mousedown').listen(this.html, stopPropagation);
that.addEvent('resize').listen(window, updateDimensions);
this.addEvent('keydown').listen(this.html, stopPropagation);
this.addEvent('keypress').listen(this.html, stopPropagation);
this.addEvent('keyup').listen(this.html, stopPropagation);
this.modalObject.addEventListener('wheel', function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
this.body.addEventListener('wheel', cancelWheeling, false);
this.footer.defineEvents();
that.closeButton.defineEvents();
this.updateModalDimensions();
return this;
};
/**
* @method createHTML
* Creates natural elements to the window html: head, body and foot
* @return {HTMLElement} returns a HTML element
*/
Window.prototype.createHTML = function () {
var header,
body,
titleContainer,
closeButton,
modal,
that = this;
if (this.html) {
return this.html;
}
Window.superclass.prototype.createHTML.call(this);
header = PMUI.createHTMLElement('div');
header.className = 'pmui-window-header';
body = PMUI.createHTMLElement('div');
body.className = 'pmui-window-body';
modal = PMUI.createHTMLElement('div');
modal.className = 'pmui-window-modal';
modal.id = this.id + "-modal";
titleContainer = PMUI.createHTMLElement('span');
titleContainer.className = 'pmui-window-title';
titleContainer.style.display = "inline-block";
closeButton = new PMUI.ui.Button({
style: {
cssClasses: ['pmui-window-close']
},
text: "",
width: '16px',
handler: function (event) {
if (typeof that.onBeforeClose === "function") {
that.onBeforeClose(that);
} else {
that.close();
}
}
});
this.modalObject = modal;
header.appendChild(titleContainer);
header.appendChild(closeButton.getHTML());
$(closeButton.html).find(".button-icon").css('opacity', 'inherit');
this.html.appendChild(header);
this.html.appendChild(body);
this.html.appendChild(this.footer.getHTML());
this.dom.titleContainer = titleContainer;
this.header = header;
this.body = this.containmentArea = body;
this.closeButton = closeButton;
this.setTitle(this.title)
.setFooterHeight(this.footerHeight)
.setItems(this.items.asArray().slice(0))
.setButtonPanelPosition(this.buttonPanelPosition);
if (this.visibleCloseButton) {
this.showCloseButton();
} else {
this.hideCloseButton();
}
if (this.visibleFooter) {
this.showFooter();
} else {
this.hideFooter();
}
if (this.eventsDefined) {
this.defineEvents();
}
return this.html;
};
PMUI.extendNamespace('PMUI.ui.Window', Window);
if (typeof exports !== "undefined") {
module.exports = Window;
}
}());
(function () {
var MessageWindow = function (settings) {
MessageWindow.superclass.call(this, settings);
/**
* The message for the message window.
* @type {String}
* @readonly
*/
this.message = null;
/**
* An object for store the necessary dom elements to build the message window HTML.
* @type {Object}
* @private
*/
this.dom = {};
this.iconClass = null;
this.windowMessageType = null;
MessageWindow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.ui.Window', MessageWindow);
MessageWindow.prototype.type = 'WindowMessage';
MessageWindow.prototype.family = 'ui';
MessageWindow.prototype.init = function (settings) {
var defaults = {
message: '[not-message]',
iconClass: '',
windowMessageType: 'default',
title: '',
footerAlign: "right"
};
jQuery.extend(true, defaults, settings);
this.setFooterAlign(defaults.footerAlign);
this.setMessage(defaults.message);
this.setWindowMessageType(defaults.windowMessageType);
this.setTitle(defaults.title);
};
MessageWindow.prototype.setMessage = function (message) {
if (typeof message !== 'string') {
throw new Error('setMessage(): the parameter must be a string.');
}
this.message = jQuery.trim(message);
if (this.dom.messageContainer) {
this.dom.messageContainer.textContent = message;
}
return this;
};
MessageWindow.prototype.getMessage = function () {
return this.message;
};
MessageWindow.prototype.setWindowMessageType = function (type) {
var classMessage = "";
if (typeof type !== "string") {
throw new Error("settype(): the type value is not valid");
}
this.windowMessageType = type;
this.style.removeClasses(["pmui-windowmessage-default", "pmui-windowmessage-error", "pmui-windowmessage-warning", "pmui-windowmessage-success"]);
classMessage = 'pmui-windowmessage-' + this.windowMessageType;
switch (this.windowMessageType) {
case 'error':
this.style.addClasses([classMessage]);
break;
case 'warning':
this.style.addClasses([classMessage]);
break;
case 'success':
this.style.addClasses([classMessage]);
break;
case 'default':
this.style.addClasses([classMessage]);
break;
}
return this;
};
MessageWindow.prototype.getWindowMessageType = function () {
return this.windowMessageType;
};
MessageWindow.prototype.addItem = function () {
return this;
};
MessageWindow.prototype.createHTML = function () {
var table,
row,
cellIcon,
cellLabel,
icon,
p,
textContent;
MessageWindow.superclass.prototype.createHTML.call(this);
table = PMUI.createHTMLElement('table');
table.className = 'pmui-messagewindow-container';
row = PMUI.createHTMLElement('tr');
cellIcon = PMUI.createHTMLElement('td');
cellLabel = PMUI.createHTMLElement('td');
cellLabel.style.textAlign = "center";
cellLabel.className = "pmui-content-label"
icon = PMUI.createHTMLElement('span');
icon.className = 'pmui-messagewindow-icon';
textContent = PMUI.createHTMLElement('span');
textContent.className = 'pmui-messagewindow-message';
cellIcon.appendChild(icon);
row.appendChild(cellIcon);
cellLabel.appendChild(textContent);
row.appendChild(cellLabel);
table.appendChild(row);
this.dom.icon = icon;
this.dom.messageContainer = textContent;
this.body.appendChild(table);
this.setMessage(this.message);
this.body.style.padding = "15px";
cellLabel.style.width = (typeof this.width == 'number') ? this.width * (0.84) + "px" : "70%";
this.body.style.minHeight = "inherit";
return this.html;
};
MessageWindow.prototype.updateDimensionsAndPosition = function () {
var bodyHeight,
footerHeight,
headerHeight,
windowHeight = this.height,
windowWidth;
if (!this.footer || !this.html || !this.isOpen) {
return this;
}
if (this.footerHeight === 'auto') {
this.footer.setHeight('auto');
} else {
this.footer.setHeight(this.footerHeight);
}
if (windowHeight === 'auto') {
this.body.style.height = 'auto';
this.body.style.minHeight = 'inherit';
} else {
if (/^\d+(\.\d+)?em$/.test(windowHeight)) {
windowHeight = PMUI.emToPx(parseInt(windowHeight, 10), this.modalObject);
} else if (/^\d+(\.\d+)?%$/.test(windowHeight)) {
windowHeight = jQuery(this.html).outerHeight();
}
footerHeight = this.visibleFooter ? jQuery(this.footer.getHTML()).outerHeight() : 0;
headerHeight = jQuery(this.header).outerHeight();
bodyHeight = windowHeight - footerHeight - headerHeight;
if (bodyHeight <= 0) {
bodyHeight = 0;
}
this.body.style.minHeight = '';
this.body.style.height = bodyHeight + "px";
}
windowWidth = jQuery(this.header).width();
windowWidth = windowWidth - (this.visibleCloseButton ? jQuery(this.closeButton.getHTML()).outerWidth() : 0);
this.dom.titleContainer.style.width = windowWidth < 0 ? 0 : windowWidth + 'px';
windowWidth = jQuery(this.html).outerWidth();
windowHeight = jQuery(this.html).outerHeight();
this.addCSSProperties({
left: '50%',
"margin-left": (windowWidth / -2) + "px",
top: '50%',
"margin-top": (windowHeight / -2) + "px"
});
return this;
}
PMUI.extendNamespace('PMUI.ui.MessageWindow', MessageWindow);
if (typeof exports !== "undefined") {
module.exports = MessageWindow;
}
}());
(function () {
/**
* @class PMUI.ui.ErrorMessageWindow
* A window to show a error message.
* @extends {PMUI.ui.MessageWindow}
*
* Usage example:
*
* @example
* var message_window = new PMUI.ui.ErrorMessageWindow({
* message: 'This is an error message.'
* });
* message_window.open();
*
* @constructor
* Creates a new instance of the class ErrorMessageWindow
* @param {Object} [settings={}] An object with the config options.
*
* @cfg {String} [title='Error'] The message to show in the message window.
*/
var ErrorMessageWindow = function (settings) {
ErrorMessageWindow.superclass.call(this, settings);
ErrorMessageWindow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.ui.MessageWindow', ErrorMessageWindow);
/**
* Initializes the object.
* @param {Object} settings The object with the config options.
* @private
*/
ErrorMessageWindow.prototype.init = function (settings) {
var defaults = {
title: 'Error'
};
jQuery.extend(true, defaults, settings);
this.setTitle(defaults.title);
};
/**
* Creates the message window HTML.
* @return {HTMLElement}
*/
ErrorMessageWindow.prototype.createHTML = function () {
ErrorMessageWindow.superclass.prototype.createHTML.call(this);
jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-error');
return this.html;
};
PMUI.extendNamespace('PMUI.ui.ErrorMessageWindow', ErrorMessageWindow);
if (typeof exports !== "undefined") {
module.exports = ErrorMessageWindow;
}
}());
(function () {
/**
* @class PMUI.ui.WarningMessageWindow
* A window to show a warning message.
* @extends {PMUI.ui.MessageWindow}
*
* Usage example:
*
* @example
* var message_window = new PMUI.ui.WarningMessageWindow({
* message: 'This is a warning message.'
* });
* message_window.open();
*
* @constructor
* Creates a new instance of the class WarningMessageWindow
* @param {Object} [settings={}] An object with the config options.
*
* @cfg {String} [title='Warning'] The message to show in the message window.
*/
var WarningMessageWindow = function (settings) {
WarningMessageWindow.superclass.call(this, settings);
WarningMessageWindow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.ui.MessageWindow', WarningMessageWindow);
/**
* Initializes the object.
* @param {Object} settings The object with the config options.
* @private
*/
WarningMessageWindow.prototype.init = function (settings) {
var defaults = {
title: 'Warning'
};
jQuery.extend(true, defaults, settings);
this.setTitle(defaults.title);
};
/**
* Creates the message window ĤTML.
* @return {HTMLElement}
*/
WarningMessageWindow.prototype.createHTML = function () {
WarningMessageWindow.superclass.prototype.createHTML.call(this);
jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-warning');
return this.html;
};
PMUI.extendNamespace('PMUI.ui.WarningMessageWindow', WarningMessageWindow);
if (typeof exports !== "undefined") {
module.exports = WarningMessageWindow;
}
}());
(function () {
/**
* @class PMUI.ui.SuccessMessageWindow
* A window to show a success message.
* @extends {PMUI.ui.MessageWindow}
*
* Usage example:
*
* @example
* var message_window = new PMUI.ui.SuccessMessageWindow({
* message: 'This is a success message.'
* });
* message_window.open();
*
* @constructor
* Creates a new instance of the class SuccessMessageWindow
* @param {Object} [settings={}] An object with the config options.
*
* @cfg {String} [title='Success'] The message to show in the message window.
*/
var SuccessMessageWindow = function (settings) {
SuccessMessageWindow.superclass.call(this, settings);
SuccessMessageWindow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.ui.MessageWindow', SuccessMessageWindow);
/**
* Initializes the object.
* @param {Object} settings The object with the config options.
* @private
*/
SuccessMessageWindow.prototype.init = function (settings) {
var defaults = {
title: 'Success'
};
jQuery.extend(true, defaults, settings);
this.setTitle(defaults.title);
};
/**
* Creates the message window ĤTML.
* @return {HTMLElement}
*/
SuccessMessageWindow.prototype.createHTML = function () {
SuccessMessageWindow.superclass.prototype.createHTML.call(this);
jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-success');
return this.html;
};
PMUI.extendNamespace('PMUI.ui.SuccessMessageWindow', SuccessMessageWindow);
if (typeof exports !== "undefined") {
module.exports = SuccessMessageWindow;
}
}());
(function () {
/**
* @class PMUI.ui.InfoMessageWindow
* A window to show an info message.
* @extends {PMUI.ui.MessageWindow}
*
* Usage example:
*
* @example
* var message_window = new PMUI.ui.InfoMessageWindow({
* message: 'This is an info message.'
* });
* message_window.open();
*
* @constructor
* Creates a new instance of the class InfoMessageWindow
* @param {Object} [settings={}] An object with the config options.
*
* @cfg {String} [title='Information'] The message to show in the message window.
*/
var InfoMessageWindow = function (settings) {
InfoMessageWindow.superclass.call(this, settings);
InfoMessageWindow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.ui.MessageWindow', InfoMessageWindow);
/**
* Initializes the object.
* @param {Object} settings The object with the config options.
* @private
*/
InfoMessageWindow.prototype.init = function (settings) {
var defaults = {
title: 'Info'
};
jQuery.extend(true, defaults, settings);
this.setTitle(defaults.title);
};
/**
* Creates the message window HTML.
* @return {HTMLElement}
*/
InfoMessageWindow.prototype.createHTML = function () {
InfoMessageWindow.superclass.prototype.createHTML.call(this);
jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-info');
return this.html;
};
PMUI.extendNamespace('PMUI.ui.InfoMessageWindow', InfoMessageWindow);
if (typeof exports !== "undefined") {
module.exports = InfoMessageWindow;
}
}());
(function () {
/**
* @class PMUI.ui.ConfirmMessageWindow
* A window to show a question message. It haas built-in Yes and No buttons, but they can be overrode by some
* custom ones.
* @extends {PMUI.ui.MessageWindow}
*
* Usage example:
*
* @example
* var message_window = new PMUI.ui.ConfirmMessageWindow({
* message: 'Do you want to display an alert?',
* onYes: function() {
* alert("this is an alert");
* message_window.close();
* },
* onNo: function() {
* message_window.close();
* }
* });
* message_window.open();
*
* @constructor
* Creates a new instance of the class ConfirmMessageWindow
* @param {Object} [settings={}] An object with the config options.
*
* @cfg {String} [title='Error'] The message to show in the message window.
* @cfg {String} [yesText='Yes'] The text for the window's built-in Yes button.
* @cfg {String} [noText='No'] The text for the window's built-in No button.
* @cfg {Function|null} [onYes=null] The handler to be executed when the built-in Yes button is clicked.
* @cfg {Function|null} [onNo=null] The handler to be executed when the built-in No button is clicked.
* @cfg footerItems
* The items to be shown in the message window's footer, the accepted values for this are the same that the ones
* for the Window's footerItems config option.
*
* By default this is an empty array, if you modify this then the built-in Yes and No buttons won't be used,
* therefore the yesText, noText config options and onYes, onNo handlers won't be used either.
*/
var ConfirmMessageWindow = function (settings) {
ConfirmMessageWindow.superclass.call(this, settings);
ConfirmMessageWindow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.ui.MessageWindow', ConfirmMessageWindow);
/**
* Initializes the object.
* @param {Object} settings The object with the config options.
* @private
*/
ConfirmMessageWindow.prototype.init = function (settings) {
var yesButton = new PMUI.ui.Button(),
noButton = new PMUI.ui.Button(),
defaults = {
title: 'Confirm',
yesText: 'Yes',
noText: 'No',
onYes: null,
onNo: null,
footerItems: []
};
jQuery.extend(true, defaults, settings);
this.setTitle(defaults.title);
if (!defaults.footerItems || !defaults.footerItems.length) {
defaults.footerItems = [
yesButton.setHandler(defaults.onYes).setText(defaults.yesText),
noButton.setHandler(defaults.onNo).setText(defaults.noText)
];
}
this.setFooterItems(defaults.footerItems);
};
/**
* @inheritdoc
*/
ConfirmMessageWindow.prototype.createHTML = function () {
ConfirmMessageWindow.superclass.prototype.createHTML.call(this);
jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-confirm');
return this.html;
};
PMUI.extendNamespace('PMUI.ui.ConfirmMessageWindow', ConfirmMessageWindow);
if (typeof exports !== "undefined") {
module.exports = ConfirmMessageWindow;
}
}());
(function () {
/**
* @class PMUI.ui.Button
* @extend PMUI.core.Element
* * Usage example (only for subclasses since this is an abstract class):
*
* @example
* //Remember, this is an abstract class so it shouldn't be instantiate,
* //anyway we are instantiating it just for this example
*
* var a;
*
* a = new PMUI.ui.Button({
* handler : function () {
* alert("hi!");
* },
* text : 'closed',
* iconClass:"pmui-gridpanel-pager_previous",
* iconPosition : "left",
* messageTooltip : 'this button to close the window'
* });
*
* document.body.appendChild(a.getHTML());
* a.defineEvents();
*
* @constructor
* Create a new instace of the Window class
* @param {Object} settigs Constructor object
*/
var Button = function (settings, parent) {
Button.superclass.call(this, jQuery.extend(settings, {elementTag: "a"}));
/**
* @property icon for the Button, it can a string
* @type {String}
*/
this.icon = null;
/**
* @property {PMUI.event.Action} Defines the Action Object to handle he action windows
* @type {Action}
*/
this.action = null;
/**
* @property parent
* @type {String}
*/
this.parent = null;
/**
* @property text of the Button
* @type {String}
*/
this.text = null;
/**
* @property alias Button for idetified to alias
* @type {String}
*/
this.aliasButton = null;
/**
* [iconPosition description]
* @type {[type]}
*/
this.iconPosition = null;
/**
* [messageTooltip description]
* @type {[type]}
*/
this.messageTooltip = null;
//this.buttonClass = null;
//this.foreignClassName = null;
this.linkStyle = null;
this.labelVisible = null;
this.iconVisible = null;
this.disabled = null;
this.dom = null;
this.buttonType = null;
Button.prototype.init.call(this, settings, parent);
};
PMUI.inheritFrom('PMUI.core.Element', Button);
Button.prototype.type = 'Button';
Button.prototype.family = 'Button';
Button.prototype.init = function (settings, parent) {
var defaults;
defaults = {
iconClass: '',
aliasButton: null,
parent: parent || null,
height: "auto",
width: "auto",
handler: function () {
},
text: '[button]',
iconPosition: "right",
tooltip: false,
messageTooltip: "",
//buttonClass : '',
iconVisible: true,
labelVisible: true,
disabled: false,
//cls: 'pmui-button',
//linkStyle: false,
//foreignClassName : "",
//useForeignClass : false
buttonType: 'default'
};
this.dom = {};
jQuery.extend(true, defaults, settings);
this.setAliasButton(defaults.aliasButton)
.setParent(defaults.parent)
.setText(defaults.text)
.setIcon(defaults.iconClass)
.setWidth(defaults.width)
.setHeight(defaults.height)
.setIconPosition(defaults.iconPosition)
.setHandler(defaults.handler)
.setTooltipMessage(defaults.messageTooltip)
.setDisabled(defaults.disabled)
.setButtonType(defaults.buttonType);
};
Button.prototype.setButtonType = function (buttonType) {
var classButton = "";
if (typeof buttonType !== "string") {
throw new Error("setButtonType(): the type value is not valid");
}
this.buttonType = buttonType;
this.style.removeClasses(["pmui-error", "pmui-warning", "pmui-success", "pmui-info", "pmui-link"]);
classButton = 'pmui-' + this.buttonType;
switch (this.buttonType) {
case 'error':
this.style.addClasses([classButton]);
break;
case 'warning':
this.style.addClasses([classButton]);
break;
case 'success':
this.style.addClasses([classButton]);
break;
case 'info':
this.style.addClasses([classButton]);
break;
case 'link':
this.style.addClasses([classButton]);
break;
}
return this;
};
Button.prototype.showLabel = function () {
this.labelVisible = true;
if (this.html) {
this.label.style.display = '';
}
return this;
};
Button.prototype.setDisabled = function (value) {
if (typeof value !== 'boolean') {
throw new Error('setDisabled(): the parameter is not valid, should be type boolean');
}
this.disabled = value;
if (this.html) {
if (this.disabled) {
this.disable();
} else {
this.enable();
}
}
return this;
}
Button.prototype.disable = function () {
this.disabled = true;
this.style.addClasses(['pmui-disabled']);
if (this.eventsDefined) {
this.defineEvents();
}
return this;
};
/**
* @method enable
* button can be enabled if it was disabled before
* @chainable
*/
Button.prototype.enable = function () {
this.disabled = false;
this.style.removeClasses(['pmui-disabled']);
if (this.eventsDefined) {
this.defineEvents();
}
return this;
};
/**
* Performs the click action on the button.
* @chainable
*/
Button.prototype.click = function () {
jQuery(this.html).trigger('click');
return this;
};
/**
* @method setHandler
* Sets the handler for the button
* @param {Function} handler is a Function.
* @chainable
*/
Button.prototype.setHandler = function (handler) {
if (typeof handler !== "function" && handler !== null) {
throw new Error("setHandler(): the parameter should be a function or null;");
}
this.handler = handler;
this.action = new PMUI.event.Action({
actionText: this.aliasButton,
handler: this.handler
});
return this;
};
/**
* @method removeEvent
* Remove Event for the button
* @param {Function} handler is a Function.
* @chainable
*/
Button.prototype.removeEvent = function (type) {
jQuery(this.html).unbind(type);
return this;
};
/**
* @method removeEvents
* Remove Events for the button
* @param {Function} handler is a Function.
* @chainable
*/
Button.prototype.removeEvents = function () {
var x;
for (x in this.events) {
this.removeEvent(this.events[x].eventName);
}
this.events = {};
return this;
};
/**
* Sets the text for the label Button.
* @param {String} is a String.
* @chainable
*/
Button.prototype.setText = function (text) {
this.text = text;
if (this.html && this.dom.spanText) {
if (jQuery.trim(text) !== "") {
this.dom.spanText.textContent = this.text;
} else {
this.dom.spanText.inerHTML = ' ';
}
}
return this;
};
/**
* Sets the icon for the Button.
* @param {String} is a String.
* @chainable
*/
Button.prototype.setIcon = function (iconClass) {
var icon;
if (!(typeof iconClass == "string")) {
throw new Error("setIcon(): the property should bo string");
}
this.icon = iconClass;
if (this.html && this.dom.icon) {
this.dom.icon.className = this.dom.icon.className + " " + this.icon;
}
return this;
};
/**
* @method setAliasButton
* Sets the alias for the label Button
* @param {String} is a String. lue
* @chainable
*/
Button.prototype.setAliasButton = function (alias) {
this.aliasButton = alias;
return this;
};
/**
* @method setParent
* Sets the parent for the Button.
* @param {Object} is a HTML Element.
* @chainable
*/
Button.prototype.setParent = function (parent) {
this.parent = parent;
return this;
};
/**
* @method createHTML
* Create HTML for the element
* @return {HTMLElement} HTML Generated
*/
Button.prototype.createHTML = function () {
var spanText,
icon;
if (this.html) {
return this.html;
}
Button.superclass.prototype.createHTML.call(this);
this.html.href = '#';
//***conatainer for text
spanText = PMUI.createHTMLElement('span');
spanText.className = "button-label";
this.dom.spanText = spanText;
this.html.appendChild(spanText);
// container for icon
icon = PMUI.createHTMLElement('span');
icon.className = 'button-icon';
this.dom.icon = icon;
this.html.appendChild(icon);
this.setIconPosition(this.iconPosition);
this.setTooltipMessage(this.messageTooltip);
this.setDisabled(this.disabled);
this.setText(this.text);
this.setIcon(this.icon);
this.applyStyle();
return this.html;
};
/**
* @method defineEvents
* Define events click for the Button, listening the function action
*/
Button.prototype.defineEvents = function () {
var that = this;
Button.superclass.prototype.defineEvents.call(this);
if (this.html) {
if (this.disabled) {
this.addEvent('click').listen(this.html, function (e) {
e.preventDefault();
e.stopPropagation();
});
} else {
this.addEvent('click').listen(this.html, function (e) {
e.preventDefault();
e.stopPropagation();
if (typeof that.handler === 'function') {
that.handler(that);
}
});
}
}
return this;
};
/**
* @method setHeight
* Sets the height for the button.
* @param {String, number} height
* This parameter can take the following types
* - number
* - string
*/
Button.prototype.setHeight = function (height) {
if (typeof height === 'number') {
this.height = height + "px";
} else if (/^\d+(\.\d+)?px$/.test(height)) {
this.height = height;
} else if (/^\d+(\.\d+)?%$/.test(height)) {
this.height = height;
} else if (/^\d+(\.\d+)?em$/.test(height)) {
this.height = height;
} else if (height === 'auto' || height === 'inherit') {
this.height = height;
} else {
throw new Error('setHeight: height param is not a number');
}
if (this.height !== 'auto') {
this.style.addProperties({"line-height": parseInt(this.height, 10) + "px"});
} else {
this.style.addProperties({"line-height": 'normal'});
}
this.applyStyle();
return this;
};
/**
* @method setIconPosition
* Creates the HTML element for the control.
* @param {String} position
* This parameter can take the following values: left, right
*/
Button.prototype.setIconPosition = function (position) {
if (position == 'right' || position == "left" || position == "top" || position == "bottom") {
this.iconPosition = position;
if (this.html) {
if (this.iconPosition == 'left' || this.iconPosition == 'top') {
$(this.html).prepend(this.dom.icon);
if (this.iconPosition == 'left') {
this.dom.icon.style.display = 'inline-block';
this.dom.icon.style.marginLeft = '';
} else {
this.dom.icon.style.display = 'block';
this.dom.icon.style.marginLeft = '50%';
}
} else {
$(this.html).append(this.dom.icon);
if (this.iconPosition == 'right') {
this.dom.icon.style.display = 'inline-block';
this.dom.icon.style.marginLeft = '';
} else {
this.dom.icon.style.display = 'block';
this.dom.icon.style.marginLeft = '50%';
}
}
}
} else {
throw new Error("setIconPosition(): the parameter is not valid, should be 'right','left', 'top' or 'bottom'");
}
return this;
};
/**
* @method setTooltipMessage
* Sets the tooltip message
* @param {String} messageTooltip
*/
Button.prototype.setTooltipMessage = function (messageTooltip) {
if (typeof messageTooltip == "string") {
this.messageTooltip = messageTooltip;
if (this.html) {
this.html.setAttribute('title', messageTooltip);
}
}
return this;
};
PMUI.extendNamespace("PMUI.ui.Button", Button);
if (typeof exports !== "undefined") {
module.exports = Button;
}
}());
(function () {
/**
* @class PMUI.ui.TooltipMessage
* @extends PMUI.core.Element
* Class to handle tooltip elements and alert messages
*
* Usage example (creates 4 TooltipMessage instances):
*
* @example
* //usage example
* //creates 4 TooltipMessage instances:
* var a, b, c, d;
* $(function() {
* // Instantiates a simple info message
* a = new PMUI.ui.TooltipMessage({
* message: "Hi folks! this is a info",
* category: "info",
* displayMode: "block",
* mode: "normal"
* });
* //Instantiates an error tooltip with mouse tracking option enabled
* b = new PMUI.ui.TooltipMessage({
* message: "Hi folks! this is an error",
* category: "error",
* track: true,
* displayMode: "block"
* });
* //Instantiates a warning tooltip with a slide down effect when the tooltip is opened
* c = new PMUI.ui.TooltipMessage({
* message: "Hi folks! this is a warning",
* category: "warning",
* displayMode: "block",
* showEffect: {
* effect: "slideDown",
* delay: 250
* }
* });
* //Instantiates a help tooltip with a explode effect when the tooltip is hidden
* d = new PMUI.ui.TooltipMessage({
* message: "Hi folks! this is a help",
* category: "help",
* displayMode: "block",
* hideEffect: {
* effect: "explode",
* delay: 250
* }
* });
* document.body.appendChild(a.getHTML());
* document.body.appendChild(b.getHTML());
* document.body.appendChild(c.getHTML());
* document.body.appendChild(d.getHTML());
* });
* @constructor
* Create a new instace of the class 'TooltipMessage'
* @param {Object} settings
* A JSON object which can contain the following fields:
*
* @cfg {String} [message = ""] The message to show
* @cfg {String} [category = 'help'] The object's category, it can be "info", "help", "warning" or "error"
* @cfg {String} [displayMode = 'inline'] Determines if the object will have an "inline" or "block" displaying
* @cfg {String} [mode="tooltip"] When is set to "tooltip" the object has a tooltip behavior, but when is set to "normal" it
* has a regular message behavior
* @cfg {String} [tooltipClass='pmui-tooltip-message'] the css class for the HTML element which will contain the message
* @cfg {Object} [tooltipPosition = {
my: "left top+15",
at: "left bottom",
collision: "flipfit"
}] specifies the position for the tooltip, read the
{@link PMUI.ui.TooltipMessage#setTooltipPosition .setTooltipPosition()} for more info
* @cfg {Object} [showEffect=null] specifies the animation to apply when the tooltip is shown, read the
{@link PMUI.ui.TooltipMessage#setShowEffect .setShowEffect()} for more info
* @cfg {Object} [hideEffect=null] specifies the animation to apply when the tooltip is hidden, read the
{@link PMUI.ui.TooltipMessage#setHideEffect .setHideEffect()} for more info
* @cfg {Function} [onOpen] a callback function to be invoked when the tooltip is shown
* @cfg {Function} [onClose] a callback functio to be invoked when the tooltip is hidden
* @cfg {Boolean} [track = false] a turns on/off the tooltip to follow the mouse movement
* Note: tooltipClass, tooltipPosition, showEffect, hideEffect, onOpen, onClose and track options
only are applied when the mode is set to "tooltip"
*/
var TooltipMessage = function (settings) {
TooltipMessage.superclass.call(this, settings);
/**
* @property {PMUI.core.Element} icon
* An {@link PMUI.core.Element} which is used to put the icon in
* @private
*/
this.icon = null;
/**
* An Element
* @type {PMUI.core.Element} which is used to display the message when the mode is set to "normal"
* @private
*/
this.messageArea = null;
/**
* @property {String} [message=""]
* The message to be displayed
* @readonly
*/
this.message = null;
/**
* @property {String} [category="help"]
* The category for the message
* @readonly
*/
this.category = null;
/**
* @property {String} [displayMode="inline"]
* The displaying mode for the object's HTML element
* @readonly
*/
this.displayMode = null;
/**
* @property {String} [mode="tooltip"]
* The object's behavior mode
* @readonly
*/
this.mode = null;
/**
* @property {Object} [tooltipPosition={
my: "left top+15",
at: "left bottom",
collision: "flipfit"
}]
* The tooltip position
* @readonly
*/
this.tooltipPosition = null;
/**
* @property {String} [tooltipClass="pmui-tooltip-message"]
* The CSS class name for the tooltip HTML element
* @readonly
*/
this.tooltipClass = null;
/**
* @property {Object|Boolean|String|Number} [showEffect=null]
* The effect to apply when the tooltip is shown
*/
this.showEffect = null;
/**
* @property {Object|Boolean|String|Number} [hideEffect=null]
* The effect to apply when the tooltip is hidden
*/
this.hideEffect = null;
/**
* @property {Function} [onOpen=null]
* The function callback to be invoked when the tooltip is shown
*/
this.onOpen = null;
/**
* @property {Function} [onClose=null]
* The function callback to be invoked when the tooltip is hidden
* @type {Function}
*/
this.onClose = null;
/**
* @property {Boolean} [track=false]
* A boolean that specifies if the the tooltip element must follow the mouse position
*/
this.track = null;
TooltipMessage.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', TooltipMessage);
TooltipMessage.prototype.type = 'PMUITooltipMessage';
TooltipMessage.prototype.family = 'PMUITooltipMessage';
TooltipMessage.prototype.init = function (settings) {
var defaults = {
message: "",
category: "help",
displayMode: "inline",
mode: "tooltip",
tooltipClass: 'pmui-tooltip-message',
tooltipPosition: {
my: "left top+15",
at: "left bottom",
collision: "flipfit"
},
showEffect: null,
hideEffect: null,
onOpen: null,
onClose: null,
track: false
};
$.extend(true, defaults, settings);
this.onClose = defaults.onClose;
this.onOpen = defaults.onOpen;
this.setTooltipClass(defaults.tooltipClass)
.setTooltipPosition(defaults.tooltipPosition)
.setShowEffect(defaults.showEffect)
.setHideEffect(defaults.hideEffect)
.setTrack(defaults.track)
.setMessage(defaults.message)
.setCategory(defaults.category)
.setDisplayMode(defaults.displayMode)
.setMode(defaults.mode);
};
/**
* Set the CSS class for the HTML element which will contain the tooltip message.
* This only takes effect when the mode property is set to "tooltip"
* @param {String} tooltipClass
*/
TooltipMessage.prototype.setTooltipClass = function (tooltipClass) {
this.tooltipClass = tooltipClass;
return this;
};
/**
* Establish if the HTML element which will be contain the tooltip message should track
(follow) the mouse position when it appears
* * This only takes effect when the mode property is set to "tooltip"
* @param {Boolean} [track=true]
*/
TooltipMessage.prototype.setTrack = function (track) {
this.track = !!track;
if (this.html) {
this.setMode(this.mode);
}
return this;
};
/**
* [setHideEffect description]
* If and how to animate the hiding of the tooltip.
* This only takes effect when the mode property is set to "tooltip"
* @param {Object|Boolean|String|Number} effect
* - Boolean: When set to false, no animation will be used and the tooltip will be hidden
immediately. When set to true, the tooltip will fade out with the default duration and the
default easing.
* - Number: The tooltip will fade out with the specified duration and the default easing.
* - String: The tooltip will be hidden using the specified effect. The value can either be the
name of a built-in jQuery animation method, such as "slideUp", or the name of a jQuery UI effect,
such as "fold". In either case the effect will be used with the default duration and the default easing.
* - Object: If the value is an object, then effect, delay, duration, and easing properties may be
provided. If the effect property contains the name of a jQuery method, then that method will be used;
otherwise it is assumed to be the name of a jQuery UI effect. When using a jQuery UI effect that supports
additional settings, you may include those settings in the object and they will be passed to the effect.
If duration or easing is omitted, then the default values will be used. If effect is omitted,
then "fadeOut" will be used. If delay is omitted, then no delay is used.
*/
TooltipMessage.prototype.setHideEffect = function (effect) {
this.hideEffect = effect;
if (this.html) {
this.setMode(this.mode);
}
return this;
};
/**
* If and how to animate the showing of the tooltip.
* This only takes effect when the mode property is set to "tooltip"
* @param {Object|Boolean|String|Number} effect
* - Boolean: When set to false, no animation will be used and the tooltip will be shown immediately.
When set to true, the tooltip will fade in with the default duration and the default easing.
* - Number: The tooltip will fade in with the specified duration and the default easing.
* - String: The tooltip will be shown using the specified effect. The value can either be the name of
a built-in jQuery animation method, such as "slideDown", or the name of a jQuery UI effect, such as
"fold". In either case the effect will be used with the default duration and the default easing.
* - Object: If the value is an object, then effect, delay, duration, and easing properties may be
provided. If the effect property contains the name of a jQuery method, then that method will be used;
otherwise it is assumed to be the name of a jQuery UI effect. When using a jQuery UI effect that
supports additional settings, you may include those settings in the object and they will be passed
to the effect. If duration or easing is omitted, then the default values will be used. If effect is
omitted, then "fadeIn" will be used. If delay is omitted, then no delay is used.
*/
TooltipMessage.prototype.setShowEffect = function (effect) {
this.showEffect = effect;
if (this.html) {
this.setMode(this.mode);
}
return this;
};
/**
* Identifies the position of the tooltip in relation to the associated target element.
* The of option defaults to the target element, but you can specify another element to position
against. You can refer to the [jQuery UI Position][1] utility for more details about the various options.
* [1]: http://api.jqueryui.com/position
* @param {Object} position
*/
TooltipMessage.prototype.setTooltipPosition = function (position) {
this.tooltipPosition = position;
if (this.html) {
this.setMode(this.mode);
}
return this;
};
/**
* Sets the message to be shown in both normal and tooltip modes
* @param {String} message
*/
TooltipMessage.prototype.setMessage = function (message) {
if (typeof message === 'string') {
this.message = message;
if (this.html) {
if (this.messageArea) {
this.messageArea.getHTML().textContent = message;
}
if (this.mode === 'tooltip') {
this.icon.html.title = message;
} else {
this.icon.html.title = "";
}
}
} else {
throw new Error("setMessage() method only accepts string values.");
}
return this;
};
/**
* Set the category for the tooltip/message object
* @param {String} category the value can be one of the following:
*
* - help
* - info
* - error
* - warning
*/
TooltipMessage.prototype.setCategory = function (category) {
var validCategories = [
"help", "info", "error", "warning"
];
if (typeof category === 'string' && validCategories.indexOf(category) > -1) {
this.category = category;
if (this.icon && this.messageArea) {
this.icon.style.removeAllClasses();
this.icon.style.addClasses(['pmui-icon', 'pmui-icon-' + category]);
this.messageArea.className = 'pmui-tooltip-message pmui-tooltip-' + category + '-message';
}
if (this.html) {
if (this.category === "error") {
this.style.addClasses(["pmui-tooltip-category-error"]);
} else {
this.style.removeClasses(["pmui-tooltip-category-error"]);
}
}
} else {
throw new Error('setCategory() method only accepts' +
' one of the following values: "help", "info", "warning", "info".');
}
return this;
};
/**
* Sets the CSS displaying mode
* @param {String} displayMode It can take the "block" or "inline" values
*/
TooltipMessage.prototype.setDisplayMode = function (displayMode) {
if (displayMode === 'block' || displayMode === 'inline') {
this.displayMode = displayMode;
if (this.html) {
this.style.addProperties({"display": displayMode});
}
} else {
throw new Error('setDisplayMode() method only accepts "inline" or "block" values');
}
return this;
};
/**
* Sets the mode for the object, it determines if the current object has a tooltip or notification message behavior
* @param {String} mode It only can take one of the following values:
* - "tooltip"
* - "normal"
*/
TooltipMessage.prototype.setMode = function (mode) {
if (mode === 'tooltip' || mode === 'normal') {
this.mode = mode;
if (this.html) {
$(this.html).addClass('pmui-tooltip-mode-' + mode);
if (mode === 'tooltip') {
this.messageArea.setVisible(false);
this.icon.html.title = this.message;
$(this.icon.html).tooltip({
tooltipClass: this.tooltipClass,
position: this.tooltipPosition,
show: this.showEffect,
hide: this.hideEffect,
open: this.onOpen,
track: this.track,
close: this.onClose
});
} else {
try {
$(this.icon.html).tooltip('destroy');
} catch (ex) {
}
this.icon.html.title = "";
this.messageArea.setVisible(true);
}
}
} else {
throw new Error('setMode() method only accepts "tooltip" or "normal" values');
}
return this;
};
/**
* It creates the HTML Element for the current object
* @return {HTMLElement}
*/
TooltipMessage.prototype.createHTML = function () {
var html,
messageArea,
icon;
if (this.html) {
return this.html;
}
html = PMUI.createHTMLElement("span");
html.className = 'pmui-tooltip';
icon = new PMUI.core.Element({
elementTag: 'span',
width: 18,
height: 18,
style: {
cssClasses: ['pmui-icon'],
cssProperties: {
display: "inline-block",
"vertical-align": "middle"
}
}
});
messageArea = new PMUI.core.Element({
elementTag: 'span',
style: {
cssClasses: ['pmui-tooltip-message']
}
});
html.appendChild(icon.getHTML());
html.appendChild(messageArea.getHTML());
this.icon = icon;
this.messageArea = messageArea;
this.html = html;
this.applyStyle();
this.setCategory(this.category);
this.setMessage(this.message);
this.setMode(this.mode);
return this.html;
};
PMUI.extendNamespace('PMUI.ui.TooltipMessage', TooltipMessage);
if (typeof exports !== "undefined") {
module.exports = TooltipMessage;
}
}());
(function () {
/**
* @class PMUI.ui.TextLabel
* A text label that can contain a plain text or an HTML formatted text.
* @extends {PMUI.core.Element}
*
* Usage example:
*
* @example
* var plainText, htmlText;
* plainText = new PMUI.ui.TextLabel({
* textMode: 'plain',
* text: 'Hello! this is a label with a plain text in it',
* style: {
* cssProperties: {
* background: "red"
* }
* }
* });
* htmlText = new PMUI.ui.TextLabel({
* textMode: 'html',
* text: '
Hello!
this ia a label with a HTML formatted text'
* });
* document.body.appendChild(plainText.getHTML());
* document.body.appendChild(htmlText.getHTML());
*
* @constructor
* Creates a new instance of the class.
* @param {Object} [settings={}] An object literal with the config options to config the object.
*
* @cfg {String} [textMode='plain'] The rendering mode for the text in the label. Possible values:
*
* - "plain": for plain text rendering.
* - "html": for html text rendering. Only the , , to tags are allowed, any other tags will be
* removed.
* @cfg {String} [elementTag='span'] The HTML tag to be used as the text label.
* @cfg {String} [text=""] The label's text.
*/
var TextLabel = function (settings) {
TextLabel.superclass.call(this, settings);
/**
* The text rendering mode. Set by the {@link #cfg-textMode textMode config option} and the
* {@link #method-setTextMode setTextMode() method}.
* @type {String}
* @readonly
*/
this.textMode = null;
/**
* The current displayed label's text. Set by the {@link #cfg-text text config option} and the
* {@link #method-setText setText() method}.
* @type {String}
* @readonly
*/
this.text = null;
/**
* The original set text.
* @type {String}
* @private
*/
this.rawText = null;
TextLabel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', TextLabel);
/**
* The object's type.
* @type {String}
*/
TextLabel.prototype.type = 'TextLabel';
/**
* Initializes the object.
* @param {Object} [settings={}] An object literal with the config options the new object will be initialized
* with.
* @chainable
* @private
*/
TextLabel.prototype.init = function (settings) {
var defaults = {
textMode: 'plain',
elementTag: 'span',
text: ''
};
jQuery.extend(true, defaults, settings);
this.setElementTag(defaults.elementTag)
.setTextMode(defaults.textMode)
.setText(defaults.text);
return this;
};
/**
* Sets the label's text.
* @param {String} text
*/
TextLabel.prototype.setText = function (text) {
var tags;
if (typeof text !== 'string') {
throw new Error('setText(): the parameter must be a string.');
}
this.text = this.rawText = text;
if (this.textMode === 'html') {
allowed = ("".match(/<[a-z][a-z0-9]*>/g) || []).join('');
tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /|<\?(?:php)?[\s\S]*?\?>/gi;
this.text = text.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
});
}
if (this.html) {
if (this.textMode === 'html') {
this.html.innerHTML = this.text;
} else {
this.html.textContent = this.text;
}
}
return this;
};
/**
* Sets the text rendering mode.
* @param {String} textMode Possible values:
*
* - "plain": for plain text rendering.
* - "html": for html text rendering. Only the , , to tags are allowed, any other tags will be
* removed.
*/
TextLabel.prototype.setTextMode = function (textMode) {
if (textMode !== 'plain' && textMode !== 'html') {
throw Error("setTextMode(): The parameter must be \"plain\" or \"html\".");
}
this.textMode = textMode;
if (typeof this.rawText === 'string') {
this.setText(this.rawText);
}
return this;
};
/**
* Creates the label's HTML.
* @return {HTMLElement}
*/
TextLabel.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
TextLabel.superclass.prototype.createHTML.call(this);
this.setText(this.rawText);
return this.html;
};
PMUI.extendNamespace('PMUI.ui.TextLabel', TextLabel);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = TextLabel;
}
}());
(function () {
/**
* @class PMUI.ui.FlashMessage
* A message to display for a while.
* @extends {PMUI.core.Element}
*
* Usage example:
*
* @example
* var message = new PMUI.ui.FlashMessage({
* message: "Hi!, this is a flash message!",
* duration: 5000,
* severity: 'info'
* });
*
* message.show();
*
* @constructor
* Creates a new instance of the class.
* @param {Object} [settings] The config options.
*
* @cfg {String|Array} [message=""] The object's message. It can be a single string or an array of strings. In the
* llatter case the mesage will be showed in a list format.
* @cfg {Number} [duration=3000] The time in milliseconds the message will be displayed.
* @cfg {HTMLElement|PMUI.ui.Element} [appendTo=document.body] The html element/PMUI's object the flash message
* will be displayed.
* @cfg {String} [display="absolute"] The display mode for the object's html.
* @cfg {String} [position="absolute"] The position mode for the object's html.
* @cfg {String} [severity="info"] The severity for the message. Valid values: 'info', 'success', 'error'.
*/
var FlashMessage = function (settings) {
FlashMessage.superclass.call(this, settings);
/**
* The object's message. Set by the {@link #cfg-message message} config option and the
* {@link #method-setMessage setMessage()} method.
* @type {String|Array}
* @readonly
*/
this.message = null;
/**
* The duration in milliseconds to show the message. Set by the {@link #cfg-duration duration} config option
* and the {@link #method-setDuration setDuration()} method.
* @type {Number}
* @readonly
*/
this.duration = null;
/**
* The html element/PMUI's object the message will be displayed in. Set by the {@link #cfg-appendTo appendTo}
* config option and the {@link #method-setAppendTo setAppendTo()} method.
* @type {HTMLElement|PMUI.core.Element}
*/
this.appendTo = null;
/**
* The message's severity. Set by the {@link #cfg-severity severity} config option and the
* {@link #method-setSeverity setSeverity()} method.
* @type {String}
* @readonly
*/
this.severity = null;
FlashMessage.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', FlashMessage);
/**
* The object's type.
* @type {String}
*/
FlashMessage.prototype.type = 'FlashMessage';
/**
* The initializer method.
* @param {Object} [settings] The config options
* @private
*/
FlashMessage.prototype.init = function (settings) {
var defaults = {
message: "",
duration: 3000,
appendTo: document.body,
display: 'inline-block',
positionMode: 'fixed',
severity: 'info'
};
jQuery.extend(true, defaults, settings);
this.setMessage(defaults.message)
.setDuration(defaults.duration)
.setAppendTo(defaults.appendTo)
.setPositionMode(defaults.positionMode)
.setDisplay(defaults.display)
.setSeverity(defaults.severity);
};
/**
* Sets the severity for the message.
* @param {String} severity Valid values: "info", "success" or "error".
* @chainable
*/
FlashMessage.prototype.setSeverity = function (severity) {
if (!(severity === 'success' || severity === 'error' || severity === 'info')) {
throw new Error('setSeverity(): the parameter must be "success" or "error" or "info"');
}
this.severity = severity;
this.style.removeClasses(['pmui-info', 'pmui-error', 'pmui-success']).addClasses(['pmui-' + severity]);
return this;
};
/**
* Sets the html element/PMUI's object in which the message will be displayed.
* @param {HTMLElement|PMUI.core.Element} appendTo
* @chainable
*/
FlashMessage.prototype.setAppendTo = function (appendTo) {
if (!(PMUI.isHTMLElement(appendTo) || appendTo instanceof PMUI.core.Element)) {
throw new Error("setAppendTo(): The parameter must be a HTML element or an instance of PMUI.ui.Element.");
}
this.appendTo = appendTo;
return this;
};
/**
* Sets the duration for display the message.
* @param {Number} duration The duration in milliseconds.
* @chainable
*/
FlashMessage.prototype.setDuration = function (duration) {
if (typeof duration !== 'number') {
throw new Error('setDuration(): The parameter must be a number.');
}
this.duration = duration;
return this;
};
/**
* Sets the message for the object.
* @param {String|Array} message It can be a string or an array of strings, in the latter case the message will be
* showed in a list format.
* @chainable
*/
FlashMessage.prototype.setMessage = function (message) {
var ul,
li,
i;
if (typeof message !== 'string' && !jQuery.isArray(message)) {
throw new Error('setMessage(): The parameter must be a message.');
}
this.message = (typeof message === 'string') ? jQuery.trim(message) : message;
if (this.html) {
jQuery(this.html).empty();
if (typeof message === 'string') {
this.html.textContent = message;
} else {
ul = PMUI.createHTMLElement('ul');
ul.className = 'pmui-flashmessage-list';
ul.style.listStyleType = 'none';
for (i = 0; i < message.length; i += 1) {
li = PMUI.createHTMLElement('li');
li.textContent = message[i];
ul.appendChild(li);
}
this.html.appendChild(ul);
}
}
return this;
};
/**
* Shows the message in the html element/PMUI's object specified by the {@link #cfg-appendTo appendTo} config
* option or the {@link #method-setAppendTo setAppendTo()} method.
* @chainable
*/
FlashMessage.prototype.show = function () {
var targetHTML = this.appendTo,
html = this.html,
top = 50,
w,
pw;
if (!PMUI.isHTMLElement(targetHTML)) {
targetHTML = targetHTML.html;
}
if (targetHTML) {
if (!html) {
html = this.getHTML();
}
jQuery(html).fadeTo(1, 0).get(0).style.top = top + "px";
document.body.appendChild(html);
w = jQuery(html).outerWidth();
targetHTML.appendChild(html);
this.style.addProperties({
left: '50%',
'margin-left': w / -2
});
jQuery(html).finish().css({
'top': '50px'
}).fadeTo(1, 0).animate({
top: "-=" + top,
opacity: 1
}, 400, 'swing').delay(this.duration).animate({
top: "+=" + top,
opacity: 0,
zIndex: '0'
});
}
return this;
};
/**
* @inheritDoc
*/
FlashMessage.prototype.createHTML = function () {
FlashMessage.superclass.prototype.createHTML.call(this);
this.setMessage(this.message);
return this.html;
};
PMUI.extendNamespace('PMUI.ui.FlashMessage', FlashMessage);
if (typeof exports !== 'undefined') {
module.exports = FlashMessage;
}
}());
(function () {
var Menu = function (settings) {
settings = jQuery.extend(true, settings, {
elementTag: 'ul',
positionMode: 'absolute'
});
Menu.superclass.call(this, settings);
this.targetElement = null;
this.parent = null;
this.onShow = null;
this.onHide = null;
this.displayed = null;
this.factory = new PMUI.menu.MenuItemFactory();
this.items = new PMUI.util.ArrayList();
this.onOutsideClickHandler = null;
Menu.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', Menu);
Menu.prototype.type = 'Menu';
Menu.prototype.init = function (settings) {
var defaults = {
targetElement: null,
parent: null,
onShow: null,
onHide: null,
items: []
};
jQuery.extend(true, defaults, settings);
this.onShow = defaults.onShow;
this.onHide = defaults.onHide;
this.setParent(defaults.parent)
.setItems(defaults.items)
.setTargetElement(defaults.targetElement)
.setOutsideClickHandler();
};
Menu.prototype.setOutsideClickHandler = function () {
var that = this;
this.onOutsideClickHandler = function (e) {
if (!jQuery(e.currentTarget).parents('#' + that.id).length) {
that.hide();
}
};
return this;
};
Menu.prototype.setTargetElement = function (target) {
var rootMenu,
targetElement = (this.targetElement && this.targetElement.html) || document.body;
$(targetElement).removeClass('pmui-contextmenu-target');
if (target instanceof PMUI.core.Element || null) {
rootMenu = this.getRootMenu();
if (rootMenu.targetElement) {
if (rootMenu.targetElement.menu === rootMenu) {
rootMenu.targetElement.menu = null;
}
}
rootMenu.targetElement = target;
}
return this;
};
Menu.prototype.getTargetElement = function () {
var rootMenu = this.getRootMenu();
return rootMenu.targetElement;
};
Menu.prototype.addItem = function (item) {
var newItem = this.factory.make(item);
if (newItem) {
newItem.setParent(this);
this.items.insert(newItem);
if (this.html) {
this.html.appendChild(newItem.getHTML());
}
}
return this;
};
Menu.prototype.removeItem = function (item) {
var itemToRemove;
if (typeof item === 'string') {
itemToRemove = this.items.find('id', item);
} else if (typeof item === 'number') {
itemToRemove = this.items.get(item);
} else if (item instanceof PMUI.item.MenuItem && this.items.contains(item)) {
itemToRemove = item;
}
if (itemToRemove) {
this.items.remove(itemToRemove);
jQuery(itemToRemove.html).detach();
}
return this;
};
Menu.prototype.clearItems = function () {
var index = 0;
while (this.items.getSize()) {
this.removeItem(index);
}
return this;
};
Menu.prototype.setItems = function (items) {
var i;
if (!jQuery.isArray(items)) {
throw new Error('setItems(): The parameter must be an array.');
}
this.clearItems();
for (i = 0; i < items.length; i += 1) {
this.addItem(items[i]);
}
return this;
};
Menu.prototype.getItems = function () {
var index = 0;
return this.items.asArray().slice(index);
};
Menu.prototype.setParent = function (parent) {
if (!(parent === null || parent instanceof PMUI.menu.MenuItem)) {
throw new Error('setParent(): The parameter must be an instance of PMUI.item.MenuItem or null.');
}
this.parent = parent;
return this;
};
Menu.prototype.defineEventListeners = function () {
this.removeEvents();
this.addEvent('contextmenu').listen(this.html, function (e) {
e.stopPropagation();
e.preventDefault();
});
this.addEvent('click').listen(this.html, function (e) {
e.stopPropagation();
});
this.addEvent('mousedown').listen(this.html, function (e) {
e.stopPropagation();
});
return this;
};
Menu.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
Menu.superclass.prototype.createHTML.call(this);
this.setItems(this.items.asArray().slice(0));
this.defineEventListeners();
return this.html;
};
Menu.prototype.isRoot = function () {
return !this.parent;
};
Menu.prototype.getRootMenu = function () {
if (this.isRoot()) {
return this;
} else {
return this.parent.parent.getRootMenu();
}
};
Menu.prototype.show = function (x, y) {
var rootMenu = this.getRootMenu(),
targetElement = (this.targetElement && this.targetElement.html) || document.body,
zIndex = parseInt(targetElement.style.zIndex);
x = x || 0;
y = y || 0;
rootMenu.setPosition({
x: x,
y: y
});
PMUI.removeCurrentMenu();
rootMenu.setZOrder(zIndex + 1);
$(targetElement).addClass('pmui-contextmenu-target');
document.body.appendChild(rootMenu.getHTML());
this.addEvent('mousedown', 'clickOutside').listen(document, this.onOutsideClickHandler);
this.displayed = true;
PMUI.currentContextMenu = this;
if (typeof this.onShow === 'function') {
this.onShow(this);
}
if (document.documentElement.clientHeight - y < jQuery(this.html).outerHeight()) {
this.html.style.top = y - jQuery(this.html).outerHeight() + 'px'
}
return this;
};
Menu.prototype.hide = function () {
var rootMenu = this.getRootMenu(),
targetElement = (this.targetElement && this.targetElement.html) || document.body;
this.removeEvent('clickOutside');
$(targetElement).removeClass('pmui-contextmenu-target');
jQuery(rootMenu.html).detach();
rootMenu.displayed = false;
PMUI.currentContextMenu = null;
if (typeof rootMenu.onHide === 'function') {
rootMenu.onHide(rootMenu);
}
return this;
};
Menu.prototype.setContextMenu = function () {
return this;
};
PMUI.extendNamespace('PMUI.menu.Menu', Menu);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Menu;
}
}());
(function () {
var MenuItem = function (settings) {
MenuItem.superclass.call(this, settings);
this.dom = {};
this.parent = null;
MenuItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', MenuItem);
MenuItem.prototype.type = 'MenuItem';
MenuItem.prototype.init = function (settings) {
var defaults = {
parent: null
}
jQuery.extend(true, defaults, settings);
this.setParent(defaults.parent);
};
MenuItem.prototype.setParent = function (parent) {
if (!(parent === null || parent instanceof PMUI.menu.Menu)) {
throw new Error('setParent(): The parameter must be an instance of PMUI.ui.Menu or null.');
}
this.parent = parent;
return this;
};
MenuItem.prototype.getParent = function () {
return this.parent;
};
MenuItem.prototype.getRootMenu = function () {
var parent = this.parent;
if (this.parent) {
return this.parent.getRootMenu();
}
return parent;
};
MenuItem.prototype.isLeaf = function () {
throw new Error("isLeaf() is being called from an abstract class.");
};
MenuItem.prototype.getMenu = function () {
return this.parent;
};
MenuItem.prototype.setContextMenu = function () {
return this;
};
MenuItem.prototype.getMenuTargetElement = function () {
var rootMenu = this.getRootMenu();
if (rootMenu) {
return rootMenu.getTargetElement();
}
return rootMenu
};
MenuItem.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
MenuItem.superclass.prototype.createHTML.call(this);
return this.html;
};
PMUI.extendNamespace('PMUI.menu.MenuItem', MenuItem);
}());
(function () {
var MenuOption = function (settings) {
MenuOption.superclass.call(this, settings);
this.onClick = null;
this.text = null;
this.hideOnClick = null;
this.disabled = null;
MenuOption.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.menu.MenuItem', MenuOption);
MenuOption.prototype.type = 'MenuOption';
MenuOption.prototype.init = function (settings) {
var defaults = {
onClick: null,
elementTag: 'li',
text: '[option]',
hideOnClick: true,
disabled: false
};
jQuery.extend(true, defaults, settings);
this.setElementTag(defaults.elementTag)
.setText(defaults.text)
.setOnClickHandler(defaults.onClick)
.hideOnClick = !!defaults.hideOnClick;
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
};
MenuOption.prototype.enable = function () {
this.disabled = false;
this.style.removeClasses(['pmui-disabled']);
return this;
};
MenuOption.prototype.disable = function () {
this.disabled = true;
this.style.addClasses(['pmui-disabled']);
return this;
};
MenuOption.prototype.setText = function (text) {
if (typeof text !== 'string') {
throw new Error('setText(): the parameter must be a srting.');
}
this.text = text;
if (this.dom.textContainer) {
this.dom.textContainer.textContent = text;
}
return this;
};
MenuOption.prototype.setOnClickHandler = function (onClick) {
if (!(onClick === null || typeof onClick === 'function')) {
throw new Error('setOnClickHandler(): The parameter must be a function or null.');
}
this.onClick = onClick;
return this;
};
MenuOption.prototype.onClickHandler = function () {
var that = this;
return function (e) {
e.preventDefault();
e.stopPropagation();
if (!that.disabled) {
if (typeof that.onClick === 'function') {
that.onClick(that);
}
if (that.hideOnClick) {
that.parent.hide();
}
}
};
};
MenuOption.prototype.remove = function () {
this.parent.removeItem(this);
return this;
};
MenuOption.prototype.defineEventListeners = function () {
this.removeEvents();
this.addEvent('click').listen(this.dom.title, this.onClickHandler());
return this;
};
MenuOption.prototype.createHTML = function () {
var link,
textContainer,
iconContainer;
if (this.html) {
return this.html;
}
MenuOption.superclass.prototype.createHTML.call(this);
link = PMUI.createHTMLElement('a');
link.href = '#';
link.className = 'pmui-menuoption-title';
textContainer = PMUI.createHTMLElement('span');
textContainer.className = 'pmui-menuoption-text';
iconContainer = PMUI.createHTMLElement('i');
iconContainer.className = 'pmui-menuoption-text-icon';
this.dom.title = link;
this.dom.textContainer = textContainer;
this.dom.iconContainer = iconContainer;
link.appendChild(iconContainer);
link.appendChild(textContainer);
this.html.appendChild(link);
this.setText(this.text);
this.defineEventListeners();
return this.html;
};
PMUI.extendNamespace('PMUI.menu.MenuOption', MenuOption);
}());
(function () {
var MenuRegularOption = function (settings) {
MenuRegularOption.superclass.call(this, settings);
this.childMenu = null;
MenuRegularOption.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.menu.MenuOption', MenuRegularOption);
MenuRegularOption.prototype.init = function (settings) {
var defaults = {
items: []
};
jQuery.extend(true, defaults, settings);
this.childMenu = new PMUI.menu.Menu({
positionMode: 'absolute',
parent: this
});
this.setItems(defaults.items);
};
MenuRegularOption.prototype.setItems = function (items) {
this.childMenu.setItems(items);
if (this.childMenu.getItems().length) {
this.style.addClasses(['pmui-father']);
}
return this;
};
MenuRegularOption.prototype.getItems = function () {
return this.childMenu.getItems();
};
MenuRegularOption.prototype.onClickHandler = function () {
var that = this;
return function (e) {
e.preventDefault();
e.stopPropagation();
if (!that.disabled) {
if (typeof that.onClick === 'function') {
that.onClick(that);
}
if (that.hideOnClick && that.childMenu.getItems().length === 0) {
that.parent.hide();
}
}
};
};
MenuRegularOption.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
MenuRegularOption.superclass.prototype.createHTML.call(this);
this.html.appendChild(this.childMenu.getHTML());
this.setItems(this.getItems());
return this.html;
};
PMUI.extendNamespace('PMUI.menu.MenuRegularOption', MenuRegularOption);
}());
(function () {
var MenuSeparatorItem = function (settings) {
MenuSeparatorItem.superclass.call(this, jQuery.extend(true, settings, {
elementTag: 'div'
}));
MenuSeparatorItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.menu.MenuItem', MenuSeparatorItem);
MenuSeparatorItem.prototype.type = 'MenuSeparatorItem';
MenuSeparatorItem.prototype.isLeaf = function () {
return true;
};
PMUI.extendNamespace('PMUI.menu.MenuSeparatorItem', MenuSeparatorItem);
}());
(function () {
var MenuItemFactory = function (settings) {
MenuItemFactory.superclass.call(this, settings);
MenuItemFactory.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.util.Factory', MenuItemFactory);
MenuItemFactory.prototype.type = "MenuItemFactory";
MenuItemFactory.prototype.init = function (settings) {
var defaults = {
products: {
"menuRegularOption": PMUI.menu.MenuRegularOption,
"menuSeparatorItem": PMUI.menu.MenuSeparatorItem
},
defaultProduct: "menuRegularOption"
};
this.setDefaultProduct(defaults.defaultProduct)
.setProducts(defaults.products);
};
PMUI.extendNamespace('PMUI.menu.MenuItemFactory', MenuItemFactory);
}());
(function () {
/**
* @class PMUI.form.Field
* Abstract class that encapsulates the field behavior
* @extends PMUI.core.Element
* @abstract
*
* @constructor
* While it is true that this class must not be instantiated,
* it is useful to mention the settings parameter for the constructor function
* (which will be used for the non abstract subclasses).
* @param {Object} [settings] A JSON object, it can be contain the following fields:
*
* - {@link PMUI.form.Field#cfg-name name}.
* - {@link PMUI.form.Field#cfg-label label}.
* - {@link PMUI.form.Field#cfg-value value}.
* - {@link PMUI.form.Field#cfg-helper helper}.
* - {@link PMUI.form.Field#cfg-showHelper showHelper}.
* - {@link PMUI.form.Field#cfg-validators validators}.
* - {@link PMUI.form.Field#cfg-valueType valueType}.
* - {@link PMUI.form.Field#cfg-controlPositioning controlPositioning}.
* - {@link PMUI.form.Field#cfg-labelWidth labelWidth}.
* - {@link PMUI.form.Field#cfg-showColon showColon}
*
* @cfg {String} name The name for the field.
* @cfg {String} label The text to be shown as the field's label
* @cfg {String} value The initial value to be set to the control.
* @cfg {String} helper The helper text to be shown in the helper tooltip
* @cfg {Boolean} showHelper A boolean value which determines if the helper tootltip will be shown or not.
* @cfg {Object} validators An array where each array's item is a JSON object (with the validator setting data)
* or a {@link PMUI.form.Validator Validator} object.
* {
* validators: [
* {
* type: "maxlength",
* criteria: 5
* },
* new LengthValidator({
* min: 0,
* max: 5
* })
* ]
* }
* In example above, "validators" is an array in which their first element is an JSON object
* and the second one is a {@link PMUI.form.Validator Validator} object.
* @cfg {String} valueType A string which specifies the data type for the Field value.
* @cfg {String} controlPositioning A formatted string that specifies the order output for the control(s).
* A string which specifies the output order for the field's controls.
* Basically this string uses a wildcard with the format "[cx]", each one is replaced by the control:
* "[c0]-[c1]-[c2]"
* If you apply the string above to the controlPositioning property, it will render
* the first three field's controls, each one separated from the other by a "-".
*
* Another wildcard is [c*], this represents all the controls or the ones that haven't been included yet:
* "[c*]-"
* The example above will render all the controls and at the end it will add a "-".
*
* "[c2]-[c*]"
* The example above will render first the second control and then the other ones (starting from the first one).
* @cfg {String} labelWidth The width for the label.
* The width label should be a String with the following format "X%"
*
* Note that this% should not exceed 80%, because 20% is the tooltip
* @cfg {Boolean} showColon If a colon is shown after the label text.
* @cfg {Boolean} [labelVisible=true] If the label is visible or not.
* @cfg {String} [labelPosition="left"] Sets the position for the label, the value can take one of the following
* options:
*
* - "left" (default).
* - "right".
* - "top".
* - "bottom"
*
* @cfg {String|Number|Array} [controlsWidth] Determines the width for the field's controls, but the width of the
* field must be a number or a string with the format "#px", otherwise the width for the controls will be set to
* "auto".It can be:
*
* - A String, in this case the string must have the following format "##px", otherwise must be "auto".
* - A Number, in this case the value received is parsed into a px units value.
* - An Array in which each element can be a String or a Number (following the rules above).
*
* This will be applied respectively to each control in the field.
* @cfg {Function} [onClick=null] Description here.
* @cfg {Function|null} [onBeforeChange] The callback function to be executed before the field's value changes. To
* avoid the change the callback function must return false. For info about the parameters sent to the calback
* function please read the {@link #event-onBeforeChange onBeforeChange event} documentation.
*/
var Field = function (settings) {
Field.superclass.call(this, settings);
/**
* @property {String} [name=[The object's id]] The field's name.
* @readonly
*/
this.name = null;
/**
* @property {String} [label = "[field]"] The field's label text.
* @readonly
*/
this.label = null;
/**
* @property {String} [value=""] The field's value.
* @readonly
*/
this.value = null;
/**
* @property {PMUI.ui.TooltipMessage} [helper] The field's help tooltip
* @readonly
*/
this.helper = null;
/**
* @property {PMUI.ui.TooltipMessage} [message] A {@link PMUI.ui.TooltipMessage TooltipMessage}
* object to show a message related to the field (i.e. validation error messages)
* @private
*/
this.message = null;
/**
* @property {Array} controls An array, it will contain all the necessary Control objects
* @private
*/
this.controls = [];
/**
* @property {Object} validators An JSON object which will contain all the
* {@link PMUI.form.Validator Validators} object.
* @private
*/
this.validators = {};
/**
* @property {String} [controlPositioning="[c*]"] A formatted string that specifies
* the order output for the field's controls.
* @readonly
*/
this.controlPositioning = null;
/**
* @property {Object} [dom] A JSON object which will contain important DOM object
* for the Field object.
* @private
*/
this.dom = null;
/**
* @property {Boolean} [helperIsVisible=false] A Boolean that let us
* know if the help tooltip will be shown or not.
* @readonly
*/
this.helperIsVisible = null;
/**
* @property {String} [labelWidth="30%"]
* The width for the label. This property should be a String with the following format "X%"
* @readonly
*/
this.labelWidth = null;
/**
* @property {Boolean} [visibleColon=true]
* If a colon is shown after the label text.
* @readonly
*/
this.visibleColon = null;
/**
* @property {Boolean} labelVisible
* If the label is visible or not.
* @readonly
*/
this.labelVisible = null;
/**
* @property {String} [labelPosition="String"] The position for the field's label.
* @readonly
*/
this.labelPosition = null;
/**
* @event onBeforeChange
* Fired before the field's value changes.
* @param {String} newValue The new value to be set.
* @param {String} oldValue The old field's value.
*/
this.onBeforeChange = null;
/**
* @event onChange
* Fired when the field's value changes.
* @param {String} newValue The field's new value.
* @param {String} oldValue The previous field's value.
*/
this.onChange = null;
/**
* @property {String} valueType The value data type for the field.
*/
this.valueType = null;
/**
* @property {PMUI.form.ValidatorFactory} validatorFactory The factory object for validator production.
*/
this.validatorFactory = null;
/**
* @property {Boolean} [required] If the field is required or not
*/
this.required = null;
/**
* @property {String} [requiredMessage="This field is required."]
* The message to display when the validation for required property fails.
*/
this.requiredMessage = null;
/**
* @property {Boolean} [validAtChange=true]
* If the validation must be executed every time the field's value changes.
*/
this.validAtChange = null;
/**
* The width for the control(s)
* @type {Number|String|Array}
* @readonly
*/
this.controlsWidth = [];
/**
* @property {Number|String} controlsContainerWidth
* @type {Number|String}
*/
this.controlsContainerWidth = null;
/**
* @property {PMUI.form.Form} form
* The form the field belongs to.
*/
this.form = null;
/**
* @property {Boolean} eventsDefined
* If the events for the object have been defined.
*/
this.eventsDefined = null;
/**
* @property {PMUI.data.DataSet} data
* The field's data object.
* @private
*/
this.data = null;
/**
* The initial value the field will be set when its reset() method is called.
* @type {String}
* @private
*/
this.initialValue = null;
/**
* [onClick description]
* @type {[type]}
*/
this.onClick = null;
/**
* If the field is disabled or not. Notice that if it is disabled the validation won't be executed.
* @type {Boolean}
*/
this.disabled = null;
/**
* @protected
* Valid Types
* @type {Array}
*/
this.dependentFields = [];
this.dependencyHandler = null;
this.validTypes = {
'string': true,
'number': true,
'boolean': true,
'date': true,
'object': true,
'integer': true,
'float': true
};
Field.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', Field);
Field.prototype.type = "Field";
Field.prototype.init = function (settings) {
var defaults = {
name: this.id,
label: '[field]',
value: '',
helper: '',
showHelper: !!(settings && settings.helper),
validators: [],
valueType: 'string',
controlPositioning: '[c*]',
labelWidth: '23.5%',
width: '100%',
showColon: true,
validatorFactory: new PMUI.form.ValidatorFactory(),
onBeforeChange: null,
onChange: null,
required: false,
validAtChange: true,
requiredMessage: 'This field is required.'.translate(),
labelVisible: true,
labelPosition: 'left',
form: null,
controlsWidth: "auto",
controlsContainerWidth: "70%",
disabled: false,
dependentFields: [],
dependencyHandler: function () {
},
onClick: null
};
this.dependentFields = [];
this.dependencyFields = new PMUI.util.ArrayList();
$.extend(true, defaults, settings);
this.initialValue = defaults.value;
this.helper = new PMUI.ui.TooltipMessage({
category: 'help'
});
this.dom = {};
this.data = new PMUI.data.DataField();
this.message = new PMUI.ui.TextLabel({
text: 'This field is required.'.translate(),
displayMode: 'block',
mode: 'normal',
visible: false
});
this.setForm(defaults.form)
.setValidAtChange(defaults.validAtChange)
.setRequired(defaults.required)
.setRequiredMessage(defaults.requiredMessage)
.setValidatorFactory(defaults.validatorFactory)
.setValidators(defaults.validators)
.setName(defaults.name)
.setLabel(defaults.label)
.setValue(defaults.value)
.setHelper(defaults.helper)
.setValueType(defaults.valueType)
.setControlPositioning(defaults.controlPositioning)
.setOnBeforeChangeHandler(defaults.onBeforeChange)
.setOnChangeHandler(defaults.onChange)
.setWidth(defaults.width)
.setLabelWidth(defaults.labelWidth)
.setLabelPosition(defaults.labelPosition)
.setControlsWidth(defaults.controlsWidth)
.setControlsContainerWidth(defaults.controlsContainerWidth)
.setOnClickHandler(defaults.onClick)
.setControls();
this.setDependencyHandler(defaults.dependencyHandler);
this.setDependentFields(defaults.dependentFields);
if (defaults.showHelper) {
this.showHelper();
} else {
this.hideHelper();
}
if (defaults.showColon) {
this.showColon();
} else {
this.hideColon();
}
if (defaults.labelVisible) {
this.showLabel();
} else {
this.hideLabel();
}
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
};
Field.prototype.onBeforeChangeHandler = function (handler) {
var that = this;
return function (newValue, oldValue) {
var callbackResponse,
futureValue = "",
i;
if (typeof that.onBeforeChange === 'function') {
for (i = 0; i < that.controls.length; i += 1) {
if (that.controls[i] === this) {
futureValue += ' ' + newValue;
} else {
futureValue += ' ' + that.controls[i].getValue();
}
}
futureValue = futureValue.substr(1);
callbackResponse = that.onBeforeChange(futureValue, that.getValue());
}
return callbackResponse;
};
};
Field.prototype.setOnBeforeChangeHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnBeforeChangeHandler(): The parameter must be a function or null.");
}
this.onBeforeChange = handler;
return this;
};
Field.prototype.setOnClickHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnClickHandler(): The parameter must be a function or null.");
}
this.onClick = handler;
return this;
};
Field.prototype.setDependentFields = function (dependentFields) {
if (!jQuery.isArray(dependentFields)) {
throw new Error("setDependentFields(): The parameter must be an array.");
}
this.dependentFields = dependentFields;
if (this.form) {
this.form.updateDependencies();
}
return this;
};
Field.prototype.removeDependentField = function (FieldName) {
var i;
for (i = 0; this.dependentFields.length; i += 1) {
if (this.dependentFields[i] === FieldName) {
this.dependentFields.splice(i, 1);
i--;
}
}
return this;
};
Field.prototype.setDependencyHandler = function (handler) {
if (typeof handler == 'function' || handler == null) {
this.dependencyHandler = handler;
}
return this;
};
/**
* Disables the field. Notice that when a field is disabled it is not validated and it is not returned when its
* form's getData() method is invoked.
* @chainable
*/
Field.prototype.disable = function () {
var i;
this.disabled = true;
this.style.addClasses(["pmui-disabled"]);
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].disable(true);
}
return this;
};
/**
* Enables the field.
* @chainable
*/
Field.prototype.enable = function () {
var i;
this.disabled = false;
this.style.removeClasses(["pmui-disabled"]);
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].disable(false);
}
return this;
};
/**
* Resets the field to its original value.
* @chainable
*/
Field.prototype.reset = function () {
this.setValue(this.initialValue);
this.hideMessage();
if (this.eventsDefined) {
this.onChangeHandler();
}
return this;
};
/**
* Determines the width for the field's controls, but the width of the field must be a number or a string with
* the format "#px", otherwise the width for the controls will be set to "auto".
* @param {Number|String|Array} width It can be:
*
* - A String, in this case the string must have the following format "##px", otherwise must be "auto".
* - A Number, in this case the value received is parsed into a px units value.
* - An Array in which each element can be a String or a Number (following the rules above).
*
* This will be applied respectively to each control in the field.
* @chainable
*/
Field.prototype.setControlsWidth = function (width) {
var i,
labelWidthPx,
auxWidth,
autoGerateWidthForControls = false,
fieldWidthIsAbsolute = false;
if (!(width === 'auto' || typeof width === 'number' || jQuery.isArray(width) || /^\d+(\.\d+)?px/.test(width))) {
throw new Error("setControlsWidth(): The parameter must be a string \"auto\", or a number or an array.");
}
this.controlsWidth = width;
if (width === 'auto') {
autoGerateWidthForControls = true;
}
if (typeof this.width === 'number' || /^\d+(\.\d+)?px/.test(this.width)) {
fieldWidthIsAbsolute = true;
}
if (!autoGerateWidthForControls) {
for (i = 0; i < this.controls.length; i += 1) {
if (jQuery.isArray(width)) {
this.controls[i].setWidth(width[i] || "auto");
} else {
this.controls[i].setWidth(width);
}
}
} else if (fieldWidthIsAbsolute) {
auxWidth = parseInt(this.width, 10);
labelWidthPx = auxWidth * (parseInt(this.labelWidth, 10) / 100) + 4;
labelWidthPx = auxWidth - 83 - labelWidthPx;
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].setWidth(labelWidthPx);
}
} else {
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].setWidth("auto");
}
}
return this;
};
/**
* Sets the width for the controls container.
* @param width
*/
Field.prototype.setControlsContainerWidth = function (width) {
if (!(typeof width === 'number' || jQuery.isArray(width) || /^\d+(\.\d+)?(px|\%)/.test(width))) {
throw new Error("setControlsContainerWidth(): The parameter must be a string (with a numeric or percentage value), or a number or an array.");
}
this.controlsContainerWidth = width;
if (this.dom && this.dom.controlContainer) {
$(this.dom.controlContainer).css('width', width);
}
return this;
};
/**
* Sets the form the field belongs to.
* @param {PMUI.form.Form} form
* @chainable
*/
Field.prototype.setForm = function (form) {
if (form instanceof PMUI.form.Form) {
this.form = form;
}
return this;
};
/**
* Turns on/off the validation when the field's value changes.
* @param {Boolean} validAtChange
* @chainable
*/
Field.prototype.setValidAtChange = function (validAtChange) {
this.validAtChange = !!validAtChange;
return this;
};
/**
* Sets the position for the label.
* @param {String} position It can take one of the following values: "top", "right", "bottom", "left".
* @chainable
*/
Field.prototype.setLabelPosition = function (position) {
if (position === 'top' || position === 'right' || position === 'bottom' || position === 'left') {
this.labelPosition = position;
if (this.html) {
if (position === 'top' || position === 'left') {
this.html.insertBefore(this.dom.labelTextContainer, this.dom.controlContainer);
this.dom.labelTextContainer.style.display = position === 'top' ? 'block' : 'inline-block';
$(this.dom.controlContainer).css("float", '');
} else {
this.html.insertBefore(this.dom.controlContainer, this.dom.labelTextContainer);
this.dom.labelTextContainer.style.display = position === 'bottom' ? 'block' : 'inline-block';
$(this.dom.labelTextContainer).css("float", '');
}
}
} else {
throw new Error('setLabelPosition(): it only accepts "top", "rigth", "left" or "bottom" as value for ' +
'the parameter');
}
return this;
};
/**
* Sets the factory which must be produce {@link PMUI.form.Validator Validator} objects.
* @param {PMUI.util.Factory|Object} factory
* It can be a:
*
* - a {@link PMUI.util.Factory} object
* - a JSON object: in this case a new {@link PMUI.form.ValidatorFactory ValidatorFactory} will be created
* using the JSON object as the constructor parameter.
* @private
* @chainable
*/
Field.prototype.setValidatorFactory = function (factory) {
if (factory instanceof PMUI.util.Factory) {
this.validatorFactory = factory;
} else {
this.validatorFactory = new PMUI.form.ValidatorFactory(factory);
}
return this;
};
/**
* Sets the message to show when the required field validation fails.
* @param {String} requiredMessage
* @chainable
*/
Field.prototype.setRequiredMessage = function (requiredMessage) {
if (typeof requiredMessage === 'string') {
this.requiredMessage = requiredMessage;
} else {
throw new Error("the setRequiredMessage() method only accepts string values.");
}
return this;
};
/**
* Sets if the fields is required or not.
* @param {Boolean} required
* @chainable
*/
Field.prototype.setRequired = function (required) {
this.required = !!required;
if (this.dom.fieldRequired) {
if (this.required) {
this.showRequired();
} else {
this.hideRequired();
}
}
return this;
};
Field.prototype.hideRequired = function () {
this.dom.fieldRequired.style.display = 'none';
return this;
};
Field.prototype.showRequired = function () {
this.dom.fieldRequired.style.display = 'inline-block';
return this;
};
/**
* Removes all the validators from the field.
* @chainable
*/
Field.prototype.clearValidators = function () {
var key;
for (key in this.validators) {
if (this.validators.hasOwnProperty(key)) {
this.validators[key] = null;
delete this.validators[key];
}
}
return this;
};
/**
* Adds a validator to the field.
* @param {String|PMUI.form.Validator|Object} validator
* It can be:
*
* - a String: it must be on of the supported pmTypes by the
* {@link PMUI.form.ValidatorFactory ValidatorFactory} class.
* - a {@link PMUI.form.Validator Validator} object.
* - a JSON object: in this case the JSON structure must be the same as the needed one to create
* the desired validator, additionally must have the respective pmType.
*
* Note. All the supported pmTypes are specified in the
* {@link PMUI.form.ValidatorFactory ValidatorFactory documentation}.
* @chainable
*/
Field.prototype.addValidator = function (validator) {
var newValidator;
if (this.validatorFactory) {
if (this.validatorFactory.isValidClass(validator) || this.validatorFactory.isValidName(validator.pmType)) {
newValidator = this.validatorFactory.make(validator);
} else {
throw new Error('Invalid validator to add.');
}
}
if (newValidator && newValidator instanceof PMUI.form.Validator) {
newValidator.setParent(this);
this.validators[newValidator.type] = newValidator;
}
return this;
};
/**
* Sets the validators for the field.
* @param {Array} validators An array in which each element can be one of the accepted types in the
* {@link PMUI.form.Field#addValidator addValidator() method}.
* @chainable
*/
Field.prototype.setValidators = function (validators) {
var i = 0;
if (jQuery.isArray(validators)) {
this.clearValidators();
for (i = 0; i < validators.length; i += 1) {
this.addValidator(validators[i]);
}
}
return this;
};
/**
* Shows a colon after the label text.
* @chainable
*/
Field.prototype.showColon = function () {
this.visibleColon = true;
if (this.dom.fieldColon) {
this.dom.fieldColon.style.display = '';
}
};
/**
* Hides the colon after the label text.
* @chainable
*/
Field.prototype.hideColon = function () {
this.visibleColon = false;
if (this.dom.fieldColon) {
this.dom.fieldColon.style.display = "none";
}
return this;
}
/**
* Sets the width for the label.
* @param {String} width It can be a Number or a String.
* This parameter should be a String with the following format "X%";
* @chainable
*/
Field.prototype.setLabelWidth = function (width) {
if (/^\d+(\.\d+)?(px|em|%)$/.test(width)) {
this.labelWidth = width;
this.setControlsWidth(this.controlsWidth);
} else {
throw new Error('setLabelWidth(): invalid "width" parameter');
}
if (this.dom.labelTextContainer) {
this.dom.labelTextContainer.style.width = this.labelWidth;
if (this.labelVisible && this.labelPosition != "top")
$(this.dom.messageContainer).css({"margin-left": parseInt(this.labelWidth) + 1 + "%"});
}
return this;
};
/**
* Sets the callback function to be called when the field's value changes.
* @param {Function} handler
* @chainable
*/
Field.prototype.setOnChangeHandler = function (handler) {
if (typeof handler === 'function') {
this.onChange = handler;
}
return this;
};
/**
* Sets the helper tooltip visible.
* @chainable
*/
Field.prototype.showHelper = function () {
this.helperIsVisible = true;
this.helper.setVisible(true);
return this;
};
/**
* Sets the helper tooltip non visible.
* @chainable
*/
Field.prototype.hideHelper = function () {
this.helperIsVisible = false;
this.helper.setVisible(false);
return this;
};
/**
* Returns an array with all the field's controls.
* @return {Array}
*/
Field.prototype.getControls = function () {
return this.controls;
};
/**
* Returns an index based field's control.
* @param {Number} index An integer value.
* @return {PMUI.control.Control}
*/
Field.prototype.getControl = function (index) {
index = index || 0;
return this.controls[index];
};
/**
* Sets the controlPositioning property.
* @param {String} positioning The string must have the same format that the
* {@link PMUI.form.Field#cfg-controlPositioning controlPositioning} config option.
*/
Field.prototype.setControlPositioning = function (positioning) {
var pos,
controlPos,
i,
j,
k,
controls,
span,
addControl,
that = this;
if (typeof positioning === 'string') {
this.controlPositioning = positioning;
if (this.html && this.controls.length) {
for (i = 0; i < this.controls.length; i += 1) {
jQuery(this.controls[i].getHTML()).detach();
}
$(this.dom.controlContainer).empty();
if (positioning !== "") {
controls = this.controls.slice();
pos = positioning.split(/\[c[\d|\*]\]/);
controlPos = positioning.match(/\[c[\d|\*]\]/g);
addControl = function (c) {
var k;
if (c === '[c*]') {
for (k = 0; k < controls.length; k += 1) {
if (controls[k] !== null) {
that.dom.controlContainer.appendChild(controls[k].getHTML());
controls[k] = null;
}
}
} else {
k = c.match(/\d+/);
k = parseInt(k[0], 10);
if (controls[k] !== null) {
that.dom.controlContainer.appendChild(controls[k].getHTML());
controls[k] = null;
}
}
};
j = 0;
for (i = 0; i < pos.length; i += 1) {
if (pos[i] === "" && j < controlPos.length) {
addControl(controlPos[j]);
j++;
} else {
span = PMUI.createHTMLElement('span');
span.textContent = pos[i];
this.dom.controlContainer.appendChild(span);
}
if (j < controlPos.length) {
addControl(controlPos[j]);
j++;
}
}
this.dom.labelTextContainer.setAttribute('for', this.controls[0].id);
this.dom.controlContainer.appendChild(this.helper.getHTML());
}
}
} else {
throw new Error("The setControlPositioning() method only accepts string values.");
}
return this;
};
/**
* Sets the controls for the field.
*
* Since this is an abstract method, it must be implemented in its non-abstract subclasses
* @abstract
* @private
*/
Field.prototype.setControls = function () {
};
/**
* Sets the name for the Field
* @param {String} name
*/
Field.prototype.setName = function (name) {
if (typeof name === 'string') {
this.name = name;
this.data.setKey(name);
} else {
throw new Error('The setName() method only accepts string values!');
}
return this;
};
/**
* Returns the field's name
* @return {String}
*/
Field.prototype.getName = function () {
return this.name;
};
/**
* Sets the text for the field's label.
* @param {String} label
*/
Field.prototype.setLabel = function (label) {
if (typeof label === 'string') {
this.label = label;
} else {
throw new Error("The setLabel() method only accepts string values!");
}
if (this.dom.fieldTextLabel) {
this.dom.fieldTextLabel.textContent = this.label;
}
if (this.dom.fieldRequired) {
this.dom.fieldRequired.textContent = '*';
this.setRequired(this.required);
}
if (this.dom.fieldColon) {
this.dom.fieldColon.textContent = ":";
}
return this;
};
/**
* Returns the text from the field's label.
* @return {String}
*/
Field.prototype.getLabel = function () {
return this.label;
};
/**
* Sets the value to the field's controls.
* @protected
* @param {String} value
* @chainable
*/
Field.prototype.setValueToControls = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].setValue(value);
}
return this;
};
/**
* Returns the current value from the field's controls without affect the field's value.
* @return {String}
* @private
*/
Field.prototype.getValueFromControls = function () {
var value = '',
i;
for (i = 0; i < this.controls.length; i += 1) {
value += ' ' + this.controls[i].getValue();
}
return value.substr(1);
};
/**
* Update the field's value property from the controls
* @protected
* @chainable
*/
Field.prototype.updateValueFromControls = function () {
var value = this.getValueFromControls();
this.value = value;
this.data.setValue(value);
return this;
};
/**
* Sets the field's value.
* @param {String} value
*/
Field.prototype.setValue = function (value) {
if (typeof value === 'number') {
value = value.toString();
}
if (typeof value === 'string') {
this.value = value;
} else {
throw new Error("The setValue() method only accepts string values!");
}
this.data.setValue(this.value);
this.setValueToControls(this.value);
return this;
};
/**
* Returns the field's value.
* @param [format] Defines the return format
* @returns {*}
*/
Field.prototype.getValue = function (format) {
var castFormat = format || this.valueType;
return PMUI.castValue(this.value, castFormat);
};
/**
* Sets the helper text.
* @param {String} helper
*/
Field.prototype.setHelper = function (helper) {
this.helper.setMessage(helper);
return this;
};
/**
* Shows a message below the control.
* @param {String} message The message
* @param {String} [category="info"] The message category,
* It can be one of the accepted values for the
* {@link PMUI.ui.TooltipMessage#setCategory TooltipMessage's setCategory() method}, it defaults to "info".
* @chainable
*/
Field.prototype.showMessage = function () {
this.message.setVisible(true)
return this;
};
/**
* Hides the message below the control.
* @chainable
*/
Field.prototype.hideMessage = function () {
this.message.setVisible(false);
return this;
};
/**
* Sets the value type for the field.
* @param {String} type
*/
Field.prototype.setValueType = function (type) {
var newType;
if (typeof type === 'string') {
newType = this.validTypes[type] ? type : 'object';
this.valueType = newType;
this.data.setType(newType);
} else {
throw new Error("The setValueType() method only accepts string values!");
}
return this;
};
/**
* Returns the field's value type.
* @return {String}
*/
Field.prototype.getValueType = function () {
return this.valueType;
};
/**
* The onChange event handler constructor.
* @private
* @return {Function} The handler.
*/
Field.prototype.onChangeHandler = function () {
var that = this,
i,
dependentFields = this.dependentFields,
form = this.form;
return function () {
var previousValue = that.value,
newValue = that.getValueFromControls();
if (newValue === previousValue) {
return this;
}
that.value = newValue;
that.data.setValue(newValue);
if (that.validAtChange) {
that.isValid();
}
if (typeof that.onChange === 'function') {
that.onChange(that.getValue(), previousValue);
}
if (that.form) {
(that.form.onChangeHandler())(that, that.value, previousValue);
//PMUI call a event for dependency [dependencyHandler]
for (i = 0; i < dependentFields.length; i += 1) {
dependentField = form.getField(dependentFields[i]);
if (!dependentField) {
that.removeDependentField(dependentFields[i]);
continue;
}
dependentField.fireDependencyHandler();
}
}
};
};
Field.prototype.fireDependencyHandler = function () {
var form = this.form,
dependsOf,
obj = {},
i,
that = this;
if (form) {
dependsOf = form.dependencies[that.name];
for (i = 0; i < dependsOf.length; i += 1) {
obj[dependsOf[i].name] = dependsOf[i];
}
if (typeof this.dependencyHandler === 'function') {
this.dependencyHandler(that, obj);
}
}
return this;
};
/**
* Attach the event listeners to the HTML element
* @private
* @chainable
*/
Field.prototype.defineEvents = function () {
var i,
that = this;
Field.superclass.prototype.defineEvents.call(this);
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].setOnChangeHandler(this.onChangeHandler())
.setOnBeforeChangeHandler(this.onBeforeChangeHandler())
.defineEvents();
}
if (this.onClick) {
this.addEvent('click').listen(this.html, function (e) {
if (typeof that.onClick === 'function') {
that.onClick(that);
}
});
this.addEvent('click').listen(this.dom.labelTextContainer, function (e) {
e.stopPropagation();
});
}
this.eventsDefined = true;
return this;
};
/**
* Returns true if the label is visible, otherwise returns false.
* @return {Boolean}
*/
Field.prototype.isLabelVisible = function () {
return this.labelVisible;
};
/**
* Hides the field's label.
* @chainable
*/
Field.prototype.hideLabel = function () {
jQuery(this.dom.labelTextContainer).css('visibility', 'hidden');
this.labelVisible = false;
return this;
};
/**
* Shows the field's label.
* @chainable
*/
Field.prototype.showLabel = function () {
jQuery(this.dom.labelTextContainer).css('visibility', '');
this.labelVisible = true;
return this;
};
/**
* Evaluates the required validation.
* @return {Boolean} Returns true if the validation passes otherwise returns false.
*/
Field.prototype.evalRequired = function () {
var valid = true;
if (this.required) {
if (this.valueType !== 'number') {
if (this.getValue() === "" || this.getValue() === "[]") {
valid = false;
}
} else {
if (this.getValue().toString() === 'NaN') {
valid = false;
}
}
if (valid) {
this.hideMessage();
} else {
this.showMessage();
}
}
return valid;
};
/**
* Executes the validations and returns true if all of them passes, otherwise returns false.
* @return {Boolean}
*/
Field.prototype.isValid = function () {
var valid = true, validator;
valid = valid && this.evalRequired();
if (!valid) {
this.controls[0].style.addClasses(['error']);
return valid;
}
this.controls[0].style.removeClasses(['error']);
for (validator in this.validators) {
if (this.validators.hasOwnProperty(validator)) {
valid = valid && this.validators[validator].isValid();
if (!valid) {
this.message.setText(this.validators[validator].errorMessage);
return valid;
}
}
}
return valid;
};
/**
* Create the HTML Element for the Field.
* @protected
* @return {HTMLElement}
*/
Field.prototype.createHTML = function () {
var html, label,
labelTextContainer,
controlContainer,
messageContainer,
fieldColon,
fieldRequired,
fieldTextLabel;
if (!this.html) {
this.style.addClasses(['pmui-field']);
Field.superclass.prototype.createHTML.call(this);
labelTextContainer = PMUI.createHTMLElement("label");
// contenedor de todos los labels
labelTextContainer.className = 'pmui-field-label';
//labelText
fieldTextLabel = PMUI.createHTMLElement('span');
fieldTextLabel.className = "pmui-field-textLabel";
//asterisk for required
fieldRequired = PMUI.createHTMLElement ('span');
fieldRequired.className = "pmui-field-required";
//colon
fieldColon = PMUI.createHTMLElement('span');
fieldColon.className = "pmui-field-colon";
controlContainer = PMUI.createHTMLElement("span");
controlContainer.className = 'pmui-field-control';
messageContainer = PMUI.createHTMLElement("span");
messageContainer.className = 'pmui-field-message';
messageContainer.style.display = "block"
//labelTextContainer append tree childs label, * and :
labelTextContainer.appendChild(fieldTextLabel);
labelTextContainer.appendChild(fieldRequired);
labelTextContainer.appendChild(fieldColon);
//messageContainer append the helper
messageContainer.appendChild(this.message.getHTML());
//this html append label, control and helper tag
this.html.appendChild(labelTextContainer);
this.html.appendChild(controlContainer);
this.html.appendChild(messageContainer);
this.dom.labelTextContainer = labelTextContainer;
this.dom.fieldTextLabel = fieldTextLabel;
this.dom.fieldColon = fieldColon;
this.dom.fieldRequired = fieldRequired;
this.dom.controlContainer = controlContainer;
this.dom.messageContainer = messageContainer;
this.setControlPositioning(this.controlPositioning);
this.setLabelWidth(this.labelWidth);
this.setLabel(this.label);
this.setValue(this.value);
this.setLabelPosition(this.labelPosition);
if (this.labelVisible) {
this.showLabel();
} else {
this.hideLabel();
}
if (this.visibleColon) {
this.showColon();
} else {
this.hideColon();
}
}
return this.html;
};
/**
* Set the width for the field.
* @param {Number|String} width height it can be a number or a string.
* In case of using a String you only can use 'auto' or ##px or ##% or ##em when ## is a number.
* @chainable
*/
Field.prototype.setWidth = function (width) {
Field.superclass.prototype.setWidth.call(this, width);
if (this.labelWidth) {
this.setLabelWidth(this.labelWidth);
}
return this;
};
/**
* @method setFocus
* set the focus on field for to control
* @param {Number} [index] if the field has more than one control and needs to focus on a specific Control
* @chainable
*/
Field.prototype.setFocus = function (index) {
var i = index || 0,
j,
controls = this.getControls();
if (this.controls[i] && this.controls[i].setFocus) {
this.controls[i].setFocus();
}
return this;
};
PMUI.extendNamespace('PMUI.form.Field', Field);
if (typeof exports !== "undefined") {
module.exports = Field;
}
}());
(function () {
/**
* @class PMUI.field.TextField
* @extends PMUI.form.Field
* Class to handle a {@link PMUI.control.TextControl} field.
*
* Usage example:
*
* @example
* var a;
* $(function() {
* a = new PMUI.field.TextField({
* label: "Some Text",
* placeholder: 'a text here',
* required: true,
* helper: "Introduce a text (6 chars. max.)",
* validators: [
* {
* pmType: 'textLength',
* criteria: {
* maxLength: 6
* }
* }
* ]
* });
* document.body.appendChild(a.getHTML());
* a.defineEvents();
* });
*
*
* The code above will generate a required TextField with "Some Text" as label,
* the control will have a placeholder with the text "a text here".
* Also the field will contain a helper with the text "Introduce a text (6 chars. max.)"
* and a validator that will control the maximum length for the text to be entered.
*
* @cfg {String} [placeholder=""] The text to show as placeholder for the field's control
* @cfg {Number} [maxLength=0] An integer that specifies the maximum character length for the text to be entered.
* 0 means no max length.
*
* #Note: This setting doesn't act as a validator, it simply set the max character length
* for the field's control.
* @cfg {Boolean} [trimOnBlur=true] A boolean that specifies if the value entered will be trimmed
* when the field loses focus.
*/
var TextField = function (settings) {
TextField.superclass.call(this, settings);
/**
* @property {Boolean} [trimOnBlur=true] If the field's value must be trimmed every time it loses focus.
*/
this.trimOnBlur = null;
TextField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', TextField);
TextField.prototype.type = "TextField";
TextField.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 0,
trimOnBlur: true,
readOnly: false
};
$.extend(true, defaults, settings);
this.setPlaceholder(defaults.placeholder)
.setMaxLength(defaults.maxLength)
.setTrimOnBlur(defaults.trimOnBlur)
.setReadOnly(defaults.readOnly)
.setDependentFields(this.dependentFields);
};
/**
* Sets the value for the field.
* @param {String} value
* @chainable
*/
TextField.prototype.setValue = function (value) {
if (this.trimOnBlur) {
value = jQuery.trim(value);
}
TextField.superclass.prototype.setValue.call(this, value);
return this;
};
/**
* Sets the placeholder for the control. Note that this feature is only supported
* by browsers which support the "placeholder" for input elements.
* @param {String} placeholder
* @chainable
*/
TextField.prototype.setReadOnly = function (readonly) {
this.controls[0].setReadOnly(readonly);
return this;
};
/**
* [setPlaceholder description]
* @param {[type]} placeholder [description]
*/
TextField.prototype.setPlaceholder = function (placeholder) {
this.controls[0].setPlaceholder(placeholder);
return this;
};
/**
* [getPlaceholder description]
* @return {[type]} [description]
*/
TextField.prototype.getPlaceholder = function () {
return this.controls[0].getPlaceholder();
};
/**
* [setMaxLength description]
* @param {[type]} maxLength [description]
*/
TextField.prototype.setMaxLength = function (maxLength) {
this.controls[0].setMaxLength(maxLength);
return this;
};
/**
* Set the parameter disabled for the control.
* @param {[disabled]} value
* @chainable
*/
TextField.prototype.setDisabled = function (value) {
if (typeof value === 'boolean') {
this.controls[0].setDisabled(value);
}
return this;
};
/**
* [getMaxLength description]
* @return {[type]} [description]
*/
TextField.prototype.getMaxLength = function () {
return this.controls[0].getMaxLength();
};
/**
* [isReadOnly description]
* @return {Boolean} [description]
*/
TextField.prototype.isReadOnly = function () {
return this.controls[0].isReadOnly();
};
/**
* Switches on/off the value trimming for the field's value when it loses focus.
* @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
* @chainable
*/
TextField.prototype.setTrimOnBlur = function (trimOnBlur) {
this.trimOnBlur = !!trimOnBlur;
return this;
};
/**
* Returns a boolean value that indicates if the trimming function is enabled/disabled
* @return {Boolean}
*/
TextField.prototype.getTrimOnBlur = function () {
return this.trimOnBlur;
};
/**
* Sets the control for the TextField
* @chainable
* @private
*/
TextField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.TextControl());
return this;
};
/**
* Establish the handler function for the internal onChange event
* @return {Function}
* @private
*/
TextField.prototype.onChangeHandler = function () {
var that = this,
i,
dependentFields = this.dependentFields,
form = this.form;
return function () {
var previousValue = that.value,
value;
if (that.trimOnBlur) {
value = that.controls[0].getValue();
value = jQuery.trim(value);
that.controls[0].setValue(value);
}
that.updateValueFromControls();
if (that.validAtChange) {
that.isValid();
}
if (typeof that.onChange === 'function') {
that.onChange(that.getValue(), previousValue);
}
if (that.form) {
(that.form.onChangeHandler())(that, that.getValue(), previousValue);
} else if (that.parent && that.parent.form) {
(that.parent.form.onChangeHandler())(that, that.getValue(), previousValue);
} else if (that.parent.parent && that.parent.parent.form) {
(that.parent.parent.form.onChangeHandler())(that, that.getValue(), previousValue);
} else if (that.parent.parent.parent && that.parent.parent.parent.form) {
(that.parent.parent.parent.form.onChangeHandler())(that, that.getValue(), previousValue);
} else {
return;
}
if (that.form) {
for (i = 0; i < dependentFields.length; i += 1) {
dependentField = form.getField(dependentFields[i]);
if (!dependentField) {
that.removeDependentField(dependentFields[i]);
continue;
}
dependentField.fireDependencyHandler();
}
}
};
};
PMUI.extendNamespace('PMUI.field.TextField', TextField);
if (typeof exports !== "undefined") {
module.exports = TextField;
}
}());
(function () {
/**
* @class PMUI.field.PasswordField
* @extends PMUI.form.Field
* Class to handle a {@link PMUI.control.PasswordControl} field.
*
* Usage example:
*
* @example
* var a;
* $(function() {
* a = new PMUI.field.PasswordField({
* label: "Some Text",
* required: true,
* helper: "Introduce a text (6 chars. max.)",
* validators: [
* {
* pmType: 'textLength',
* criteria: {
* maxLength: 6
* }
* }
* ]
* });
* document.body.appendChild(a.getHTML());
* a.defineEvents();
* });
*
*
* The code above will generate a required PasswordField with "Some Text" as label,
* the control will have a placeholder with the text "a text here".
* Also the field will contain a helper with the text "Introduce a text (6 chars. max.)"
* and a validator that will control the maximum length for the text to be entered.
*
* @cfg {Number} [maxLength=0] An integer that specifies the maximum character length for the text to be entered.
* 0 means no max length.
*
* #Note: This setting doesn't act as a validator, it simply set the max character length
* for the field's control.
* @cfg {Boolean} [trimOnBlur=true] A boolean that specifies if the value entered will be trimmed
* when the field loses focus.
*/
var PasswordField = function (settings) {
PasswordField.superclass.call(this, settings);
/**
* @property {Boolean} [trimOnBlur=true] If the field's value must be trimmed every time it loses focus.
*/
this.trimOnBlur = null;
PasswordField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', PasswordField);
PasswordField.prototype.type = "PasswordField";
PasswordField.prototype.init = function (settings) {
var defaults = {
maxLength: 0,
trimOnBlur: true
};
$.extend(true, defaults, settings);
this.setMaxLength(defaults.maxLength)
.setTrimOnBlur(defaults.trimOnBlur);
};
/**
* Sets the value for the field.
* @param {String} value
* @chainable
*/
PasswordField.prototype.setValue = function (value) {
if (this.trimOnBlur) {
value = jQuery.trim(value);
}
PasswordField.superclass.prototype.setValue.call(this, value);
return this;
};
/**
* Set MaxLength
* @param maxLength
* @returns {PMUI.field.PasswordField}
*/
PasswordField.prototype.setMaxLength = function (maxLength) {
this.controls[0].setMaxLength(maxLength);
return this;
};
/**
* Get Max Length
* @returns {*}
*/
PasswordField.prototype.getMaxLength = function () {
return this.controls[0].getMaxLength();
};
/**
* Switches on/off the value trimming for the field's value when it loses focus.
* @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
* @chainable
*/
PasswordField.prototype.setTrimOnBlur = function (trimOnBlur) {
this.trimOnBlur = !!trimOnBlur;
return this;
};
/**
* Returns a boolean value that indicates if the trimming function is enabled/disabled
* @return {Boolean}
*/
PasswordField.prototype.getTrimOnBlur = function () {
return this.trimOnBlur;
};
/**
* Sets the control for the PasswordField
* @chainable
* @private
*/
PasswordField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.PasswordControl());
return this;
};
/**
* Establish the handler function for the internal onChange event
* @return {Function}
* @private
*/
PasswordField.prototype.onChangeHandler = function () {
var that = this;
return function () {
var previousValue = that.value,
value;
if (that.trimOnBlur) {
value = that.controls[0].getValue();
value = jQuery.trim(value);
that.controls[0].setValue(value);
}
that.updateValueFromControls();
if (that.validAtChange) {
that.isValid();
}
if (typeof that.onChange === 'function') {
that.onChange(that.getValue(), previousValue);
}
if (that.form) {
(that.form.onChangeHandler())(that, that.getValue(), previousValue);
}
};
};
PMUI.extendNamespace('PMUI.field.PasswordField', PasswordField);
if (typeof exports !== "undefined") {
module.exports = PasswordField;
}
}());
(function () {
/**
* @class PMUI.field.TextAreaField
* @extends PMUI.field.TextField
* Class to handle a {@link PMUI.control.TextAreaControl}.
*
* Usage example:
*
* @example
* var a;
* $(function() {
* a = new PMUI.field.TextAreaField({
* id:'12345',
* label: "coment",
* cols: 300,
* rows: 200,
* name: 'coments',
* placeholder: 'make your comment here...........',
* required: true,
* labelposition:'left',
* labelVerticalPosition:'top',
* tooltipPosition:'bottom',
* helper: "Introduce a text (200 chars. max.)",
* validators: [
* {
* pmType: 'textLength',
* criteria: {
* maxLength: 200
* }
* }
* ]
* });
* document.body.appendChild(a.getHTML());
* });
*
*
* TextAreaField, is a field for very long text strings
* to generate the above code on TextAreaField requires, one id, label, name, placeholder, required,
labelposition,
* labelVerticalPosition, tooltipPosition, helper.
*
* In case to modified label position or tooltip position it can contain the next properties:
* @cfg {'string'} [labelVerticalPosition='top'] in case that the position of the label left or right we
fixed this
* property in three cases ('top', 'center', 'bottom')
* @cfg {'string'} [tooltipPosition='top'] this property can set the tooltip in two cases ('top' or 'bottom')
for TextAreaField
*/
var TextAreaField = function (settings) {
TextAreaField.superclass.call(this, settings);
/**
* @property {string} [labelVerticalPosition='top'], in case that the position of the label left
or right we fixed this
* property in three cases ('top', 'center', 'bottom')
* @readonly
*/
this.labelVerticalPosition = null;
/**
* @property {string} [tooltipPosition='top'] this property can set the tooltip in two cases ('top' or
'bottom').
* @readonly
*/
this.tooltipPosition = null;
this.cols = null;
this.rows = null;
TextAreaField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.field.TextField', TextAreaField);
TextAreaField.prototype.type = "TextAreaField";
TextAreaField.prototype.init = function (settings) {
var defaults = {
labelVerticalPosition: 'top',
tooltipPosition: 'top',
cols: 'auto',
rows: 'auto'
};
jQuery.extend(true, defaults, settings);
this.setLabelVerticalPosition(defaults.labelVerticalPosition);
this.setTooltipPosition(defaults.tooltipPosition);
this.setRows(defaults.rows);
this.setCols(defaults.cols);
};
/**
* Sets the labelVerticalPosition for the Label of the TextAreaField.
* input values (top, center and bottom) only affect the field if the label of TextAreaField is in
* left or right position.
* @param {String} labelVerticalPosition
* @chainable
*/
TextAreaField.prototype.setLabelVerticalPosition = function (value) {
var x,
result;
if (value === 'top' || value === 'bottom' || value === 'center') {
this.labelVerticalPosition = value;
if (this.controls[0] && this.html) {
this.dom.labelTextContainer.style.position = 'relative';
if (this.labelPosition === 'left' || this.labelPosition === 'right') {
if (this.labelVerticalPosition === 'top') {
this.dom.labelTextContainer.style.top = '0px';
this.dom.labelTextContainer.style.verticalAlign = 'top';
}
if (this.labelVerticalPosition === 'bottom') {
this.dom.labelTextContainer.style.top = '0px';
this.dom.labelTextContainer.style.verticalAlign = 'bottom';
}
if (this.labelVerticalPosition === 'center') {
this.dom.labelTextContainer.style.verticalAlign = 'top';
result = ($(this.controls[0].getHTML()).outerHeight() / 2) -
($(this.dom.labelTextContainer).outerHeight() / 2);
this.dom.labelTextContainer.style.top = (result) + 'px';
}
}
}
}
else {
throw new Error("The value is not 'top' or 'bottom' please enter these values");
}
return this;
};
/**
* Sets the label position
* input values(top, bottom, left, right)
* @param {String} labelPosition
* @chainable
*/
TextAreaField.prototype.setLabelPosition = function (position) {
if (this.html && (position === 'top' || position === 'bottom')) {
this.dom.labelTextContainer.style.top = '0px';
}
TextAreaField.superclass.prototype.setLabelPosition.call(this, position);
return this;
};
/**
* Sets the tooltipPosition for the Label of the TextAreaField.
* input values (top and bottom) only affect the field if the label of TextAreaField
* @param {String} tooltipPosition
* @chainable
*/
TextAreaField.prototype.setTooltipPosition = function (value) {
if (value === 'top' || value === 'bottom') {
this.tooltipPosition = value;
if (this.controls[0] && this.html) {
if (this.tooltipPosition === 'top') {
this.helper.icon.style.addProperties({'vertical-align': 'top'});
}
if (this.tooltipPosition === 'bottom') {
this.helper.icon.style.addProperties({'vertical-align': 'bottom'});
}
}
}
else {
throw new Error("The value is not 'top' or 'bottom' please enter these values");
}
return this;
};
/**
* Sets the setHeight for the {PMUI.controls.TextAreaControl}.
* @param {String|number} [PMUI.controls.TextAreaControl]
* @chainable
*/
TextAreaField.prototype.setControlHeight = function (height) {
this.controls[0].setHeight(height);
return this;
};
/**
* Sets the setWidth for the {PMUI.controls.TextAreaControl}.
* @param {String|number} [PMUI.controls.TextAreaControl]
* @chainable
*/
TextAreaField.prototype.setControlWidth = function (width) {
this.controls[0].setWidth(width);
return this;
};
/**
* Sets the text for the TextAreaField label.
* @param {String} label
*/
TextAreaField.prototype.setLabel = function (label) {
if (typeof label === 'string') {
this.label = label;
} else {
throw new Error("The setLabel() method only accepts string values!");
}
if (this.dom.fieldTextLabel) {
this.dom.fieldTextLabel.textContent = this.label;
}
if (this.dom.fieldRequired) {
this.dom.fieldRequired.textContent = '*';
this.setRequired(this.required);
}
if (this.dom.fieldColon) {
this.dom.fieldColon.textContent = ":";
}
return this;
};
/**
* Sets the control for the TextField
* @chainable
* @private
*/
TextAreaField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.TextAreaControl());
return this;
};
/**
* Disables the field
* @param {Boolean} Disable, if the field, then control is activated, if the control is enabled
* @chainable
*/
TextAreaField.prototype.disableField = function () {
this.controls[0].disable(true);
return this;
};
/**
* Disables/enables the control
* @param {Boolean} Enable, if the field, then control is activated, if the control is diabled
* @chainable
*/
TextAreaField.prototype.enableField = function () {
this.controls[0].disable(false);
return this;
};
/**
* Creates the HTML element for the textAreaField
* @return {HTMLElement}
*/
TextAreaField.prototype.createHTML = function () {
TextAreaField.superclass.prototype.createHTML.call(this);
this.setLabelVerticalPosition(this.labelVerticalPosition);
this.setTooltipPosition(this.tooltipPosition);
return this.html;
};
/**
* assign hight as rows to textArea,
* @param {number|String} rows will be assigned as high in PX, the entered values can
* be numbers, 'number + px', 'auto' or percentage
* @chainable
*/
TextAreaField.prototype.setRows = function (rows) {
this.controls[0].setHeight(rows);
return this;
};
/**
* assign width as Cols to textArea,
* @param {number|String} Cols will be assigned as high in PX, the entered values can
* be numbers, 'number + px', 'auto' or percentage
* @chainable
*/
TextAreaField.prototype.setCols = function (cols) {
this.controls[0].setWidth(cols);
return this;
};
PMUI.extendNamespace('PMUI.field.TextAreaField', TextAreaField);
if (typeof exports !== "undefined") {
module.exports = TextAreaField;
}
}());
(function () {
/**
* @class PMUI.field.DropDownListField
* @extends PMUI.form.Field
* Class to handle a {@link PMUI.control.DropDownListControl} field.
*
* Usage example:
*
* @example
* var settings, list;
* $(function(){
* settings = {
* id : '123',
* label : "Pais",
* name : 'List Select',
* required: true,
* helper: "selected one opcion of the list",
* options: [
* {
* label: "BOLIVIA",
* options: [
* {
* label: "La Paz",
* value: 1,
* disabled : true
* },
* {
* label: "Cochabamba",
* value: 2
* },
* {
* label: "SantaCruz",
* value: 3
* },
* ]
* },
* {
* label: "ARGENTINA",
* options: [
* {
* label: "Buenos Aires",
* value: 4
* },
* {
* label: "Santa Fe",
* value: 5
* },
* {
* value: "Cordoba"
* }
* ]
* },
* {
* label: "CHILE",
* options: [
* {
* label: "Arica",
* value: 6
* },
* {
* label: "Santiago",
* value: 7
* },
* {
* value: "Concepcion"
* }
* ]
* },
* {
* label: "New York",
* value: 8
* },
* {
* label: "Mexico D.F.",
* value: 9
* }
* ]
* }
* list = new PMUI.field.DropDownListField(settings);
* document.body.appendChild(list.getHTML());
* });
*
*
* DropDownListField typo is a selection field, enclose the values ​​that we can choose from a list
* of options. The attributes that accompany the opening tag are:
* The code above will generate a DropDownListField, required one id, name, requiere,
* helper, text for list the options, one opcion array with the list options
*
* @constructor Creates an instance of the class DropDownListField.
* @param {Object} options Initialization options.
* @cfg {Array} [options=[]] An array with all the options to be contained by the
{PMUI.control.DropDownListControl}.
*
* Each element in the array is a JSON object, this JSON object can represent an option group
* or an option item.
*
* In case to represent an option item it can contain the next properties:
*
* - value {String} (required): the value for the option.
* - label {String} (optional): the label for the option, if isn't specified the value is used instead.
* - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
* "value" propery set then this "selected" property will be
* - disabled {Boolean} (optional): if the option is disabled or not.
*
* On the other hand, in case to represent an option group, it can contain the next properties:
*
* - label {String} (required): The name for the option group.
* - disabled {Boolean} (optional): If the group is disabled or not.
* - options {Array} (required): An array in which each element is a JSON object representing an option item,
* so every item must have the structure explained above (for represent option items). #Note. This propery makes
* the difference between an option and a option group. If the "options" property is not specified or if it isn't
* an array then it will treated like a option item.
* @cfg {String|Number} [listWidth='auto'] This value sets the width of the list
*/
var DropDownListField = function (settings) {
DropDownListField.superclass.call(this, settings);
/**
* @property {Boolean} [disabled=false] If the field is disabled or not.
* @readonly
*/
this.listWidth = null;
//this.defaultValue = null;
DropDownListField.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.form.Field", DropDownListField);
DropDownListField.prototype.type = "DropDownListField";
DropDownListField.prototype.init = function (settings) {
var defaults = {
options: [],
listWidth: 'auto',
value: null
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options);
this.setListWidth(defaults.listWidth);
this.updateValueFromControls();
if (defaults.value !== null) {
this.setValue(defaults.value);
}
};
/**
* Sets the options/option groups for the control.
* @param {Array} options An array with the same structure that the
* {@link PMUI.field.DropDownListField#cfg-options "options"} property in the
* Config settings section.
*/
DropDownListField.prototype.setOptions = function (options) {
var i;
if (jQuery.isArray(options)) {
this.controls[0].setOptions(options);
}
if (!this.value && options) {
for (i = 0; i < options.length; i += 1) {
if (options[i].selected) {
this.initialValue = options[i].value || options[i].label || "";
break;
}
}
}
this.value = this.controls[0].getValue();
return this;
};
/**
* Returns the options/option groups from the field
* @param {Boolean} [includeGroups=false] If it's evaluated as true then it will include
* the option groups with its child elements, otherwise it will return only the option items.
* @return {Array}
*
* example
*
* list.getOptions(false)
* [La Paz][Cochabamba][Santa Cruz][Buenos Aires][Santa Fe][Cordoba][Santiago][.][.][Mexico D.F]
*
* list.getOptions(true)
* [BOLIVIA
* [La Paz][Cochabamba][SantaCruz]
* ]
* [ARGENTINA
* [Buenos Aires][Santa Fe][Cordoba]
* ]
* [CHILE
* [x][y][z]
* ]
* [New York]
* [Mexico D.F.]
*
* @return {Array}
*/
DropDownListField.prototype.getOptions = function (includeGroups) {
return this.controls[0].getOptions(includeGroups);
};
/**
* Clear all the options from the control.
* @chainable
*/
DropDownListField.prototype.clearOptions = function () {
this.controls[0].clearOptions();
this.value = this.controls[0].value;
return this;
};
/**
* Disables one or more options/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be disabled the options that match the string in its value and the option
* groups which match the string in its label. In this case more than one single item can be disabled.
* - In case to be a Number, it will be disabled the option/option group which index position matches the number.
* Obviously, in this case only one item will be disabled.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items
needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group
label.
* Using this parameter, the elements to be match by the first parameter will be search only in the option groups
* that match this parameter in its label.
*
* example
* list = [a
* [a0]
* [a1]
* ]
* [b
* [b0]
* [b1]
* ]
* [c]
* //disabling sending a number
* list.disableOption(0); -->disabled group a
* list.disableOption(4); -->disabled option b[b0]
*
* //disabling sending a string
* list.disableOption('a') --> disabled group a
* list.disableOption('b0') --> disabled option b[b0]
* //disabling sending a objects
list.disableOption({criteria:'a', applyTo:'groups'}) --> disabled group a
*
* @chainable
*/
DropDownListField.prototype.disableOption = function (option, group) {
this.controls[0].disableOption(option, group);
return this;
};
/**
* Enables one or more options/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be enabled the options that match the string in its value and the option
* groups which match the string in its label. In this case more than one single item can be enabled.
* - In case to be a Number, it will be enabled the option/option group which index position matches the number.
* Obviously, in this case only one item will be enabled.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group
label.
* Using this parameter, the elements to be match by the first parameter will be search only in the option groups
* that match this parameter in its label.
* @chainable
*/
DropDownListField.prototype.enableOption = function (option, group) {
this.controls[0].enableOption(option, group);
return this;
};
/**
* Removes one or more option/option groups.
* @param {String|Number|Object} option It can be a string, a number or a JSON object.
*
* - In case to be a String, it will be removed the options that match the string in its value and the option
* groups which match the string in its label. In this case more than one single item can be removed.
* - In case to be a Number, it will be removed the option/option group which index position matches the number.
* Obviously, in this case only one item will be removed.
* - In case to be an object you can specify if the change will be applied only to options or option groups,
* it should have two properties:
* - criteria {String}: The value (in case of options) or the label (in case of option groups) the items
needs
* to match for apply the changes.
* - applyTo {String} (optional), it can take the following values:
* - "groups", the change will be applied only to the option groups.
* - "options", the change will be applied only to the options (direct child of the object).
* - [any other string value], the default value, it indicates that the change will be applied to both
* options/option groups that matches the criteria in its value/label respectly.
*
* @param {String} [group] It it is specified must be an String making reference to an existing option group
label.
* Using this parameter, the elements to be match by the first parameter will be search only in the option groups
* that match this parameter in its label.
*
* ##Note. Removing an option group implies removing all its child options.
* @chainable
*/
DropDownListField.prototype.removeOption = function (option, group) {
this.controls[0].removeOption(option, group);
return this;
};
/**
* Adds a new option group to the Field
* @param {Object} optionGroup A JSON object with the following properties:
*
* - label {String} (required): the label for the option group.
* - disabled {Boolean}(optional): if the option group will be disabled or not.
* it defaults to false.
* - options {Array} (optional): An array of JSON object, each one represents an option and
* should have the same structure than the "option" paremeter for the
* {@link PMUI.field.DropDownListField#addOption addOption() method}.
*/
DropDownListField.prototype.addOptionGroup = function (optionGroup) {
this.controls[0].addOptionGroup(optionGroup);
return this;
};
/**
* Adds a new option to the list of the DropDownListField or to an option group.
* @param {Object} option An object with ther settings for the new option.
* this object can have the following properties:
*
* - value {String} (required): the value for the option.
* - label {String} (optional): the label for the option, if isn't specified the value is used instead.
* - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
* "value" propery set then this "selected" property will be
* - disabled {Boolean} (optional): if the option is disabled or not.
*
* @param {String} group The name of the option group in which the new option will be added. If it doesn't exist
* it will be created.
*
* usage
* // add option a groupç
*
* list.addOption({value:'5', label:'Pando', selected: true, disabled:true },'BOLIVIA')
*
* //or add option
*
* list.addOption({value:'paris', label:'Paris'})
*
* @chainable
*/
DropDownListField.prototype.addOption = function (option, group) {
this.controls[0].addOption(option, group);
if (this.getOptions().length == 1) {
this.value = this.controls[0].value;
}
return this;
};
/**
* Returns the label from the option currently selected.
* @return {String}
*/
DropDownListField.prototype.getSelectedLabel = function () {
return this.controls[0].getSelectedLabel();
};
/**
* Sets the control for the DropDownListField
* @chainable
* @private
*/
DropDownListField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.DropDownListControl());
return this;
};
/**
* Set the width for the HTML DropDownListControl(select)
* @param {Number|String} width height it can be a number or a string.
In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
* @chainable
*/
DropDownListField.prototype.setListWidth = function (width) {
this.listWidth = width;
this.controls[0].setWidth(this.listWidth);
return this;
};
DropDownListField.prototype.getListWidth = function (width) {
return this.listWidth;
};
DropDownListField.prototype.setValue = function (value) {
var val;
if (this.controls[0]) {
val = this.controls[0].setValue(value).getValue();
}
DropDownListField.superclass.prototype.setValue.call(this, val || value);
return this;
};
PMUI.extendNamespace('PMUI.field.DropDownListField', DropDownListField);
if (typeof exports !== "undefined") {
module.exports = DropDownListField;
}
}());
(function () {
/**
* @class PMUI.field.CheckBoxGroupField
* Field where the values can be selected from a group of options.
* @extends PMUI.form.Field
*
* Usage example:
*
* @example
* var a;
* $(function() {
* a = new PMUI.field.CheckBoxGroupField({
* label: "development tools",
* controlPositioning: 'vertical',
* maxDirectionOptions: 2,
* required: true,
* value: '1',
* helper: "what lanaguages you know?. Select others besides javascript",
* options : [
* { label: 'Java',
* disabled: false,
* value: 'java',
* selected :true
* },
* { label: 'JavaScript',
* disabled: true,
* value: 'javaScript',
* selected :true
* },
* { label: 'Python',
* disabled: false,
* value: 'python'
* },
* { label: 'C#',
* disabled: true,
* value: 'visualbasic'
* }
* ],
* onChange: function(newVal, oldVal) {
* console.log("The value for the field \"" + this.getLabel() +
* "\": has change from \"" + oldVal + "\" to \"" + newVal + "\"");
* },
* });
* document.body.appendChild(a.getHTML());
* });
*
*
* The example above will generate a field with 4 CheckBox options.
*
* @constructor Creates an instance of the class CheckBoxGroupField.
* @param {Object} options Initialization options.
* @cfg {Array} [options=[]] An array in which every element is a JSON object with the same
structure required in the
* {@link PMUI.field.CheckBoxGroupField#addOption addOption() method}.
* @cfg {String} [controlPositioning="vertical"] A string that determines the direction for the options to be added
* in the field. This string can have one of the following values:
*
* - "horizontal" (default), in this case all the options will be included in horizontal order.
* - "vertical", in this case all the options will be included in vertical order.
* @cfg {Number} [maxDirectionOptions=1] The maximum number of options to be added in the current direction
* (set by the {@link PMUI.control.CheckBoxGroupField#cfg-controlPositioning controlPositioning config option}).
*
* @cfg {String} [value='[]'] The value that determines which options will be checked. the String must have an
* array format in which each element is the value of the options that are wanted to be checked.
*/
var CheckBoxGroupField = function (settings) {
CheckBoxGroupField.superclass.call(this, settings);
/**
* @property {String} controlPositioning The direction for the options to be added in the field.
* @readonly
*/
this.controlPositioning = null;
/**
* @property {Number} maxDirectionOptions The max number of options that can be in the current options
* direction.
* @readonly
*/
this.maxDirectionOptions = null;
/**
* The status if the controls to be saved when the field is switch between enabled/disabled.
* @type {Object}
* @private
*/
this.auxControlStates = {};
this.controlTable = {};
CheckBoxGroupField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', CheckBoxGroupField);
CheckBoxGroupField.prototype.type = "CheckBoxGroupField";
CheckBoxGroupField.prototype.init = function (settings) {
var defaults = {
options: [],
controlPositioning: "vertical",
maxDirectionOptions: 1,
value: '[]'
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options)
.setMaxDirectionOptions(defaults.maxDirectionOptions)
.setControlPositioning(defaults.controlPositioning);
};
/**
* Enables the field. Notice that the controls that initially were disabled will continue being disabled.
* @chainable
*/
CheckBoxGroupField.prototype.enable = function () {
var key,
i,
controlsLength = this.controls.length,
controls = this.controls;
CheckBoxGroupField.superclass.prototype.enable.call(this);
this.disabled = false;
for (i = 0; i < controlsLength; i += 1) {
controls[i].disable(this.auxControlStates[controls[i].id]);
}
return this;
};
/**
* Sets the max number of controls that can be in the current direction (the direction is set by the
* CheckBoxGroupField's
* {@link PMUI.field.CheckBoxGroupField#setControlPositioning setControlPositioning() method}).
* @param {Number} max It should be an integer, otherwise it will be floor rounded. If the value is equal or minor
* than 0 it means there's no limit.
*/
CheckBoxGroupField.prototype.setMaxDirectionOptions = function (max) {
if (typeof max === 'number') {
this.maxDirectionOptions = Math.floor(max);
if (this.html) {
this.setControlPositioning(this.controlPositioning);
}
} else {
throw new Error("setMaxDirectionOptions(): it only accepts number values.");
}
return this;
};
/**
* It overrides the Field's {@link PMUI.form.Field#setControlPositioning setControlPositioning() method}.
* This new implementation sets the direction for the control addition: horizontal or vertical.
* @param {String} positioning It can be "horizontal" or "vertical".
*/
CheckBoxGroupField.prototype.setControlPositioning = function (positioning) {
var errorMessage = "The setControlPositioning() method only accepts \"horizontal\" or \"vertical\" as value.",
table,
tbody,
cell,
row,
i,
column,
rowIndex;
if (typeof positioning === 'string') {
if (!(positioning === 'horizontal' || positioning === 'vertical')) {
return this;
}
this.controlPositioning = positioning;
if (this.html && this.controls) {
for (i = 0; i < this.controls.length; i += 1) {
jQuery(this.controls[i].getHTML()).detach();
}
$(this.dom.controlContainer).empty();
table = PMUI.createHTMLElement("table");
table.className = 'pmui-field-control-table';
tbody = PMUI.createHTMLElement("tbody");
if (positioning === 'horizontal') {
row = PMUI.createHTMLElement("tr");
for (i = 0; i < this.controls.length; i += 1) {
cell = PMUI.createHTMLElement('td');
this.controls[i].getHTML();
this.controls[i].control.tabIndex = i;
cell.appendChild(this.controls[i].getHTML());
row.appendChild(cell);
if (this.maxDirectionOptions > 0 && (i + 1) % this.maxDirectionOptions === 0) {
tbody.appendChild(row);
row = PMUI.createHTMLElement("tr");
}
}
tbody.appendChild(row);
} else {
column = 0;
for (i = 0; i < this.controls.length; i += 1) {
cell = PMUI.createHTMLElement('td');
this.controls[i].getHTML();
this.controls[i].control.tabIndex = i;
cell.appendChild(this.controls[i].getHTML());
rowIndex = this.maxDirectionOptions === 0 ? i : i % this.maxDirectionOptions;
row = jQuery(tbody).find('tr').eq(rowIndex).get(0);
if (!row) {
row = PMUI.createHTMLElement('tr');
tbody.appendChild(row);
}
row.appendChild(cell);
}
}
this.controlTable = table;
table.appendChild(tbody);
this.dom.controlContainer.appendChild(table);
this.dom.controlContainer.appendChild(this.helper.getHTML());
}
}
return this;
};
/**
* Sets all the options for the control for checkbox button.
* @param {Array} options An array in which every element is a JSON object with the
same structure required in the
* {@link PMUI.field.CheckBoxGroupField#addOption addOption() method}.
* @chainable
*/
CheckBoxGroupField.prototype.setOptions = function (options) {
var i = 0;
this.value = [];
this.value = JSON.stringify(this.value);
if (jQuery.isArray(options)) {
for (i = 0; i < options.length; i += 1) {
this.addOption(options[i]);
}
} else {
throw new Error("setOptions(): the supplied argument must be an array.");
}
return this;
};
/**
* Adds a new option to the CheckBoxGroupField.
* @param {Object} option A JSON object, which can have the same properties that the config options
* for the {@link PMUI.control.SelectableControl SelectableControl class} except by "mode",
it will always be override by the value "checkBox".
* @chainable
*/
CheckBoxGroupField.prototype.addOption = function (option) {
var newOption;
newOption = new PMUI.control.SelectableControl(jQuery.extend(true, option, {
mode: 'checkbox',
name: this.controls.length + 1,
selected: option.selected
}));
if (newOption.isSelected()) {
this.value = JSON.parse(this.value);
this.value.push(newOption.getValue());
this.value = JSON.stringify(this.value);
}
if (this.eventsDefined) {
newOption.setOnChangeHandler(this.onChangeHandler()).getHTML();
newOption.setOnBeforeChangeHandler(this.onBeforeChangeHandler());
newOption.defineEvents();
}
this.auxControlStates[newOption.id] = newOption.disabled;
this.controls.push(newOption);
this.setControlPositioning(this.controlPositioning);
return this;
};
/**
* Removes one or more options (checkBox Button).
* @param {String|Number|PMUI.control.SelectableControl} option It can be:
*
* - A number, in that case the parameter is used as the index of the item to be removed.
* - A String, in that case the parameter is used as the item's value,
* that means that everyone item that has that value will be removed.
* - An instance of {@link PMUI.control.SelectableControl SelectableControl} which must be a control of the field.
* @chainable
*/
CheckBoxGroupField.prototype.removeOption = function (item) {
var itemToRemove,
i;
if (item instanceof PMUI.control.SelectableControl) {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i] === item) {
itemToRemove = i;
break;
}
}
} else if (typeof item === 'string') {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].id === item) {
itemToRemove = this.controls[i];
break;
}
}
} else {
itemToRemove = item;
}
if (typeof itemToRemove === 'number') {
itemToRemove = this.controls[itemToRemove];
delete this.auxControlStates[itemToRemove.id];
jQuery(itemToRemove.html).detach();
this.controls.splice(itemToRemove, 1);
this.setControlPositioning(this.controlPositioning);
}
return this;
};
/**
* It clears all the options from the field.
* @chainable
*/
CheckBoxGroupField.prototype.clearOptions = function () {
while (this.controls.length) {
this.removeOption(0);
}
return this;
};
/**
* Update the field's value property from the controls
* the parameter value is a clean array;
* @protected
* @chainable
*/
CheckBoxGroupField.prototype.updateValueFromControls = function () {
var value = [], i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].isSelected()) {
value.push(this.controls[i].getValue());
}
}
this.value = JSON.stringify(value);
this.data.setValue(this.value);
return this;
};
/**
* Sets the value to the field's controls.
* @protected
* @param {String} value
* @chainable
*/
CheckBoxGroupField.prototype.setValueToControls = function (value) {
var i, j, values, controls, controlsLength;
try {
value = value.replace(/'/g, "\"");
values = JSON.parse(value);
} catch (e) {
values = '[]';
}
controlsLength = (controls = this.controls.slice(0)).length;
for (i = 0; i < controlsLength; i += 1) {
controls[i].deselect();
}
for (i = 0; i < values.length; i += 1) {
for (j = 0; j < controlsLength; j += 1) {
if (controls[j].getValue() === values[i]) {
controls[j].select();
controls.splice(j, 1);
controlsLength -= 1;
j -= 1;
}
}
}
return this;
};
/**
* Evaluates the required validation.
* @return {Boolean} Returns true if the validation passes otherwise returns false.
*/
CheckBoxGroupField.prototype.evalRequired = function () {
var valid = true, value;
value = JSON.parse(this.getValue());
if (this.required && value.length === 0) {
this.showMessage(this.requiredMessage, "error");
valid = false;
} else {
this.hideMessage();
}
return valid;
};
CheckBoxGroupField.prototype.isValid = function () {
var valid = true,
validator;
valid = valid && this.evalRequired();
if (!valid) {
$(this.controlTable).addClass('error');
return valid;
}
$(this.controlTable).removeClass('error');
for (validator in this.validators) {
if (this.validators.hasOwnProperty(validator)) {
valid = valid && this.validators[validator].isValid();
if (!valid) {
this.message.setText(this.validators[validator].errorMessage);
return valid;
}
}
}
return valid;
};
/**
* @inheritdoc
*/
CheckBoxGroupField.prototype.getValue = function (format) {
var castFormat = format || this.valueType,
controls = this.controls,
i,
res = [];
for (i = 0; i < controls.length; i += 1) {
if (controls[i].isSelected()) {
res.push(PMUI.castValue(controls[i].getValue(), castFormat));
}
}
return JSON.stringify(res);
};
/**
* Disable the option
* @param {number|string} disable If the value is part of group CheckBox
* @chainable
*/
CheckBoxGroupField.prototype.disableOption = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].value == value || this.controls[i].label == value || value == i) {
this.controls[i].disable(true);
return this;
}
}
throw new Error('the value send is not part of group CheckBox');
};
/**
* Enable the option
* @param {number|string} disable If the value is part of group CheckBox
* Enables a field's option.
*/
CheckBoxGroupField.prototype.enableOption = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].value == value || this.controls[i].label == value || value == i) {
this.controls[i].disable(false);
return this;
}
}
throw new Error('the value send is not part of group CheckBox');
};
CheckBoxGroupField.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
CheckBoxGroupField.superclass.prototype.createHTML.call(this)
if (this.disabled) {
this.disable();
}
return this.html;
};
CheckBoxGroupField.prototype.checkedOption = function (option) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (option === i || option === this.controls[i].label || option === this.controls[i].value) {
if (!this.controls[i].selected) {
this.controls[i].select();
} else {
this.controls[i].deselect();
}
return this;
}
}
return this;
};
CheckBoxGroupField.prototype.defineEvents = function () {
var that = this;
CheckBoxGroupField.superclass.prototype.defineEvents.call(this);
this.addEvent('click').listen(this.dom.labelTextContainer, function (e) {
if (that.controls.length) {
$(that.controls[0].html).find('input')[0].focus();
}
});
return this;
};
/**
* @method getOptions
* @ obtains under each option and configuration
* @chainable
*/
CheckBoxGroupField.prototype.getOptions = function () {
var i,
options = [],
option;
for (i = 0; i < this.controls.length; i += 1) {
option = {};
option["label"] = this.controls[i].label;
option["value"] = this.controls[i].value;
option["disabled"] = this.controls[i].disabled;
option["selected"] = this.controls[i].selected;
options.push(option);
}
return options;
};
CheckBoxGroupField.prototype.getValueFromControls = function () {
var value = [],
i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].isSelected()) {
value.push(this.controls[i].getValue());
}
}
this.value = JSON.stringify(value);
this.data.setValue(this.value);
return this.value;
};
PMUI.extendNamespace('PMUI.field.CheckBoxGroupField', CheckBoxGroupField);
if (typeof exports !== "undefined") {
module.exports = CheckBoxGroupField;
}
}());
(function () {
/**
* @class PMUI.field.RadioButtonGroupField
* Field whose value can be selected from a group of options.
* @extends PMUI.form.Field
*
* Usage example:
*
* @example
* var a;
* $(function() {
* a = new PMUI.field.RadioButtonGroupField({
* label: "Some Text",
* controlPositioning: 'vertical',
* maxDirectionOptions: 2,
* options: [
* {
* label: "opt1",
* value: "1"
* },
* {
* label: "opt2",
* value: "2"
* },
* {
* label: "opt3",
* value: "3"
* }
* ],
* onChange: function(newVal, oldVal) {
* console.log("The value for the field \"" + this.getLabel()
* + "\": has change from \"" + oldVal + "\" to \"" + newVal + "\"");
* },
* required: true,
* value: "2"
* });
* document.body.appendChild(a.getHTML());
* });
*
*
* The example above will generate a field with 3 radio buttons.
*
* @cfg {Array} [options=[]] An array in which every element is a JSON object with the same structure required in
* the {@link PMUI.field.RadioButtonGroupField#addOption addOption() method}.
* @cfg {String} [controlPositioning="vertical"] A string that determines the direction for the options to be added
* in the field. This string can have one of the following values:
*
* - "horizontal" (default), in this case all the options will be included in horizontal order.
* - "vertical", in this case all the options will be included in vertical order.
* @cfg {Number} [maxDirectionOptions=1] The maximum number of options to be added in the current direction
* (set by the {@link PMUI.control.RadioButtonGroupField#cfg-controlPositioning controlPositioning config option}).
*/
var RadioButtonGroupField = function (settings) {
RadioButtonGroupField.superclass.call(this, settings);
/**
* @property {String} controlPositioning The direction for the options to be added in the field.
* @readonly
*/
this.controlPositioning = null;
/**
* @property {Number} maxDirectionOptions The max number of options that can be in the current options
* direction.
* @readonly
*/
this.maxDirectionOptions = null;
/**
* The status if the controls to be saved when the field is switch between enabled/disabled.
* @type {Object}
* @private
*/
this.auxControlStates = {};
this.controlTable = null;
RadioButtonGroupField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', RadioButtonGroupField);
RadioButtonGroupField.prototype.type = "RadioButtonGroupField";
RadioButtonGroupField.prototype.init = function (settings) {
var defaults = {
options: [],
controlPositioning: "vertical",
maxDirectionOptions: 1
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options)
.setMaxDirectionOptions(defaults.maxDirectionOptions)
.setControlPositioning(defaults.controlPositioning);
};
/**
* Enables the field. Notice that the controls that initially were disabled will continue being disabled.
* @chainable
*/
RadioButtonGroupField.prototype.enable = function () {
var key,
i,
controlsLength = this.controls.length,
controls = this.controls;
RadioButtonGroupField.superclass.prototype.enable.call(this);
this.disabled = false;
for (i = 0; i < controlsLength; i += 1) {
controls[i].disable(this.auxControlStates[controls[i].id]);
}
return this;
};
/**
* Sets the max number of controls that can be in the current direction (the direction is set by the
* RadioButtonGroupField's
* {@link PMUI.field.RadioButtonGroupField#setControlPositioning setControlPositioning() method}).
* @param {Number} max It should be an integer, otherwise it will be floor rounded. If the value is equal or minor
* than 0 it means there's no limit.
*/
RadioButtonGroupField.prototype.setMaxDirectionOptions = function (max) {
if (typeof max === 'number') {
this.maxDirectionOptions = Math.floor(max);
if (this.html) {
this.setControlPositioning(this.controlPositioning);
}
} else {
throw new Error("setMaxDirectionOptions(): it only accepts number values.");
}
return this;
};
/**
* Removes one or more options (radio button)
* @param {String|Number|PMUI.control.SelectableControl} option It can be:
*
* - A number, in that case the parameter is used as the index of the item to be removed.
* - A String, in that case the parameter is used as the item's value,
* that means that everyone item that has that value will be removed.
* - An instance of {@link PMUI.control.SelectableControl SelectableControl} which must be a control of the field.
* @chainable
*/
RadioButtonGroupField.prototype.removeOption = function (item) {
var itemToRemove,
i;
if (item instanceof PMUI.control.SelectableControl) {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i] === item) {
itemToRemove = i;
break;
}
}
} else if (typeof item === 'string') {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].id === item) {
itemToRemove = this.controls[i];
break;
}
}
} else {
itemToRemove = item;
}
if (typeof itemToRemove === 'number') {
itemToRemove = this.controls[itemToRemove];
delete this.auxControlStates[itemToRemove.id];
jQuery(itemToRemove.html).detach();
this.controls.splice(itemToRemove, 1);
this.setControlPositioning(this.controlPositioning);
}
return this;
};
/**
* It clears all the options from the field.
* @chainable
*/
RadioButtonGroupField.prototype.clearOptions = function () {
while (this.controls.length) {
this.removeOption(0);
}
return this;
};
/**
* It overrides the Field's {@link PMUI.form.Field#setControlPositioning setControlPositioning() method}.
* This new implementation sets the direction for the control addition: horizontal or vertical.
* @param {String} positioning It can be "horizontal" or "vertical".
*/
RadioButtonGroupField.prototype.setControlPositioning = function (positioning) {
var errorMessage = "The setControlPositioning() method only accepts \"horizontal\" or \"vertical\" as value.",
table,
tbody,
cell,
row,
i,
column,
rowIndex;
if (typeof positioning === 'string') {
if (!(positioning === 'horizontal' || positioning === 'vertical')) {
return this;
}
this.controlPositioning = positioning;
if (this.html && this.controls) {
for (i = 0; i < this.controls.length; i += 1) {
jQuery(this.controls[i].getHTML()).detach();
}
$(this.dom.controlContainer).empty();
table = PMUI.createHTMLElement("table");
table.className = 'pmui-field-control-table';
tbody = PMUI.createHTMLElement("tbody");
if (positioning === 'horizontal') {
row = PMUI.createHTMLElement("tr");
for (i = 0; i < this.controls.length; i += 1) {
cell = PMUI.createHTMLElement('td');
this.controls[i].getHTML();
this.controls[i].control.tabIndex = i;
cell.appendChild(this.controls[i].getHTML());
row.appendChild(cell);
if (this.maxDirectionOptions > 0 && (i + 1) % this.maxDirectionOptions === 0) {
tbody.appendChild(row);
row = PMUI.createHTMLElement("tr");
}
}
tbody.appendChild(row);
} else {
column = 0;
for (i = 0; i < this.controls.length; i += 1) {
cell = PMUI.createHTMLElement('td');
this.controls[i].getHTML();
this.controls[i].control.tabIndex = i;
cell.appendChild(this.controls[i].getHTML());
rowIndex = this.maxDirectionOptions === 0 ? i : i % this.maxDirectionOptions;
row = jQuery(tbody).find('tr').eq(rowIndex).get(0);
if (!row) {
row = PMUI.createHTMLElement('tr');
tbody.appendChild(row);
}
row.appendChild(cell);
}
}
this.controlTable = table;
table.appendChild(tbody);
this.dom.controlContainer.appendChild(table);
this.dom.controlContainer.appendChild(this.helper.getHTML());
}
}
return this;
};
/**
* Adds a new option to the field.
* @param {Object} option A JSON object, which can have the same properties that the config options
* for the {@link PMUI.control.SelectableControl SelectablecControl class} except by "mode",
* it will always be override by the value "radio".
* @chainable
*/
RadioButtonGroupField.prototype.addOption = function (option) {
var newOption,
settings = {
mode: 'radio',
name: this.name,
selected: option.selected
};
newOption = new PMUI.control.SelectableControl(jQuery.extend(true, option, settings));
if (this.eventsDefined) {
newOption.setOnChangeHandler(this.onChangeHandler()).getHTML();
newOption.defineEvents();
}
this.auxControlStates[newOption.id] = newOption.disabled;
this.controls.push(newOption);
this.setControlPositioning(this.controlPositioning);
return this;
};
/**
* Sets all the options for the control.
* @param {Array} options An array in which every element is a JSON object with the same structure required in the
* {@link PMUI.field.RadioButtonGroupField#addOption addOption() method}.
* @chainable
*/
RadioButtonGroupField.prototype.setOptions = function (options) {
var i = 0;
if (jQuery.isArray(options)) {
for (i = 0; i < options.length; i += 1) {
this.addOption(options[i]);
}
} else {
throw new Error("setOptions(): the supplied argument must be an array.");
}
return this;
};
/**
* Update the field's value property from the controls
* @protected
* @chainable
*/
RadioButtonGroupField.prototype.updateValueFromControls = function () {
var value = '',
i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].isSelected()) {
value = this.controls[i].getValue();
break;
}
}
this.value = value;
this.data.setValue(this.value);
return this;
};
/**
* Sets the value to the field's controls.
* @protected
* @param {String} value
* @chainable
*/
RadioButtonGroupField.prototype.setValueToControls = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].getValue() === value) {
this.controls[i].select();
}
}
return this;
};
/**
* @inheritdoc
*/
RadioButtonGroupField.prototype.getValueFromControls = function () {
var value = '',
i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].isSelected()) {
value += ' ' + this.controls[i].getValue();
}
}
return value.substr(1);
};
RadioButtonGroupField.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
RadioButtonGroupField.superclass.prototype.createHTML.call(this)
if (this.disabled) {
this.disable();
}
return this.html;
};
/**
* @method selection Option
* @param {Number|String} value This is the index or the name of the option that will be selected
* @chainable
*/
RadioButtonGroupField.prototype.selectOption = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (value == this.controls[i].label || value == i) {
this.setValue(this.controls[i].value);
return this;
}
}
return this;
};
RadioButtonGroupField.prototype.disableOption = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].value === value || this.controls[i].label === value || value === i) {
this.controls[i].disable(true);
return this;
}
}
throw new Error('the value send is not part of group Radio');
};
RadioButtonGroupField.prototype.enableOption = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].value === value || this.controls[i].label === value || value === i) {
this.controls[i].disable(false);
return this;
}
}
throw new Error('the value send is not part of group Radio');
};
RadioButtonGroupField.prototype.defineEvents = function () {
var that = this;
if (this.html && !this.eventsDefined) {
RadioButtonGroupField.superclass.prototype.defineEvents.call(this);
this.addEvent('click').listen(this.dom.labelTextContainer, function (e) {
if (that.controls.length) {
$(that.controls[0].html).find('input')[0].focus();
}
});
this.eventsDefined = true;
}
return this;
};
RadioButtonGroupField.prototype.isValid = function () {
var valid = true,
validator;
valid = valid && this.evalRequired();
if (!valid) {
$(this.controlTable).addClass('error');
return valid;
}
$(this.controlTable).removeClass('error');
for (validator in this.validators) {
if (this.validators.hasOwnProperty(validator)) {
valid = valid && this.validators[validator].isValid();
if (!valid) {
this.message.setText(this.validators[validator].errorMessage);
return valid;
}
}
}
return valid;
};
/**
* @method getOptions
* @ obtains under each option and configuration
* @chainable
*/
RadioButtonGroupField.prototype.getOptions = function () {
var i,
options = [],
option;
for (i = 0; i < this.controls.length; i += 1) {
option = {};
option["label"] = this.controls[i].label;
option["value"] = this.controls[i].value;
option["disabled"] = this.controls[i].disabled;
option["selected"] = this.controls[i].selected;
options.push(option);
}
return options;
};
RadioButtonGroupField.prototype.getValueFromControls = function () {
var value = '', i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].isSelected()) {
value = this.controls[i].getValue();
break;
}
}
this.value = value;
this.data.setValue(this.value);
return this.value;
};
PMUI.extendNamespace('PMUI.field.RadioButtonGroupField', RadioButtonGroupField);
if (typeof exports !== "undefined") {
module.exports = RadioButtonGroupField;
}
}());
(function () {
/**
* @class PMUI.field.DateTimeField
* @extends {PMUI.form.Field}
* This field is to manipulate the [PMI.control.DateTimeControl]
*
* example:
*
* @example
* var dateTimePicker;
* $(function() {
* dateTimePicker = new PMUI.field.DateTimeField(
* {
* label:'Calendar',
* helper: 'This is calendar Gregorian',
* value: new Date(),
* datetime : true,
* dateFormat: 'M dd yy',
* minDate: -90,
* maxDate: "+1y -1m -4d",
* firstDay: 1,
* months: {
* "january": {
* name: "Enero",
* shortname: "Ene"
* },
* "february": {
* name: "Febrero",
* shortname: "Feb"
* },
* "march": {
* name: "Marzo",
* shortname: "Mar"
* },
* "april": {
* name: "Abril",
* shortname: "Abr"
* },
* "may": "May",
* "june": "Junio",
* "july": "July",
* "august": "Agosto",
* "september": "Septiembre",
* "october": "Octubre",
* "november": "Noviembre",
* "december": "Diciembre"
* },
* days: {
* "sunday": {
* name: "Domingo",
* shortname: "Do"
* },
* "monday": {
* name: "Lunes",
* shortname: "Lu"
* },
* "tuesday": {
* name: "Martes",
* shortname: "Ma"
* },
* "wednesday": {
* name: "Miércoles",
* shortname: "Mi"
* },
* "thursday": {
* name: "Jueves",
* shortname: "Ju"
* },
* "friday": "Viernes",
* "saturday": "Sábado"
* }
* }
* );
* document.body.appendChild(dateTimePicker.getHTML());
* dateTimePicker.defineEvents();
* });
*
* @constructor Creates an instance of the class DateTimeField.
* @param {Object} options Initialization options.
* @cfg {Boolean} [datetime=false] If the [PMUI.control.DateTimeControl] You can see more about the configuration
* in {@link PMUI.control.DateTimeControl#cfg-datetime datetime}
* @cfg {String} [dateFormat="yy-mm-dd H:i:s"|"yy-mm-dd"],necessary to set the date format property of the control
* [PMUI.control.DateTimeControl]
* You can see more about the configuration in {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat}
* @cfg {Object} [months={"january": "January", "february": "February", "march": "March", "april": "April",
* "may": "May", "june": "June", "july": "July", "august": "August", "september": "September",
* "october": "October", "november": "November", "december": "December"}], A JSON object to set the names and
* shortnames for every month in year.
* You can see more about the configuration in {@link PMUI.control.DateTimeControl#cfg-months months}
* @cfg {Object} [days={"sunday": "Sunday","monday": "Monday","tuesday": "Tuesday","wednesday": "Wednesday",
* "thursday": "Thursday","friday": "Friday","saturday": "Saturday"}], A JSON object to set the name and shortname
* for every day of week.
* You can see more about the configuration in {@link PMUI.control.DateTimeControl#cfg-days days}
* @cfg {String|Number} [minDate=-365] A value which sets the min selectable date for the calendar. You can see
* more about the configuration in
* {@link PMUI.control.DateTimeControl#cfg-minDate minDate}
* @cfg {String|Number} [maxDate=365] A value which sets the max selectable date for the calendar.
* @cfg {Number} [firstDay=0] Sets the first day of week. You can use numbers from 0 to 6, 0 means Sunday, 1 means
* Monday and so on.
* @cfg {String} [returnFormat="UTC"] Specifies the date format that will be used for the returning date.
* The valid values are:
*
* - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
* - "@" or "timestamp", returns a date in timestamp format.
* - [any other valid format string], will return the date using the string as the dateformat. This string can
* contain any of the wilcards specified in the
* {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
*/
var DateTimeField = function (settings) {
DateTimeField.superclass.call(this, settings);
/**
* The default date format to be used in the returning date. Set by the {@link #cfg-returnFormat} config option
* and the {@link #method-setReturnFormat setReturnFormat()} method.
* @type {String}
*/
this.returnFormat = null;
DateTimeField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', DateTimeField);
DateTimeField.prototype.type = "DateTimeField";
DateTimeField.prototype.init = function (settings) {
var defaults = {
datetime: false,
dateFormat: settings && settings.datetime ? 'yy-mm-dd H:i:s' : 'yy-mm-dd',
months: {
"january": "January".translate(),
"february": "February".translate(),
"march": "March".translate(),
"april": "April".translate(),
"may": "May".translate(),
"june": "June".translate(),
"july": "July".translate(),
"august": "August".translate(),
"september": "September".translate(),
"october": "October".translate(),
"november": "November".translate(),
"december": "December".translate()
},
days: {
"sunday": "Sunday".translate(),
"monday": "Monday".translate(),
"tuesday": "Tuesday".translate(),
"wednesday": "Wednesday".translate(),
"thursday": "Thursday".translate(),
"friday": "Friday".translate(),
"saturday": "Saturday".translate()
},
minDate: -365,
maxDate: 365,
firstDay: 0,
returnFormat: 'UTC'
};
jQuery.extend(true, defaults, settings);
this.setDateFormat(defaults.dateFormat)
.setMonths(defaults.months)
.setDays(defaults.days)
.setMinDate(defaults.minDate)
.setMaxDate(defaults.maxDate)
.setFirstDay(defaults.firstDay)
.setReturnFormat(defaults.returnFormat)
.setValue(defaults.value)
.visibleDateTime(defaults.datetime);
};
/**
* Enables/disabled the calendar's [PMUI.control.DateTimeControl] time supporting.
* @param {Boolean} visible If it's true, then the time supporting is enabled, otherwise it's disabled.
* @chainable
*/
DateTimeField.prototype.visibleDateTime = function (visible) {
this.controls[0].visibleDateTime(visible);
return this;
};
/**
* Returns the [PMUI.control.DateTimeControl] maximum selectable date.
* @param {String} [format="UTC"] The format to applied to the returning date.
* @return {String} The maximum selectable date in string format.
*/
DateTimeField.prototype.getMaxDate = function (format) {
return this.controls[0].getMaxDate(format);
};
/**
* Returns the [PMUI.control.DateTimeControl] minimun selectable date.
* @param {String} [format="UTC"] The format to applied to the returning date.
* @return {String} The maximum selectable date in string format.
*/
DateTimeField.prototype.getMinDate = function (format) {
return this.controls[0].getMinDate(format);
};
/**
* Sets the date formatr to be used when you get the value from the object.
* @param {String} format The valid values are:
*
* - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
* - "@" or "timestamp", returns a date in timestamp format.
* - [any other valid format string], will return the date using the string as the dateformat. This string can
* contain any of the wilcards specified in the
* {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
* @chainable
*/
DateTimeField.prototype.setReturnFormat = function (format) {
if (typeof format === 'string') {
this.returnFormat = format;
} else {
throw new Error("setReturnFormat(): the parameter must be a string.");
}
return this;
};
/**
* Returns the day index of the first day of week.
* @return {Number} A number refering a day: 0 for Sunday, 1 for Monday and so on.
*/
DateTimeField.prototype.getFirstDay = function () {
return this.controls[0].getFirstDay();
};
/**
* Sets the value to the field's controls.
* @protected
* @param {String} value
* @param {String} utc
* @chainable
*/
DateTimeField.prototype.setValueToControls = function (value, utc) {
this.controls[0].setValue(value, utc);
return this;
};
/**
* Sets the field's value.
* @param {String} value
* @param {String} utc
*/
DateTimeField.prototype.setValue = function (value, utc) {
value = value || "";
if (value instanceof Date || typeof value === 'string' || typeof value === 'number') {
if (this.controls[0]) {
this.setValueToControls(value, utc);
this.value = this.controls[0].getValue(this.returnFormat);
this.data.setValue(this.value);
}
} else {
throw new Error("The setValue() method only accepts string values!");
}
return this;
};
/**
* Sets the first day of week.
* @param {Number} day Use 0 for Sunday, 1 for Monday, 2 for Tuesday and so on!.
*/
DateTimeField.prototype.setFirstDay = function (day) {
this.controls[0].setFirstDay(day);
return this;
};
/**
* Sets the format for the date to be displayed in the control's textbox control.
* You can see more about here [PMUI.control.DateTimeControl] in method
* {@link PMUI.control.DateTimeControl#setDateFormat setDateFormat}
**/
DateTimeField.prototype.setDateFormat = function (dateFormat) {
this.controls[0].setDateFormat(dateFormat);
return this;
};
/**
* Sets the minimum date the [PMUI.control.DateTimeControl] can accept as a selectable one.
* You can see more about here [PMUI.control.DateTimeControl] in method
* {@link PMUI.control.DateTimeControl#setMinDate setMinDate}
*/
DateTimeField.prototype.setMinDate = function (date) {
this.controls[0].setMinDate(date);
return this;
};
/**
* Sets the max date the [PMUI.control.DateTimeControl] can accept as a selectable one.
* You can see more about here in the class [PMUI.control.DateTimeControl] in method
{@link PMUI.control.DateTimeControl#setMaxDate setMaxDate}
*/
DateTimeField.prototype.setMaxDate = function (date) {
this.controls[0].setMaxDate(date);
return this;
};
/**
* Sets the months the [PMUI.control.DateTimeControl] names/shortnames to be used by the calendar.
* @param {Object} months An object with the same structure that the [PMUI.control.DateTimeControl]
* method {@link PMUI.control.DateTimeControl#setMonths setMonths}.
* @chainable
*/
DateTimeField.prototype.setMonths = function (months) {
this.controls[0].setMonths(months);
return this;
};
/**
* Sets the [PMUI.control.DateTimeControl] name/shortnames for the days to be used in the calendar.
* @param {Object} days A JSON object with the same structure than the [PMUI.control.DateTimeControl]
* method {@link PMUI.control.DateTimeControl#setDays setDays}
*/
DateTimeField.prototype.setDays = function (days) {
this.controls[0].setDays(days);
return this;
};
/**
* Returns the formated date.
* @return {String}
*/
DateTimeField.prototype.getFormatedDate = function () {
return this.controls[0].getFormatedDate();
};
/**
* Returns the field's value.
* @param {String} [format] Specifies the date format that will be used for the returning date. If it isn't
* specified, the {@link #property-returnFormat returnFormat} will be used instead.
*
* The valid values are:
*
* - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
* - "@" or "timestamp", returns a date in timestamp format.
* - [any other valid format string], will return the date using the string as the dateformat. This string can
* contain any of the wilcards specified in the
* {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
* @return {String}
*/
DateTimeField.prototype.getValue = function (format) {
return this.controls[0].getValue(format || this.returnFormat);
};
/**
* Sets the controls for the field.
* Since this is an abstract method, it must be implemented in its non-abstract subclasses
* @abstract
* @private
*/
DateTimeField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.DateTimeControl());
return this;
};
/**
* Updates the current value from the controls which are part of the field
* @chainable
*/
DateTimeField.prototype.updateValueFromControls = function () {
this.value = this.controls[0].getValue(this.returnFormat);
this.data.setValue(this.value);
return this;
};
PMUI.extendNamespace('PMUI.field.DateTimeField', DateTimeField);
if (typeof exports !== "undefined") {
module.exports = DateTimeField;
}
}());
(function () {
var TextAnnotationField = function (settings) {
TextAnnotationField.superclass.call(this, settings);
this.text = null;
this._textType = null;
this.text_Align = null;
this._labelTag = null;
TextAnnotationField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', TextAnnotationField);
TextAnnotationField.prototype.type = "TextAnnotationField";
TextAnnotationField.TEXT_TYPES = {
Plain: 0,
HTML: 1
};
TextAnnotationField.prototype.init = function (settings) {
var defaults = {
maxLength: 0,
text: "",
textType: TextAnnotationField.TEXT_TYPES.Plain,
text_Align: "left"
};
$.extend(true, defaults, settings);
this.setText(defaults.text);
this.setTextType(defaults.textType);
this.setText_Align(defaults.text_Align);
};
TextAnnotationField.prototype.setTextType = function (type) {
switch (type) {
case TextAnnotationField.TEXT_TYPES.Plain:
case TextAnnotationField.TEXT_TYPES.HTML:
this._textType = type;
break;
default:
throw new Error("setTextType(): Invalid argument.");
}
this.setText(this.text);
return this;
};
TextAnnotationField.prototype.setText = function (text) {
if (typeof text !== "string") {
throw new Error("setText(): The parameter is not valid");
}
this.text = text;
if (this.html) {
if (this._textType == TextAnnotationField.TEXT_TYPES.HTML) {
jQuery(this._labelTag).html(this.text);
} else {
jQuery(this._labelTag).text(this.text);
}
}
return this;
};
TextAnnotationField.prototype.setText_Align = function (value) {
if (typeof value === "string") {
this.text_Align = value;
} else {
throw new Error("the enter parameter is no valid");
}
return this;
};
TextAnnotationField.prototype.setValue = function () {
return this;
};
TextAnnotationField.prototype.setControls = function () {
return this;
};
TextAnnotationField.prototype.setHelper = function () {
return this;
};
TextAnnotationField.prototype.createHTML = function () {
var labelTag, textbefore, spanRequired, sp = " ";
if (this.html) {
return this.html;
}
TextAnnotationField.superclass.superclass.prototype.createHTML.call(this);
labelTag = PMUI.createHTMLElement('span');
labelTag.className = 'pmui-field-label';
labelTag.style.width = "100%";
labelTag.style.textAlign = this.text_Align;
this._labelTag = labelTag;
this.html.appendChild(labelTag);
this.setText(this.text);
return this.html;
};
TextAnnotationField.prototype.isValid = function () {
return true;
};
PMUI.extendNamespace('PMUI.field.TextAnnotationField', TextAnnotationField);
if (typeof exports !== "undefined") {
module.exports = TextAnnotationField;
}
}());
(function () {
/**
* @class PMUI.form.FormItemFactory
* @extend PMUI.util.Factory
* Extends the factory class to produce objects to be included in containers for forms.
*
* Its default products are:
*
* - {@link PMUI.form.FormPanel FormPanel} objects: using "panel".
* - {@link PMUI.field.TextField TextField} objects: using "text".
* - {@link PMUI.field.DropDownListField DropDownListField} objects: using "dropdown".
* - {@link PMUI.field.RadioButtonGroupField RadioButtonGroupField} objects: using "radio".
* - {@link PMUI.field.CheckBoxGroupField CheckBoxGroupField} objects: using "checkbox".
* - {@link PMUI.field.TextAreaField TextAreaField} objects: using "textarea".
* - {@link PMUI.field.DateTimeField DateTimeField} objects: using "datetime".
*
* @constructor
* Creates a new instance od the class
*/
var FormItemFactory = function () {
FormItemFactory.superclass.call(this);
FormItemFactory.prototype.init.call(this);
};
PMUI.inheritFrom('PMUI.util.Factory', FormItemFactory);
FormItemFactory.prototype.init = function () {
var defaults = {
products: {
"field": PMUI.form.Field,
"panel": PMUI.form.FormPanel,
"text": PMUI.field.TextField,
"password": PMUI.field.PasswordField,
"dropdown": PMUI.field.DropDownListField,
"radio": PMUI.field.RadioButtonGroupField,
"checkbox": PMUI.field.CheckBoxGroupField,
"textarea": PMUI.field.TextAreaField,
"datetime": PMUI.field.DateTimeField,
"optionsSelector": PMUI.field.OptionsSelectorField,
"upload": PMUI.field.UploadField,
"buttonField": PMUI.field.ButtonField,
"annotation": PMUI.field.TextAnnotationField
},
defaultProduct: "panel"
};
this.setProducts(defaults.products)
.setDefaultProduct(defaults.defaultProduct);
};
PMUI.extendNamespace('PMUI.form.FormItemFactory', FormItemFactory);
}());
(function () {
/**
* @class PMUI.form.FormPanel
* @extends PMUI.core.Panel
* Class to handle form containers for {@link PMUI.form.Field Field objects},
* {@link PMUI.form.FormPanel FormPanel objects}, and {@link PMUI.form.TabPanel TabPanel objects}.
*
* @example
* var p;
* $(function() {
* p = new PMUI.form.FormPanel({
* width: 600,
* height: 130,
* fieldset: true,
* legend: "my fieldset panel",
* items: [
* {
* pmType: "text",
* label: "Name",
* id: "123",
* value: "",
* placeholder: "insert your name",
* name: "name"
* },{
* pmType: "text",
* label: "Last name",
* value: "",
* placeholder: "your lastname here asshole!",
* name: "lastname"
* }, {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "E-mail",
* value: "",
* name: "email"
* },{
* pmType: "text",
* label: "Phone",
* value: "555",
* name: "phone"
* }
* ]
* }
* ],
* layout: "vbox"
* });
* document.body.appendChild(p.getHTML());
* console.log("haha");
* });
*
* @cfg {PMUI.form.Form} [form=null] The {@link PMUI.form.Form Form} the object belongs to.
* @cfg {Array} [items=[]] The array with the items to be contained by the object.
* //example
* {
* ......
* items: [
* {
* pmType: "text",
* label: "Name",
* id: "123",
* value: "",
* placeholder: "insert your name",
* name: "name"
* }, {
* pmType: "text",
* label: "Last name",
* value: "",
* placeholder: "your lastname here asshole!",
* name: "lastname"
* }, {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "E-mail",
* value: "",
* name: "email"
* },{
* pmType: "text",
* label: "Phone",
* value: "555",
* name: "phone"
* }
* ]
* }
* ],
* ......
* });
* @cfg {Boolean} [fieldset=false] If the panel will apply the fieldset behavior.
* @cfg {String} [legend=""] The text for the legend to show in case the object applies the fieldset behavior.
* (only applicable if the the object will apply the fieldset behavior).
* @cfg {Number} [legendSize=12] The size for the text in the fieldset's legend.
*/
var FormPanel = function (settings) {
/**
* @property {PMUI.util.ArrayList} fields
* Object that contains all the object's direct children that are {@link PMUI.form.Field Field} objects.
* @private
*/
this.fields = new PMUI.util.ArrayList();
/**
* @property {PMUI.util.ArrayList} formPanels
* Object that contains all the object's direct children that are {@link PMUI.form.FormPanel FormPanel}
objects.
* @private
*/
this.formPanels = new PMUI.util.ArrayList();
/**
* @property {PMUI.util.ArrayList} tabPanels
* Object that contains all the object's direct children that are {@link PMUI.form.TabPanel TabPanel} objects.
* @private
*/
this.tabPanels = new PMUI.util.ArrayList();
FormPanel.superclass.call(this, settings);
/**
* @property {Boolean} [fieldset]
* If the panel has the fieldset behavior.
* @readonly
*/
this.fieldset = null;
/**
* @property {PMUI.form.Form} [form=null] The {@link PMUI.form.Form Form} the object belongs to.
*/
this.form = null;
/**
* @property {String} [legend=""] The text for the legend to show in case of the fieldset behavior
will be applied.
*/
this.legend = null;
/**
* @property {String} [legendElement=""] The HTML element for containt the legend to show in case of the
fieldset behavior will be applied.
*/
this.legendElement = null;
/**
* @property {Number} [fieldsetLegendSize] The size for the text showed in the fieldset legend.
* @readonly
*/
this.legendSize = null;
this.onChange = null;
this._onAvailabilityChange = null;
FormPanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Panel', FormPanel);
FormPanel.prototype.type = 'FormPanel';
FormPanel.prototype.init = function (settings) {
var defaults = {
form: null,
fieldset: false,
legend: "",
padding: (settings && settings.fieldset) ? "5px 10px" : "10px",
borderWidth: (settings && settings.fieldset) ? 1 : "",
legendSize: 12,
onChange: null,
onAvailabilityChange: null
};
jQuery.extend(true, defaults, settings);
this.setPadding(defaults.padding)
.setBorderWidth(defaults.borderWidth)
.setForm(defaults.form)
.setOnAvailabilityChange(defaults.onAvailabilityChange);
this.form = defaults.form;
this.fieldset = defaults.fieldset;
this.legend = defaults.legend;
this.legendSize = defaults.legendSize;
this.onChange = defaults.onChange;
if (this.fieldset) {
this.setElementTag("fieldset");
}
};
/**
* Sets the function to be called when the availability on any contained item changes.
* @param handler
*/
FormPanel.prototype.setOnAvailabilityChange = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error("onAvailabilityChange(): The parameter must be a function or NULL.");
}
this._onAvailabilityChange = handler;
return this;
};
/**
* Returns a field from the form panel.
* @param {String} name The name of the field to find.
* @return {PMUI.form.Field|null}
*/
FormPanel.prototype.getField = function (name) {
var i,
items = this.getItems(),
field = null;
for (i = 0; i < items.length; i += 1) {
if (items[i] instanceof PMUI.form.Field) {
if (items[i].getName() === name) {
return items[i];
}
} else {
field = items[i].getField(name);
if (field) {
return field;
}
}
}
return field;
};
/**
* Returns an array of items depending on the parameters the method receives.
* @param {String} [filter=undefined]
* A string which specifiest the bunch of items that will be returned.
* It defaults to {undefined}, that means that only the direct child items will be in the returned array.
*
* Alternatively this param can take one of the following values:
*
* - 'fields': it will return only the child {@link PMUI.form.Field Field} items.
* - 'formPanels': it will return only the child {@link PMUI.form.FormPanel FormPanel} items.
* - 'tabPanels': it will return only the child {@link PMUI.form.TabPanel TabPanel} items.
*
* @param {Boolean} [includeChildren=false]
*
* If the value is evaluated as false only the direct child items will be returned,
* otherwise additionaly will be added the items for all child items.
*
* Note: This parameter only has effect when the [filter] parameter is provided.
* @return {Array}
*/
FormPanel.prototype.getItems = function (filter, includeChildren) {
var res = [],
items,
size,
i,
targetArray,
the_class;
switch (filter) {
case 'fields':
the_class = PMUI.form.Field;
targetArray = this.fields;
break;
case 'formPanels':
the_class = PMUI.form.FormPanel;
targetArray = this.formPanels;
break;
case 'tabPanels':
the_class = PMUI.form.TabPanel;
targetArray = this.tabPanels;
break;
default:
return FormPanel.superclass.prototype.getItems.call(this);
}
if (includeChildren) {
if (the_class) {
items = this.items.asArray();
size = items.length;
for (i = 0; i < size; i += 1) {
if (items[i] instanceof the_class) {
res.push(items[i]);
} else {
res = res.concat(items[i].getItems(filter, true));
}
}
} else {
throw new Error('getItems(): The valid values for the "filter" parameter are: "fields",' +
'"formPanels" or "tabPanels", received: ' + filter);
}
} else {
res = targetArray.asArray().slice(0);
}
return res;
};
/**
* Sets the form the form panel belongs to.
* @param {PMUI.form.Form} form
* @chainable
*/
FormPanel.prototype.setForm = function (form) {
if (form instanceof PMUI.form.Form) {
this.form = form;
}
return this;
};
/**
* Sets the {@link PMUI.util.Factory Factory} object for the FormPanel
* @param {PMUI.util.Factory|Object} factory It can be a {@link PMUI.util.Factory Factory} object,
or a JSON object.
* If it is a JSON object then a {@link PMUI.form.FormItemFactory FormItemFactory}
* is created using the JSON object as the config options.
* @chainable
* @private
*/
FormPanel.prototype.setFactory = function (factory) {
if (factory instanceof PMUI.util.Factory) {
this.factory = factory;
} else {
this.factory = new PMUI.form.FormItemFactory(factory);
}
return this;
};
/**
* Clear all the items.
* @chainable
*/
FormPanel.prototype.clearItems = function () {
FormPanel.superclass.prototype.clearItems.call(this);
if (this.fields) {
this.fields.clear();
this.formPanels.clear();
this.tabPanels.clear();
}
return this;
};
/**
* Sets the text for the legend to be displayed just in case of the fieldset behavior is beign applied.
* @param {String} legend
* @chainable
*/
FormPanel.prototype.setLegend = function (legend) {
if (typeof legend === 'string') {
this.legend = legend;
if (this.legendElement) {
this.legendElement.textContent = legend;
}
} else {
throw new Error("setLegend(): this method accepts string values as only parameter.");
}
return this;
};
/**
* Creates the HTML element for the FormPanel.
* @chainable
*/
FormPanel.prototype.createHTML = function () {
var legendElement,
html;
if (this.html) {
return this.html;
}
FormPanel.superclass.prototype.createHTML.call(this);
if (this.fieldset) {
legendElement = PMUI.createHTMLElement('legend');
legendElement.textContent = this.legend;
legendElement.className = 'pmui-formpanel-legend';
legendElement.style.fontSize = this.legendSize + "px";
this.legendElement = legendElement;
jQuery(this.html).prepend(legendElement);
}
return this.html;
};
/**
* Returns the object's usable height.
* @return {Number|String}
*/
FormPanel.prototype.getUsableHeight = function () {
var height = FormPanel.superclass.prototype.getUsableHeight.call(this);
if (isNaN(height)) {
return height;
}
if (this.fieldset) {
height -= this.legendSize;
}
return height;
};
/**
* Sets the internal handler function for the onChange Event
* @return {Function} The handler function.
* @private
*/
FormPanel.prototype.onChangeHandler = function () {
var that = this;
return function (a, b, c) {
var target,
newValue,
previousValue;
if (this instanceof PMUI.form.Field) {
target = this;
newValue = a;
previousValue = b;
} else {
target = a;
newValue = b;
previousValue = c;
}
if (typeof that.onChange === 'function') {
that.onChange(target, newValue, previousValue);
}
if (this instanceof PMUI.form.Field && this.form) {
(this.form.onChangeHandler())(target, newValue, previousValue);
}
};
};
FormPanel.prototype._getOnAvailabilityChangeHandler = function () {
var that = this;
return function (panel, enabled) {
if (typeof that._onAvailabilityChange === 'function') {
that._onAvailabilityChange(panel, enabled);
}
};
};
/**
* Add an item to the object.
* @param {Object|PMUI.form.Field|PMUI.form.FormPanel|PMUI.form.TabPanel} item
* The item parameter can be:
*
* - A JSON object, in that case, it must have at least a "pmType" property
* which can have any of the values specifed in the {@link PMUI.form.FormItemFactory FormItemFactory
documentation}.
* - A {@link PMUI.form.Field Field} object.
* - A {@link PMUI.form.FormPanel FormPanel} object.
* - A {@link PMUI.form.TabPanel TabPanel} object.
*/
FormPanel.prototype.addItem = function (item, index) {
var itemToBeAdded;
if (this.factory) {
if (typeof item === 'object' && !item.layout) {
item.layout = (this.layout && this.layout.type.toLowerCase()) || "vbox";
}
if (this.factory.isValidClass(item) || this.factory.isValidName(item.pmType)) {
itemToBeAdded = this.factory.make(item);
} else {
throw new Error('Invalid item to add.');
}
}
if (itemToBeAdded) {
itemToBeAdded.setForm(this.form);
itemToBeAdded.setDisplay("inline-block");
if (itemToBeAdded instanceof PMUI.form.FormPanel) {
itemToBeAdded.setOnAvailabilityChange(this._getOnAvailabilityChangeHandler());
this.formPanels.insert(itemToBeAdded);
} else if (itemToBeAdded instanceof PMUI.form.Field) {
this.fields.insert(itemToBeAdded);
if (this.form) {
this.form.data.addItem(itemToBeAdded.data);
}
}
FormPanel.superclass.prototype.addItem.call(this, itemToBeAdded, index);
}
return this;
};
/**
* Removes an item from the form panel.
* @param {String|Number|PMUI.core.Element} item
* It can be:
* - a String: in this case this string must be the id of the element to be removed.
* - a Number: in this case this number must be the index of the element to be removed.
* - a {@link PMUI.core.Element Element} object: in this case it will be removed
* (if it is contained by the current form panel).
* @chainable
*/
FormPanel.prototype.removeItem = function (item) {
var itemToRemove;
if (item instanceof PMUI.core.Element) {
itemToRemove = item;
} else {
if (typeof item === 'string') {
itemToRemove = this.items.find("id", item.id);
} else if (typeof item === 'number') {
itemToRemove = this.items.get(item);
}
}
if (itemToRemove) {
if (itemToRemove instanceof PMUI.form.Field) {
if (this.form) {
this.form.data.removeItem(itemToRemove.data);
}
}
FormPanel.superclass.prototype.removeItem.call(this, itemToRemove);
}
return this;
};
/**
* Returns true if the validation passes otherwise it returns false.
* @return {Boolean}
*/
FormPanel.prototype.isValid = function () {
var items = this.items.asArray(),
i,
valid = true;
for (i = 0; i < items.length; i += 1) {
if (items[i].disabled) {
continue;
}
valid = valid && items[i].isValid();
if (!valid) {
if (items[i] instanceof PMUI.form.Field) {
items[i].setFocus();
}
return valid;
}
}
return valid;
};
FormPanel.prototype.disable = function () {
FormPanel.superclass.prototype.disable.call(this);
if (typeof this._onAvailabilityChange === 'function') {
this._onAvailabilityChange(this, false);
}
return this;
};
FormPanel.prototype.enable = function () {
FormPanel.superclass.prototype.enable.call(this);
if (typeof this._onAvailabilityChange === 'function') {
this._onAvailabilityChange(this, true);
}
return this;
};
PMUI.extendNamespace('PMUI.form.FormPanel', FormPanel);
if (typeof exports !== "undefined") {
module.exports = FormPanel;
}
}());
(function () {
/**
* @class PMUI.form.Form
* @extends PMUI.core.Panel
*
* Usage example:
*
* @example
* var f;
* $(function() {
* f = new PMUI.form.Form({
* onSubmit: function() {
* console.log("submitting...");
* },
* onChange: function(field, newValue, previousValue) {
* console.log("The field " + field.getName() + " has changed from \"" + previousValue + "\" to \"" + newValue + "\"");
* },
* items: [
* {
* pmType: "text",
* label: "Name",
* id: "123",
* value: "",
* placeholder: "insert your name",
* name: "name",
* helper: "Introduce your name",
* required : true,
* controlsWidth: 200
* },
* {
* pmType: "datetime",
* visible : false,
* label: "birth date",
* name: "birthdate"
* },
* {
* pmType: "dropdown",
* label: "age",
* options: [
* {
* label: "",
* value: ""
* },
* {
* label: "from 0 to 7 years old",
* value: "0-7"
* },
* {
* label: "from 8 to 13 years old",
* value: "8-13"
* },
* {
* label: "from 14 to 19 years old",
* value: "14-19"
* },
* {
* label: "from 20 to 30 years old",
* value: "20-30"
* },
* {
* label: "from 31 to 45 years old",
* value: "31-45"
* },
* {
* label: "older than 45",
* value: "46+"
* }
* ],
* name: "age",
* helper: "Select one of the options",
* required: true,
* onChange: function() {
* console.log("The value has been changed");
* },
* controlsWidth: 100
* },
* {
* pmType: "radio",
* label: "Gender",
* value: "m",
* name: "gender",
* required: true,
* options: [
* {
* label: "Male",
* value: "m"
* },
* {
* label: "Female",
* value: "f"
* }
* ]
* },
* {
* pmType: "text",
* label: "E-mail",
* value: "",
* name: "email",
* helper: "you email here",
* required: true,
* validators: [
* {
* pmType: "regexp",
* criteria: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
* errorMessage: "Please enter a valid email address"
* }
* ]
* },
* {
* pmType: "checkbox",
* label: "Things you like to do",
* value: "[\"music\"]",
* name: "hobbies",
* helper: "Check up to 3 options",
* options: [
* {
* label: "Music",
* value: "music"
* },
* {
* label: "Programming",
* value: "programming"
* },
* {
* label: "Bike ridding",
* value: "bike"
* },
* {
* label: "Gastronomy",
* value: "gastronomy"
* },
* {
* label: "Movies",
* value: "movies"
* }
* ],
* validators: [],
* required: true
* },
* {
* pmType: "panel",
* fieldset: true,
* legend: "Another Info",
* layout: "vbox",
* items: [
* {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "Country",
* value: "Bolivia",
* name: "country"
* },
* {
* pmType: "text",
* label: "City",
* name: "city"
* }
* ]
* },
* {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "Address",
* name: "address",
* placeholder: "Your address here"
* },
* {
* pmType: "text",
* label: "Zip Code",
* name: "zip"
* }
* ]
* },
* {
* pmType: "panel",
* layout: 'vbox',
* items: [
* {
* pmType: "textarea",
* label: "About you",
* name: "about"
* }
* ]
* }
* ]
* }
* ],
* footerItems: [
* {
* pmType: "button",
* text: "Submit",
* handler: function() {
* f.submit();
* console.log("submitting form...");
* }
* }, {
* pmType: "button",
* text: "Reset",
* handler: function() {
* f.reset();
* }
* }
* ]
* });
* document.body.appendChild(f.getHTML());
* });
*
* The example above will generate a form with 8 fields:
* Name, Last Name, E-mail, Phone, Country, City, Address, Zip Code.
* The first four ones are required and each one has a helper.
* The email and Phone fields also have a validator for control the input.
*
* The latest four ones are contained by a fieldset.
*
* @cfg {String} title The title for the form.
* @cfg {name} [name=] The name for the form.
* @cfg {String} [encType="application/x-www-form-urlencoded"] The value for the form's encType attribute .
* @cfg {Array} [items=[]] An array in which every element can be
* a JSON Object or a supported object by Form:
*
* - In case to be a JSON object, it should includes a "pmType" property with a valid value.
* The valid values for the pmType property are specified in the
* {@link PMUI.form.FormItemFactory FormItemFactory documentation}.
*
* - In case of objects, use an object that the {@link PMUI.form.FormItemFactory FormItemFactory} can produce.
*
* Example:
* var form, items;
*
* items = [
* {
* pmType: "text",
* label: "Name",
* id: "123",
* value: "",
* placeholder: "insert your name",
* name: "name",
* helper: "Introduce your name",
* required: true
* },
* {
* pmType: "text",
* label: "Last name",
* value: "",
* placeholder: "your lastname here asshole!",
* name: "lastname",
* helper: "Introduce your lastname",
* required: true
* }
* ];
*
* form = new PMUI.form.Form({
* name: "My form",
* items: items
* });
*
* Depending on the type of element you add, it can contain a nested items property to define its child items:
*
* f = new PMUI.form.Form({
* items: [
* .......
* {
* pmType: "panel",
* fieldset: true,
* legend: "Another Info",
* layout: "vbox",
* items: [
* {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "Country",
* value: "Bolivia",
* name: "country"
* },
* {
* pmType: "text",
* label: "City",
* name: "city"
* }
* ]
* },
* {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "Address",
* name: "address",
* placeholder: "Your address here"
* },
* {
* pmType: "text",
* label: "Zip Code",
* name: "zip"
* }
* ]
* }
* ]
* }
* ]
* });
*
* As you can see in the example above, only the container type objects can have an "item" property.
*
* @cfg {Number} [fontSize=12] The size to be use for the font in the form.
* @cfg {Number} [width=600] The width for the form.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
* @cfg {Number} [height=400] The height for the form.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
* @cfg {Boolean} [visibleHeader=true] If the header is visible or not.
* @cfg {String} [layout='vbox'] The layout type to apply to the form.
* @cfg {Array} [buttons=[]] An array in which each element is JSON Object or a
* {@link Form.ui.Button Button object}
* @deprecated This config option will be removed soon, please use the {@link #cfg-footerItems footerItems} config
* option instead.
* @cfg {Function} [onChange=null] A callback function to be called everytime a form's field changes.
* This callback function will be called in the current object context and will receive three parameters:
*
* - The field that changed.
* - The new field's value.
* - The previous field's value.
*
* var f = new PMUI.form.Form({
* onChange: function(field, newValue, previousValue) {
* console.log("Action in the form named \"" + this.getName() + "\":");
* console.log("The field " + field.getName() + " has changed from \"" + previousValue +
"\" to \"" + newValue + "\"");
* },
* ......
* };
*
* @cfg {Function} [onSubmit=null] A callback function to be called when the form is submitted.
* This callback function will be called in the form object context.
*
* var f = new PMUI.form.Form({
* onSubmit: function() {
* console.log("Submitting the form named \"" + this.getName() + "\"...");
* },
* ......
* };
* @cfg {Array} [footerItems=[]] Sets the elements in the window footer, this elements can be instances of Button
* and/or instances of Label. The value for this config option must be an array in which each element can be:
*
* - An object literal, in this case the object literal must have the property "pmType" with its value set to
* "button" (if you want the element be a {@link PMUI.ui.Button Button}) or "label" (if you want the element be a
* {@link PMUI.ui.TextLabel Label}). Optionally you can add the respective config options for each case.
*
* - A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of
* {@link PMUI.ui.TextLabel Label}.
* @cfg {Function} [onLoad=null] The callback function to be executed when the form is ready to use. for info about
* the parameters received by the callback please read the {@link #event-onLoad onLoad event} documentation.
* @cfg {Srting} [buttonPanelPosition='bottom'] The position for the form's footer. It can be 'bottom' or 'top'.
*/
var Form = function (settings) {
Form.superclass.call(this, settings);
/**
* The name for the form
* @type {String}
* @readonly
*/
this.name = null;
/**
* The form's encType property
* @type {String}
* @readonly
*/
this.encType = null;
/**
* The text for the form's title.
* @type {String}
* @readonly
*/
this.title = null;
/**
* The size for the font in the form.
* @type {Number}
* @readonly
*/
this.fontSize = null;
/**
* If the header is visible or not.
* @type {Boolean}
* @readonly
*/
this.visibleHeader = null;
/**
* The callback function to be called when a form's field changes.
* @type {Function}
*/
this.onChange = null;
/**
* The callback functionto be called when the form is submitted.
* @type {Function}
*/
this.onSubmit = null;
/**
* The form's data object.
* @type {PMUI.data.DataSet}
* @private
*/
this.data = null;
/**
* The DOM object that plays the role of title container in the form.
* @type {HTMLElement}
* @private
*/
this.header = null;
/**
* The form footer.
* @type {PMUI.panel.ButtonPanel}
* @private
*/
this.footer = null;
/**
* The height for the footer area in the Form
* @type {Number}
* @readonly
*/
this.footerHeight = null;
/**
* @event onLoad
* Fired when the form is ready to use.
* @param {PMUI.form.Form} form The form.
*/
this.onLoad = null;
/**
* If the form is dirty.
* @type {Boolean}
*/
this.dirty = null;
this._requiredFieldNotification = null;
/**
* The position for the form's footer.
* @type {String}
*/
this.buttonPanelPosition = null;
this.dependencies = {};
this.alignmentButtons = null;
Form.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Panel', Form);
Form.prototype.type = 'PMForm';
Form.prototype.family = 'PMForm';
Form.prototype.init = function (settings) {
var defaults = {
title: "Untitled form",
name: this.id,
encType: "application/x-www-form-urlencoded",
items: [],
fontSize: 12,
width: 600,
height: 400,
visibleHeader: true,
layout: 'vbox',
buttons: [],
footerItems: [],
buttonPanelPosition: 'bottom',
onChange: null,
onSubmit: null,
onLoad: null,
alignmentButtons: 'right'
};
$.extend(true, defaults, settings);
this.panel = new PMUI.form.FormPanel({
form: this,
width: defaults.width,
layout: defaults.layout,
style: {
cssProperties: {
"overflow": "auto"
}
},
onChange: this.onChangeHandler()
});
this.footer = new PMUI.panel.ButtonPanel({
style: {
cssClasses: ['pmui-form-footer']
}
});
this.data = new PMUI.data.DataSet();
this.buttons = new PMUI.util.ArrayList();
this.visibleHeader = defaults.visibleHeader;
this.onChange = defaults.onChange;
this.onSubmit = defaults.onSubmit;
this.setLayout(defaults.layout)
.setFontSize(defaults.fontSize)
.setWidth(defaults.width)
.setTitle(defaults.title)
.setName(defaults.name)
.setEncType(defaults.encType)
.setItems(defaults.items)
.setOnLoadHandler(defaults.onLoad)
.setButtonPanelPosition(defaults.buttonPanelPosition)
.setAlignmentButtons(defaults.alignmentButtons);
//TODO tell the guys about the deprecated methods
if (defaults.footerItems && defaults.footerItems.length) {
this.setFooterItems(defaults.footerItems);
} else {
this.setButtons(defaults.buttons);
}
if (typeof this.onLoad === 'function') {
this.onLoad(this);
}
};
Form.prototype.updateDependencies = function () {
var dependencies = {},
i,
j,
fields = this.getFields(),
dependent,
dependents;
for (i = 0; i < fields.length; i += 1) {
dependents = fields[i].dependentFields;
for (j = 0; j < dependents.length; j += 1) {
if (this.getField(dependents[j])) {
if (!dependencies[dependents[j]]) {
dependencies[dependents[j]] = [];
}
dependencies[dependents[j]].push(fields[i]);
}
}
}
this.dependencies = dependencies;
return this;
};
/**
* Sets the position for the form's footer.
* @param {String} position A string with the text 'bottom' or 'top', this sets the position for the form's footer.
*/
Form.prototype.setButtonPanelPosition = function (position) {
if (position === 'top' || position === 'bottom') {
this.buttonPanelPosition = position;
if (this.html) {
if (position === 'top') {
this.html.insertBefore(this.footer.html, this.panel.html);
} else {
this.html.appendChild(this.footer.html);
}
}
}
return this;
};
/**
* Sets the callback to be executed when the {@link #event-onLoad onLoad event} is fired.
* @param {Function|null} handler The callback function or the null constant, in the latter case no callback
* function will be executed.
* @chainable
*/
Form.prototype.setOnLoadHandler = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error('setOnLoadHandler(): the parameter must be a function or null.');
}
this.onLoad = handler;
return this;
}
/**
* Removes an iten from the form's footer.
* @param {PMUI.core.Element|String|Number} item It can be a string (id of the child to remove),
a number (index of the child to remove) or a {Element} object.
* @chainable
*/
Form.prototype.removeFooterItem = function (item) {
this.footer.removeItem(item);
return this;
};
/**
* Removes all the items in the form's footer.
* @chainable
*/
Form.prototype.clearFooterItems = function () {
this.footer.clearItems();
return this;
};
/**
* Adds an item into the forms footer.
* @param {Object|PMUI.ui.Button|PMUI.ui.Button} item It can be:
*
* - An object literal, in this case it can have the config options for create a {@link PMUI.ui.Button Button} or a
* {@link PMUI.ui.TextLabel Label}, additionally it must include the respective pmType ('button' for Button and 'label'
* for Label).
*
* -A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of {@link
* PMUI.ui.TextLabel Label}.
*/
Form.prototype.addFooterItem = function (item) {
this.footer.addItem(item);
this.showFooter();
return this;
};
/**
* Sets the items to be shown in the form's footer.
* @param {Array} items An array in which each element can be:
*
* - An object literal, in this case it can have the config options for create a {@link PMUI.ui.Button Button} or a
* {@link PMUI.ui.TextLabel Label}, additionally it must include the respective pmType ('button' for Button and 'label'
* for Label).
*
* -A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of {@link
* PMUI.ui.TextLabel Label}.
*/
Form.prototype.setFooterItems = function (items) {
var i;
if (!jQuery.isArray(items)) {
throw new Error('setFooterItems(): The parameter must be an array.');
}
this.clearFooterItems();
this.hideFooter();
for (i = 0; i < items.length; i += 1) {
this.addFooterItem(items[i]);
}
return this;
};
/**
* The method which sets the internal callback function for the onChange event.
* @return {Function}
* @private
*/
Form.prototype.onChangeHandler = function () {
var that = this;
return function (field, newValue, previousValue) {
if (field.initialValue === newValue) {
that.dirty = false;
} else {
that.dirty = true;
}
if (typeof that.onChange === 'function') {
that.onChange(field, newValue, previousValue);
}
};
};
/**
* Sets the form to dirty/clean.
* @param {Boolean} dirty True to set the form to dirty, False to set it to clean.
* @chainable
*/
Form.prototype.setDirty = function (dirty) {
this.dirty = !!dirty;
return this;
};
/**
* Check if the form is dirty
* @return {Boolean} Returns true if the for is dirty, otherwise it returns false.
*/
Form.prototype.isDirty = function () {
return this.dirty;
};
/**
* Removes any button previously added to the form.
* @param {String|Number|PMUI.ui.Button} button
* This param can be:
* - String: in that case the string must be the id of the button to remove.
* - Number: in that case the number is evaluated as the index of the button to remove.
* - Button: the button to remove.
* @chainable
* @deprecated This method will be removed soon, please use the {@link #method-removeFooterItem removeFooterItem()}
* method instead.
*/
Form.prototype.removeButton = function (button) {
return this.removeFooterItem(button);
};
/**
* Remove all the buttons from the form.
* @chainable
* @deprecated This method will be removed soon, please use the {@link #method-clearFooterItems clearFooterItems()}
* method instead.
*/
Form.prototype.clearButtons = function () {
return this.clearFooterItems();
};
/**
* Adds a button into the form. The button will be added to the form's footer.
* @param {Object|PMUI.ui.Button} button
* It can be a:
*
* - {@link PMUI.ui.Button Button} object.
* - JSON Object: use the same JSON structure for create {@link PMUI.ui.Button Button} objects.
*
* @chainable
* @deprecated This method will be removed soon, please use the {@link #method-addFooterItem addFooterItem()}
* method instead.
*/
Form.prototype.addButton = function (button) {
return this.addFooterItem(button);
};
/**
* Sets the buttons for the form. They will be added to the footer.
* @param {Array} buttons An array in which each element is a JSON object or a {@link PMUI.ui.Button Button}.
* @chainable
* @deprecated This method will be removed soon, please use the {@link #method-setFooterItems setFooterItems()}
* method instead.
*/
Form.prototype.setButtons = function (buttons) {
return this.setFooterItems(buttons);
};
/**
* Updates the dimensions for the form's areas.
* @chainable
*/
Form.prototype.updateDimensions = function () {
var headerHeight = 0,
footerHeight = 0,
bodyHeight;
if (!this.panel) {
return this;
}
if (this.getHeight() !== 'auto') {
if (this.visibleHeader && this.title) {
headerHeight = 2 * this.fontSize;
}
if (this.footer.getItems().length) {
footerHeight = (1.7 * this.fontSize) + 12;
}
bodyHeight = this.getHeight() - headerHeight - footerHeight;
}
return this;
};
/**
* Sets the form's height.
* @param {Number|String} height it can be a number or a string.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
* @chainable
*/
Form.prototype.setHeight = function (height) {
Form.superclass.prototype.setHeight.call(this, height);
this.updateDimensions();
return this;
};
/**
* Sets the form's width.
* @param {Number|String} width it can be a number or a string.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
* @chainable
*/
Form.prototype.setWidth = function (width) {
Form.superclass.prototype.setWidth.call(this, width);
this.updateDimensions();
return this;
};
/**
* Sets the size for the font toi be used in the form.
* @param {Number} fontSize
*/
Form.prototype.setFontSize = function (fontSize) {
if (typeof fontSize === 'number') {
this.fontSize = fontSize;
this.style.addProperties({
"font-size": fontSize + "px"
});
this.updateDimensions();
} else {
throw new Error("setFontSize(): this method only accepts a number as parameter.");
}
return this;
};
/**
* Returns a field from the form.
* @param {String} name The name of the field to find.
* @return {PMUI.form.Field|null}
*/
Form.prototype.getField = function (name) {
return this.panel.getField(name);
};
/**
* Returns all the fields contained by the form.
* @return {Array} An array in which each element is a {PMUI.form.Field}.
*/
Form.prototype.getFields = function () {
return this.panel.getItems("fields", true);
};
/**
* Returns the items from the form's body.
* @return {Array} An array with all the form's items.
*/
Form.prototype.getItems = function () {
return this.panel.getItems();
};
/**
* Sets the layout mode for the form's root panel.
* @param {String} layout it can take one of the following values:
*
* - "vbox" for vertical positioning.
* - "hbox" for horizontal positioning.
* - "box" without layout.
*/
Form.prototype.setLayout = function (layout) {
var factory;
if (this.panel) {
factory = new PMUI.layout.LayoutFactory();
this.panel.layout = factory.make(layout);
this.panel.layout.setContainer(this.panel);
if (this.html) {
this.panel.layout.applyLayout();
}
}
return this;
};
/**
* Adds an item to the form's root panel. It only creates one of
* the {@link PMUI.form.FormItemFactory FormItemFactory} supported products.
* @param {String|Object|PMUI.form.FormPanel|PMUI.form.Field} item
* It can accept one of the following data types:
*
* - a String: one of the supported pmTypes by the {@link PMUI.form.FormItemFactory FormItemFactory} object.
* - a JSON Object: use the JSON structure the object's constructor needs, additionally add the pmType property,
* you can use any of the pmTypes supported by the {@link PMUI.form.FormItemFactory FormItemFactory}.
* - a {@link PMUI.form.FormPanel FormPanel} object.
* - a{@link PMUI.form.Field Field} object
*/
Form.prototype.addItem = function (item) {
if (this.panel) {
this.panel.addItem(item);
this.updateDependencies();
}
return this;
};
/**
* Set items for the form's root panel.
* @param {Array} items An array in which each element can be one of the accepted parameters
* in the {@link PMUI.form.Form#addItem addItem() method}.
* @chainable
*/
Form.prototype.setItems = function (items) {
if (this.panel) {
this.panel.setItems(items);
this.updateDependencies();
}
return this;
};
/**
* Returns all the form's direct items.
* @param {String|Number} id If the parameter is a string then
* it will be take as the id for the element to find and return,
* but if the element is a Number it will return the object with that
* index position.
* @returns {Object}
*/
Form.prototype.getItem = function (i) {
return Form.superclass.prototype.getItem.call(this.panel, i);
};
/**
* Sets the title text for the form.
* @param {String} title
* @chainable
*/
Form.prototype.setTitle = function (title) {
if (typeof title === 'string') {
this.title = title;
if (this.header) {
$(this.header).empty();
if (title) {
$(this.header).append('').find('h2').text(title);
} else {
$(this.header).append('').find('h2').html(" ");
}
this.updateDimensions();
}
}
return this;
};
/**
* Sets the name for the form
* @param {String} name
* @chainable
*/
Form.prototype.setName = function (name) {
this.name = name;
if (this.html) {
this.html.name = name;
}
return this;
};
/**
* Sets the enctype property for the form
* @param {String} encType
* @chainable
*/
Form.prototype.setEncType = function (encType) {
this.encType = encType;
if (this.html) {
this.html.setAttribute("encType", encType);
}
return this;
};
/**
* Shows the form header
* @chainable
*/
Form.prototype.showHeader = function () {
this.visibleHeader = true;
this.header.style.display = "";
this.updateDimensions();
return this;
};
/**
* Hides the form's header
* @chainable
*/
Form.prototype.hideHeader = function () {
this.visibleHeader = false;
this.header.style.display = "none";
this.updateDimensions();
return this;
};
/**
* Returns true if all the form's fields assert the their validations otherwise returns false.
* @return {Boolean}
*/
Form.prototype.isValid = function () {
var res = this.panel.isValid();
if (res) {
this.hideRequiredFieldNotification();
} else {
this.showRequiredFieldNotification();
}
return res;
};
/**
* Submits the form.
* @chainable
*/
Form.prototype.submit = function () {
if (this.isValid()) {
if (typeof this.onSubmit === 'function') {
this.onSubmit(this.getData());
}
//TODO: call send() method from proxy
}
return this;
};
/**
* Resets the form to their initial values.
* @chainable
*/
Form.prototype.reset = function () {
var fields,
i;
fields = this.getFields();
for (i = 0; i < fields.length; i += 1) {
fields[i].reset();
}
if (this.dirty) {
this.dirty = false;
}
return this;
};
/**
* Returns the form's data.
* @param {String} [format='object'] The format in which the data will be returned, it can take three values:
*
* - 'xml', the data will be returned in xml format.
* - 'json', the data will be returned in a json-formated string.
* - 'object', the data will be returned in a object literal format.
* - otherwise or nothing, the data will be returned in a object literal format but only containing the name and values from
* fields.
* @return {Object|String} The data type of the returned item will depend on the input parameter, please read the
* method description for more info.
*/
Form.prototype.getData = function (format) {
var fields,
i,
res,
data = new PMUI.data.DataSet();
fields = this.getFields();
if (format !== 'xml' && format !== 'json' && format !== 'object') {
res = {};
for (i = 0; i < fields.length; i += 1) {
if (!fields[i].disabled) {
res[fields[i].getName()] = fields[i].getValue();
}
}
} else {
for (i = 0; i < fields.length; i += 1) {
if (!fields[i].disabled) {
data.addItem(fields[i].data);
}
}
if (format === 'xml') {
return data.getXML();
} else if (format === 'json') {
return data.getJSON();
} else if (format === 'object') {
return data.getData();
}
}
return res;
};
Form.prototype.hideRequiredFieldNotification = function () {
if (this._requiredFieldNotification) {
this._requiredFieldNotification.style.display = 'none';
}
return this;
};
Form.prototype.showRequiredFieldNotification = function () {
if (this._requiredFieldNotification) {
this._requiredFieldNotification.style.display = '';
}
return this;
};
/**
* Define the event listeners for the form.
* @chainable
*/
Form.prototype.defineEvents = function () {
var that = this;
if (this.html && !this.eventsDefined) {
this.panel.defineEvents();
this.footer.defineEvents();
this.addEvent('submit').listen(this.html, function (e) {
e.preventDefault();
e.stopPropagation();
});
this.panel.setOnAvailabilityChange(function () {
that.isValid();
});
this.eventsDefined = true;
}
return this;
};
/**
* Creates the HTML for the form.
* @return {HTMLElement}
*/
Form.prototype.createHTML = function () {
var html,
header,
notificationArea,
notification,
notificationText = "Fields marked with an asterisk (%%ASTERISK%%) are required.".translate()
.replace(/%%ASTERISK%%/g, '*');
if (this.html) {
return this.html;
}
html = PMUI.createHTMLElement('form');
html.id = this.id;
html.className = 'pmui-form';
header = PMUI.createHTMLElement('div');
header.className = 'pmui-form-header';
notification = new PMUI.field.TextAnnotationField({
id: "requiredMessage",
name: "Message",
textType: PMUI.field.TextAnnotationField.TEXT_TYPES.HTML,
text: notificationText,
text_Align: "center"
});
notificationArea = PMUI.createHTMLElement('div');
notificationArea.style.display = 'none';
notificationArea.appendChild(notification.getHTML());
this._requiredFieldNotification = notificationArea;
this.header = header;
html.appendChild(header);
html.appendChild(this.panel.getHTML());
html.appendChild(notificationArea);
html.appendChild(this.footer.getHTML());
this.html = html;
this.setTitle(this.title)
.setName(this.name)
.setEncType(this.encType);
if (this.visibleHeader) {
this.showHeader();
} else {
this.hideHeader();
}
this.panel.setPadding('20px 10px 20px 10px');
this.style.applyStyle();
if (this.layout) {
this.layout.applyLayout();
}
if (this.getFields().length && this.setFocus) {
this.setFocus(0);
}
this.setButtonPanelPosition(this.buttonPanelPosition);
this.defineEvents();
return this.html;
};
/**
* clean all form fields
* @chainable
*/
Form.prototype.clearItems = function () {
this.panel.clearItems();
return this;
};
/**
* clear all form fields
* @chainable
*/
Form.prototype.removeItem = function (item) {
var i = 0;
Form.superclass.prototype.removeItem.call(this, item);
this.panel.removeItem(item);
return this;
};
/**
* hides the footer where the buttons are located
* @chainable
*/
Form.prototype.hideFooter = function () {
this.footer.setVisible(false);
return this;
};
/**
* shows the footer where the buttons are located
* @chainable
*/
Form.prototype.showFooter = function () {
this.footer.setVisible(true);
return this;
};
/**
* @method setFocus
* set the focus on a form field
* @chainable
*/
Form.prototype.setFocus = function () {
var j,
fields = this.getFields();
for (j = 0; j < fields.length; j += 1) {
if ((!fields[j].isReadOnly || !fields[j].isReadOnly()) && !fields[j].disabled && fields[j].isVisible() && fields[j].setFocus) {
fields[j].setFocus();
break;
}
}
return this;
}
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Form;
}
Form.prototype.setAlignmentButtons = function (value) {
if (typeof value == "string") {
this.alignmentButtons = value;
this.footer.setAlignment(value);
return this;
}
throw new Error('setAlignmentButtons(), the value is no type valid')
};
PMUI.extendNamespace('PMUI.form.Form', Form);
}());
(function () {
/**
* @class PMUI.form.Validator
* Handles the validations of the fields
* @extend PMUI.core.Base
*
* @constructor
* Create a new instance of the class
* @param {Object} settings
* @param {PMUI.form.Field} parent
*
* @cfg {*} [criteria=null] This is the criteria info to execute the validation,
* each validator needs a different data type criteria,
* so please read about the criteria config option for every validator object.
* @cfg {String} [errorMessage='the validation has failed'] This property defines
* the error message to display if validation fails when filled fields
* @cfg {PMUI.form.Field} [parent=null] Defines the field the validator belongs to.
*/
var Validator = function (settings) {
Validator.superclass.call(this, settings);
/**
* Defines the Field parent
* @type {PMUI.form.Field}
*/
this.parent = null;
/**
* Defines the criteria object
* @type {Object}
*/
this.criteria = null;
/**
* Defines if the object is validated
* @type {Boolean}
*/
this.validated = false;
/**
* Defines the validation state
* @type {null/Boolean}
*/
this.valid = null;
/**
* Defines the error message to show in case of the validation fails
* @type {null/Boolean}
*/
this.errorMessage = null;
Validator.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Base', Validator);
/**
* Defines the object's type
* @type {String}
*/
Validator.prototype.type = 'Validator';
/**
* Defines the object's family
* @type {String}
*/
Validator.prototype.family = 'Validator';
/**
* Initializes the object with default values
* @param {Object} settings
* @param {PMUI.form.Field} parent
*/
Validator.prototype.init = function (settings) {
var defaults = {
criteria: null,
errorMessage: 'the validation has failed',
parent: null
};
jQuery.extend(true, defaults, settings);
this.setCriteria(defaults.criteria)
.setParent(defaults.parent)
.setErrorMessage(defaults.errorMessage);
};
/**
* Sets the validation error message to show in case of the validation fails
* @param {String} errorMessage
* @chainable
*/
Validator.prototype.setErrorMessage = function (errorMessage) {
this.errorMessage = errorMessage;
return this;
};
/**
* Sets the validation error message to show in case of the validation fails
* @param {String} errorMessage
* @chainable
*/
Validator.prototype.getErrorMessage = function () {
return this.errorMessage;
};
/**
* Sets the validation criteria
* @param {Object} criteria
* @chainable
*/
Validator.prototype.setCriteria = function (criteria) {
this.criteria = criteria;
return this;
};
/**
* Sets the parent field
* @param {PMUI.form.Field|GridControlCell} parent
* @chainable
*/
Validator.prototype.setParent = function (parent) {
if (parent) {
if (parent instanceof PMUI.form.Field || parent instanceof PMUI.grid.GridControlCell) {
this.parent = parent;
} else {
throw new Error("setParent() method only accepts a Field object as parameter.");
}
}
return this;
};
/**
* Returns the parent field
* @return {PMUI.form.Field}
*/
Validator.prototype.getParent = function () {
return this.parent;
};
/**
* Evaluates the validator
*/
Validator.prototype.validate = function () {
this.valid = true;
};
/**
* Returns the validation response
* @chainable
*/
Validator.prototype.isValid = function () {
this.validate();
this.updateTooltip();
return this.valid;
};
/**
* Updates de the tooltip message
*/
Validator.prototype.updateTooltip = function () {
if (this.parent && this.parent.message) {
if (this.valid) {
this.parent.hideMessage();
} else {
this.parent.showMessage(this.errorMessage, "error");
}
}
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Validator;
}
PMUI.extendNamespace('PMUI.form.Validator', Validator);
}());
(function () {
/**
* @class PMUI.form.TextLengthValidator
* @extend PMUI.form.Validator
*
* Create a new instace abstrac of the TextLengthValidator class
* @param {Object} settings
*
* @cfg {String} [errorMessage='the validation has failed'] This property defines the error
* message to display if the validation text size entered is not valid
*/
var TextLengthValidator = function (settings) {
TextLengthValidator.superclass.call(this, settings);
TextLengthValidator.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Validator', TextLengthValidator);
/**
* Defines the object's type
* @type {String}
*/
TextLengthValidator.prototype.type = 'TextLengthValidator';
TextLengthValidator.prototype.init = function (settings) {
var maxMessage = settings.criteria.maxLength ? " at most " + settings.criteria.maxLength + " characters" : "",
minMessage = settings.criteria.minLength ? "at least " + settings.criteria.minLength + " characters" : "",
defaults = {
errorMessage: "The text length must have " + minMessage + (maxMessage ? " and " : "") + maxMessage
};
jQuery.extend(true, defaults, settings);
this.setErrorMessage(defaults.errorMessage);
};
/**
* Evaluates the validator
* this method is for validate the length of the string on control the value
* input in text field, cutting spaces white in use trim of the jquery function
* @chainable
*/
TextLengthValidator.prototype.validate = function () {
var res = false,
value = this.criteria.trim ? jQuery.trim(this.parent.value) : this.parent.value;
this.valid = true;
if (this.criteria.maxLength) {
this.valid = value.length <= parseInt(this.criteria.maxLength, 10);
}
if (this.criteria.minLength) {
this.valid = (this.valid !== null ? this.valid : true) &&
value.length >= parseInt(this.criteria.minLength, 10);
}
return this;
};
PMUI.extendNamespace('PMUI.form.TextLengthValidator', TextLengthValidator);
if (typeof exports !== "undefined") {
module.exports = TextLengthValidator;
}
}());
(function () {
/**
* @class PMUI.form.RegExpValidator
* @extends PMUI.form.Validator
* A text validator based in regular expressions.
*
* Usually this object won't be instatiate directly, it will be instantiate through a
* {@link PMUI.form.Field Field} object just like is shown in the
* {@link PMUI.field.TextField TextField documentation}. We recommend to use it this way.
*
* The other way to instantiate an object from this class:
*
* var myValidator,
* field,
* callback = function() {
* this.isValid();
* };
*
* field = new PMUI.field.TextField({
* onChange: callback
* });
*
* myValidator = new RegExpValidator({
* criteria: /^\d+$/,
* errorMessage: "You must introduce only number digits."
* });
*
* field.addValidator(myValidator);
*
* document.body.appendChild(field.getHTML());
*
* @cfg {Object|RegExp} criteria The criteria config option can be:
*
* - a JSON object: in this case it must have two porperties:
* - pattern (String): the pattern string to be use as the regular expression
(don't forget to escape special characters)
* - modifiers (String): this is optional, this is the modifier for the regular expression.
* - a RegExp object
*/
var RegExpValidator = function (options) {
RegExpValidator.superclass.call(this, options);
RegExpValidator.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.form.Validator', RegExpValidator);
RegExpValidator.prototype.type = "RegExpValidator";
RegExpValidator.prototype.init = function (options) {
var defaults = {
errorMessage: "The text pattern doesn't match"
};
$.extend(true, defaults, options);
this.setErrorMessage(defaults.errorMessage);
};
/**
* Execute the validation.
* @chainable
* @private
*/
RegExpValidator.prototype.validate = function () {
var res = false,
regExp;
if (this.parent) {
if (this.criteria instanceof RegExp) {
if ((this.parent.valueType == 'integer' || this.parent.valueType == 'float' || this.parent.valueType == 'number') && !this.parent.value) {
this.valid = this.criteria.test(0);
} else {
this.valid = this.criteria.test(this.parent.value);
}
} else if (typeof this.criteria === 'string') {
regExp = new RegExp(this.criteria.pattern, this.criteria.modifiers);
this.valid = regExp.text(this.parent.value);
}
} else {
this.valid = false;
}
return this;
};
PMUI.extendNamespace('PMUI.form.RegExpValidator', RegExpValidator);
if (typeof exports !== "undefined") {
module.exports = RegExpValidator;
}
}());
(function () {
/**
* @class PMUI.form.ValidatorFactory
* @extend PMUI.util.Factory
* Extends the factory class to produce field validators.
*
* Its default products are:
*
* - {@link PMUI.form.TextLengthValidator TextLengthValidator} objects: using "textLength".
* - {@link PMUI.form.RegExpValidator RegExpValidator} objects: using "regexp".
*
* @constructor
* Creates a new instance od the class
*/
var ValidatorFactory = function () {
ValidatorFactory.superclass.call(this);
ValidatorFactory.prototype.init.call(this);
};
PMUI.inheritFrom('PMUI.util.Factory', ValidatorFactory);
ValidatorFactory.prototype.init = function () {
var defaults = {
products: {
"textLength": PMUI.form.TextLengthValidator,
"regexp": PMUI.form.RegExpValidator
},
defaultProduct: "textLength"
};
this.setProducts(defaults.products)
.setDefaultProduct(defaults.defaultProduct);
};
PMUI.extendNamespace('PMUI.form.ValidatorFactory', ValidatorFactory);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ValidatorFactory;
}
}());
(function () {
/**
* @class PMUI.panel.LayoutPanel
* @extends PMUI.core.Panel
* Handles layouts to be inserted into panels.
* It is composed by five main elements: north, south, west, east and center
*
* Example:
*
* @example
*
* layout = new PMUI.panel.LayoutPanel({
* id: "myLayout",
* center: {
* cssProperties: {
* "background-color": "#91b3e0"
* },
* cssClasses: ['my-class']
* },
* west: {
* resizable: false,
* closed: true
* },
* south: new PMUI.panel.LayoutPanel({
* north:{
* size: 50
* }
* }),
* southConfig:{
* size: 200
* },
* north: {
* size: 50,
* togglerOpen: 80,
* overflow: false
* },
* east: {
* closable: false
* }
* });
* document.body.appendChild(layout.getHTML());
* layout.render(true);
*
* @constructor
* Create a new instance of the LayoutPanel class
* @param {Object} options Contructor object
*/
var LayoutPanel = function (options) {
LayoutPanel.superclass.call(this, options);
/**
* @property {String} prefixPlugin
* The prefix for the classes in jquery.layout plugin
* @private
*/
var prefixPlugin = 'ui-layout-',
/**
* @property {Array} positions
* The array with the position accepted in the layout
* @private
*/
positions = ['north', 'center', 'south', 'east', 'west'],
/**
* @property {Object} settings
* Defines the settings acceptted by the layout panel
* @private
*/
settings = {
size: "size",
closed: "initClosed",
resizable: "resizable",
closable: "closable",
togglerOpen: "togglerLength_open",
togglerClosed: "togglerLength_closed",
overflow: "allowOverflow",
hidden: "initHidden",
cssProperties: "cssProperties",
cssClasses: "cssClasses"
};
/**
* @property {Object} panels
* The settings for the plugin organized by section
* @private
*/
this.panels = {};
/**
* North panel pointer
* @type {HTMLElement}
*/
this.north = null;
/**
* Center panel pointer
* @type {HTMLElement}
*/
this.center = null;
/**
* South panel pointer
* @type {HTMLElement}
*/
this.south = null;
/**
* East panel pointer
* @type {HTMLElement}
*/
this.east = null;
/**
* West panel pointer
* @type {HTMLElement}
*/
this.west = null;
/**
* Javascript pointer to the jquery layout plugin instance
* @type {Object}
*/
this.instance = null;
this.onClose = null;
this.onOpen = null;
/**
* Returns the config object
* @return {Object}
*/
LayoutPanel.prototype.getConfig = function () {
return {
panels: this.panels,
onclose_end: this.onClose,
onopen_start: this.onOpen,
prefix: prefixPlugin,
positions: positions,
isSetting: function (value) {
var valid = settings[value];
return (typeof valid !== 'undefined');
},
transformSetting: function (value) {
return settings[value];
}
};
};
/**
* Sets the panel settings
* @param {String} pos Position (north, center, south, east or west)
* @param {Object} settings Object settings
*/
LayoutPanel.prototype.setConfig = function (pos, settings) {
this.panels[pos] = settings;
return this;
};
LayoutPanel.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.core.Panel', LayoutPanel);
/**
* Defines the object's type
* @type {String}
*/
LayoutPanel.prototype.type = 'LayoutPanel';
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
LayoutPanel.prototype.init = function (options) {
var defaults = {
center: {
cssProperties: {},
cssClasses: []
},
factory: {
products: {
"layout": PMUI.panel.LayoutPanel,
"treePanel": PMUI.panel.TreePanel,
"panel": PMUI.core.Panel
},
defaultProduct: "panel"
},
onClose: null,
onOpen: null
};
jQuery.extend(true, defaults, options);
this.setFactory(defaults.factory)
.setPluginSettings(defaults)
.setPanels(defaults);
this.setOnCloseHandler(defaults.onClose);
this.setOnOpenHandler(defaults.onOpen);
};
LayoutPanel.prototype.setOnCloseHandler = function (hanlder) {
if (!(typeof hanlder === "function" || hanlder === null)) {
throw new Error("setOnCloseHandler(): hanlder should be a funtion");
}
this.onClose = hanlder;
return this;
};
LayoutPanel.prototype.setOnOpenHandler = function (hanlder) {
if (!(typeof hanlder === "function" || hanlder === null)) {
throw new Error("setOnOpenHandler(): hanlder should be a funtion");
}
this.onOpen = hanlder;
return this;
};
/**
* Calculate and sets the settings for each panel
* @param {Object} obj Object constructor
*/
LayoutPanel.prototype.setPluginSettings = function (obj) {
var config = this.getConfig(),
that = this;
jQuery.each(config.positions, function (index, pos) {
var panel = obj[pos],
panelSettings = {};
if (panel) {
if (that.factory && that.factory.isValidClass(panel)) {
panelSettings = obj[pos + "Config"] || {};
} else {
jQuery.each(panel, function (key, value) {
if (config.isSetting(key)) {
panelSettings[config.transformSetting(key)] = value;
}
});
}
if (!(panelSettings.cssClasses && jQuery.isArray(panelSettings.cssClasses))) {
panelSettings.cssClasses = [];
}
panelSettings.cssClasses.push(config.prefix + pos);
that.setConfig(pos, panelSettings);
}
});
return this;
};
/**
* Set panels into north, south, center, west and east pointers
* @param {Object} obj Constructor object
*/
LayoutPanel.prototype.setPanels = function (obj) {
var config = this.getConfig(),
that = this,
treeOptions;
jQuery.each(config.positions, function (index, pos) {
if (obj[pos]) {
that[pos] = that.factory.make(obj[pos]);
if (that[pos] instanceof PMUI.panel.LayoutPanel) {
that.setConfig(pos, jQuery.extend(true, config.panels[pos], {instance: that[pos]}));
}
if (that[pos] instanceof PMUI.panel.TreePanel) {
treeOptions = {
cssProperties: that[pos].style.cssProperties,
cssClasses: that[pos].style.cssClasses
};
that.setConfig(pos, jQuery.extend(true, config.panels[pos], treeOptions));
}
that[pos].style.cssProperties = config.panels[pos].cssProperties || {};
that[pos].style.cssClasses = config.panels[pos].cssClasses || [config.prefix + pos];
that.addItem(that[pos]);
}
});
return this;
};
/**
* Create html for each item to container
* @return {Object} the current object html
*/
LayoutPanel.prototype.createHTML = function () {
var i,
items;
items = this.items.asArray();
LayoutPanel.superclass.prototype.createHTML.call(this);
for (i = 0; i < items.length; i += 1) {
$(this.html).append(items[i].getHTML());
}
return this.html;
};
/**
* Render all the html object
* @param {Boolean} [recursive] Defines if the render must be recursive
*/
LayoutPanel.prototype.render = function (recursive) {
var that = this,
options,
renderRecursive = recursive || false;
options = that.getConfig().panels;
options.onclose_end = that.getConfig().onclose_end;
options.onopen_start = that.getConfig().onopen_start;
if (jQuery(that.html).height() === 0) {
jQuery(that.html).height(jQuery(document).height() - 20);
}
this.instance = jQuery(that.html).layout(options);
jQuery.each(options, function (position, val) {
if (options[position] !== null) {
if (options[position].instance && renderRecursive) {
options[position].instance.render(renderRecursive);
}
if (options[position].allowOverflow === true) {
that.instance.allowOverflow(position);
}
}
});
};
/**
* Remove each html related to the layout from the DOM and destroy the layout instance
*/
LayoutPanel.prototype.destroy = function () {
if (this.instance.destroy !== undefined) {
this.instance.destroy();
}
if (this.html !== undefined) {
jQuery(this.html).remove();
this.html = null;
}
this.instance = null;
};
PMUI.extendNamespace('PMUI.panel.LayoutPanel', LayoutPanel);
if (typeof exports !== 'undefined') {
module.exports = LayoutPanel;
}
}());
(function () {
/**
* @class PMUI.layout.Layout
* Defines the properties for the different layouts in the panels
* @abstract
**/
var Layout = function (options) {
/**
* Defines the container to apply the layout
* @type {Object}
*/
this.belongsTo = null;
Layout.prototype.init.call(this, options);
};
/**
* Defines the object's type
* @type {String}
*/
Layout.prototype.type = "Layout";
/**
* Defines the object's family
* @type {String}
*/
Layout.prototype.family = "Layout";
Layout.prototype.init = function (options) {
var defaults = {
belongsTo: null
};
jQuery.extend(true, defaults, options);
this.setContainer(defaults.belongsTo);
};
/**
* Applies the layout to the container
* @abstract
*/
Layout.prototype.applyLayout = function () {
return this;
};
/**
* Sets the parent container
* @param {Object} parent Container Object
*/
Layout.prototype.setContainer = function (parent) {
this.belongsTo = parent;
return this;
};
PMUI.extendNamespace('PMUI.layout.Layout', Layout);
if (typeof exports !== 'undefined') {
module.exports = Layout;
}
}());
(function () {
/**
* @class PMUI.layout.HBox
* @extends PMUI.layout.Layout
* Class created to handle Horizontal Box layout changes
*
* @constructor
* Creates a new isntance of the object
* @param {Object} options Constructor object
*/
var HBox = function (options) {
HBox.superclass.call(this, options);
HBox.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.layout.Layout', HBox);
/**
* Defines the object's type
* @type {String}
*/
HBox.prototype.type = "HBox";
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
HBox.prototype.init = function (options) {
var defaults = {};
jQuery.extend(true, defaults, options);
};
/**
* Applies the layout to the current element
*/
HBox.prototype.applyLayout = function () {
// get the object of this layout
var owner = this.belongsTo,
items = owner.items,
totalProportion = 0,
usableWidth = owner.getUsableWidth(),
usableHeight = owner.getUsableHeight(),
i,
width,
item;
var ownerParent = owner;
// search the width in anysome parent
if (owner != null) {
if (owner.getWidth() != "auto" && owner.parent != null) {
ownerParent = owner.parent;
}
if (owner.html) {
owner.html.style.width = ownerParent.getUsableWidth();
}
}
// compute totalProportion
for (i = 0; i < items.getSize(); i += 1) {
item = items.get(i);
totalProportion += item.proportion;
}
xPosition = 0;
for (i = 0; i < items.getSize(); i += 1) {
item = items.get(i);
if (item.html) {
item.setDisplay("inline-block");
}
width = usableWidth * (item.proportion / totalProportion);
item.setWidth(width);
item.setHeight(usableHeight);
}
return this;
};
PMUI.extendNamespace('PMUI.layout.HBox', HBox);
if (typeof exports !== 'undefined') {
module.exports = HBox;
}
}());
(function () {
/**
* @class PMUI.layout.VBox
* @extends PMUI.layout.Layout
* Class created to handle Vertical Box layout changes
*
* @constructor
* Creates a new isntance of the object
* @param {Object} options Constructor object
*/
var VBox = function (options) {
VBox.superclass.call(this, options);
VBox.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.layout.Layout', VBox);
/**
* Defines the object's type
* @type {String}
*/
VBox.prototype.type = "VBox";
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
VBox.prototype.init = function (options) {
var defaults = {};
jQuery.extend(true, defaults, options);
};
/**
* Applies the layout to the current element
*/
VBox.prototype.applyLayout = function () {
// get the object of this layout
var owner = this.belongsTo,
items = owner.items,
totalProportion = 0,
usableWidth = owner.getUsableWidth(),
usableHeight = owner.getUsableHeight(),
i,
height,
item;
// compute totalProportion
for (i = 0; i < items.getSize(); i += 1) {
item = items.get(i);
totalProportion += item.proportion;
}
// set the width of each object based on the width of its parent
for (i = 0; i < items.getSize(); i += 1) {
item = items.get(i);
if (item.html) {
item.setDisplay("block");
}
height = usableHeight * (item.proportion / totalProportion);
item.setHeight(height);
item.setWidth(usableWidth);
}
return this;
};
PMUI.extendNamespace('PMUI.layout.VBox', VBox);
if (typeof exports !== 'undefined') {
module.exports = VBox;
}
}());
(function () {
/**
* @class PMUI.layout.Box
* @extends PMUI.layout.Layout
* Class created to no layout changes
*
* @constructor
* Creates a new isntance of the object
* @param {Object} options Constructor object
*/
var Box = function (options) {
Box.superclass.call(this, options);
Box.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.layout.Layout', Box);
/**
* Defines the object's type
* @type {String}
*/
Box.prototype.type = "Box";
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
Box.prototype.init = function (options) {
var defaults = {};
jQuery.extend(true, defaults, options);
};
/**
* Applies the layout to the current element
*/
Box.prototype.applyLayout = function () {
};
PMUI.extendNamespace('PMUI.layout.Box', Box);
if (typeof exports !== 'undefined') {
module.exports = Box;
}
}());
(function () {
/**
* @class PMUI.layout.LayoutFactory
* @extends {PMUI.util.Factory}
* Extends the functionality of Factory to set the constructor for layouts
*
* @constructor
* Makes a new instance of the class
* @param {Object} options
*/
var LayoutFactory = function (options) {
LayoutFactory.superclass.call(this, options);
LayoutFactory.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.util.Factory', LayoutFactory);
/**
* Defines the object's type
* @type {String}
*/
LayoutFactory.prototype.type = 'LayoutFactory';
/**
* @private
* Initializes the object with default values
* @param {Object} options
*/
LayoutFactory.prototype.init = function (options) {
var defaults = {
products: {
"hbox": PMUI.layout.HBox,
"vbox": PMUI.layout.VBox,
"box": PMUI.layout.Box
},
defaultProduct: "box"
};
jQuery.extend(true, defaults, options);
this.setProducts(defaults.products)
.setDefaultProduct(defaults.defaultProduct);
};
LayoutFactory.prototype.make = function (obj) {
var product,
productType = obj.pmType || '';
if (this.isValidClass(obj)) {
product = obj;
} else if (this.isValidName(productType)) {
product = this.build.call(this, productType, obj);
} else if (this.isValidName(obj)) {
product = this.build.call(this, obj, {});
} else {
product = this.build.call(this, this.defaultProduct, obj);
}
return product;
};
PMUI.extendNamespace('PMUI.layout.LayoutFactory', LayoutFactory);
if (typeof exports !== 'undefined') {
module.exports = LayoutFactory;
}
}());
(function () {
/**
* @class PMUI.panel.TreePanel
* A panel that contains {@link PMUI.item.TreeNode TreeNode items}.
* @extends {PMUI.core.Panel}
*
* An example of a TreePanel with descendant nodes (using the items {@link #cfg-items config option}):
*
* @example
* var t = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ['xxxxxxxx']
* },
* items: [
* {
* label: "America",
* items: [
* {
* label: "North America",
* items: [
* {
* label: "Canada"
* },
* {
* label: "USA"
* },
* {
* label: "Mexico"
* }
* ]
* },
* {
* label: "Central America and Caribbean",
* items: [
* {
* label: "Guatemala"
* },
* {
* label: "Cuba"
* },
* {
* label: "Costa Rica"
* }
* ]
* },
* {
* label: "South America",
* items: [
* {
* label: "Argentina"
* },
* {
* label: "Bolivia"
* },
* {
* label: "Brasil"
* }
* ]
* }
* ]
* }
* ]
* });
* document.body.appendChild(t.getHTML());
* t.defineEvents();
*
* An example of a TreePanel with descendant nodes (using the {@link #cfg-dataItems dataItems} config option):
*
* @example
* t2 = new PMUI.panel.TreePanel({
* nodeDefaultSettings: {
* labelDataBind: 'name',
* itemsDataBind: 'regions',
* recursiveChildrenDefaultSettings: true,
* childrenDefaultSettings: {
* labelDataBind: 'name',
* itemsDataBind: 'countries',
* autoBind: true
* },
* autoBind: true
* },
* dataItems: [
* {
* name: 'America',
* regions: [
* {
* name: 'North America',
* countries: [
* {
* name: 'Canada'
* },
* {
* name: 'USA'
* },
* {
* name: 'Mexico'
* }
* ]
* },
* {
* name: 'Central America and Caribbean',
* countries: [
* {
* name: 'Guatemala'
* },
* {
* name: 'Cuba'
* },
* {
* name: 'Costa Rica'
* }
* ]
* },
* {
* name: 'South America',
* countries: [
* {
* name: 'Argentina'
* },
* {
* name: 'Bolivia'
* },
* {
* name: 'Brasil'
* }
* ]
* }
* ]
* }
* ]
* });
* document.body.appendChild(t2.getHTML());
* t2.defineEvents();
*
* @constructor
* Creates a new instance of the class.
* @param {Object} settings The config options.
*
* @cfg {Object|null} [nodeDefaultSettings=null] The default settings for all the direct child nodes of the tree panel.
* It is an object with the config options for {@link PMUI.item.TreeNode TreeNode}.
* @cfg {Object|null} [dataItems=null] An object with the data for the items in the tree panel.
* @cfg {Array} [data=[]] An array in which each element is an object with the config options for
* {@link PMUI.item.TreeNode TreeNode} an instance of it. If you use both dataItems and this config option, this
* config option will be ignored and the dataItems will be used instead.
* @cfg {String} [filterPlaceholder="search"] The text to show as placeholder for the text control for filtering.
* @cfg {Function|String|null} [emptyMessage=null] The message to show when the grid is showing no items. It
* can be:
*
* - A String, in this case the string will be displayed in both cases, when the treepanel has no items and when no
* treepanel's item meet the filter.
* - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
* will receive two parameters: the {@link PMUI.panel.TreePanel TreePanel}, a boolean If it is true it means the returned
* value will be used when a filter is applied, otherwise it means that the returned value will be used when
* there are no items in the treepanel.
* - null, in this case a default message will be used for each situation.
* @cfg {Boolean} [visibleTitle=false] If the title will be displayed.
* @cfg {String} [title=""] The text for the title.
* @cfg {Function} [onNodeClick=null] The callback function to be executed everytime the
* {@link #event-onNodeClick onNodeClick} event fires. For info about the parameters sent to the callback please
* read the {@link #event-onNodeClick onNodeClick} event documentation.
* @cfg {Function|null} [onBeforeAppend=null] A callback function to be called everytime the
* {@link #event-onBeforeAppend onBeforeAppend} event is fired. For info about the parameters used for this
* callback please read the documentation for this event.
* @cfg {Function|null} [onAppend=null] A callback function to be called everytime the
* {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
* read the documentation for this event.
*/
var TreePanel = function (settings) {
TreePanel.superclass.call(this, jQuery.extend(settings, {
factory: {
products: {
'node': PMUI.item.TreeNode
},
defaultProduct: 'node'
}
}));
/**
* The default settings for all the direct child nodes of the tree panel.
* @type {Object}
*/
this.nodeDefaultSettings = null;
/**
* The filter criteria being applied currently.
* @type {String}
* @readonly
*/
this.filterCriteria = "";
/**
* If the tree panel is filterable or not.
* @type {Boolean}
* @readonly
*/
this.filterable = null;
/**
* The text control in which the user can introduce a text to filter.
* @type {PMUI.control.TextControl}
* @private
*/
this.filterControl = null;
/**
* A string or a function or null. Determines the text/HTML Element to show when there are no items to show.
* @type {Function|String|null}
* @readonly
*/
this.emptyMessage = null;
/**
* The title for the TreePanel
* @type {String}
*/
this.title = null;
/**
* If the title is visible or not.
* @type {Boolean}
* @readonly
*/
this.visibleTitle = null;
/**
* @event onItemClick
* Fired everytime an tree item is clicked.
* @param {PMUI.panel.TreePanel} treePanel The TreePanel in which the event was fired.
* @param {PMUI.item.TreeNode} node The node that was clicked.
*/
this.onNodeClick = null;
/**
* An private object to store the DOM elements that compose the object's HTML.
* @type {Object}
* @private
*/
this.dom = {};
/**
* @event onBeforeAppend
* Fired before a child node is appended to the current node or to any of its children nodes.
* @param {PMUI.item.TreeNode} node The node in which the event is being fired.
* @param {PMUI.item.TreeNode} newNode The new node to be appended.
*/
this.onBeforeAppend = null;
/**
* @event onAppend
* Fired everytime a child node is appended to the current node or to any of its children nodes.
* @param {PMUI.item.TreeNode} node The node in which the event is being fired.
* @param {PMUI.item.TreeNode} newNode The appended node.
*/
this.onAppend = null;
TreePanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Panel', TreePanel);
/**
* The object's type.
* @type {String}
*/
TreePanel.prototype.type = 'TreePanel';
/**
* The class family.
* @type {String}
*/
TreePanel.prototype.family = 'Panel';
/**
* Initializes the object.
* @param {Object} settings The config options for the object.
* @private
*/
TreePanel.prototype.init = function (settings) {
var defaults = {
nodeDefaultSettings: null,
dataItems: null,
data: [],
filterPlaceholder: 'search',
filterable: false,
emptyMessage: null,
visibleTitle: false,
title: "",
onNodeClick: null,
onAppend: null,
onBeforeAppend: null,
collapse: false,
onStop : function(){console.log("trepanel")}
};
jQuery.extend(true, defaults, settings);
this.setNodeDefaultSettings(defaults.nodeDefaultSettings)
.setEmptyMessage(defaults.emptyMessage)
.setTitle(defaults.title)
.setOnNodeClickHandler(defaults.onNodeClick)
.setOnAppendHandler(defaults.onAppend)
.setOnBeforeAppendHandler(defaults.onBeforeAppend);
this.filterControl = new PMUI.control.TextControl({
onKeyUp: this.onFilterControlChangeHandler(),
placeholder: defaults.filterPlaceholder
});
if (jQuery.isArray(defaults.dataItems)) {
this.setDataItems(defaults.dataItems);
} else {
this.setItems(defaults.items);
}
if (defaults.filterable) {
this.enableFiltering();
} else {
this.disableFiltering();
}
if (defaults.visibleTitle) {
this.showTitle();
} else {
this.hideTitle();
}
if (defaults.collapse) {
this.collapse(true);
} else {
this.expand(true);
}
};
/**
* Sets the callback function to be called everytime the {@link #event-onBeforeAppend onBeforeAppend} event is
* fired. For info about the parameters used for this callback please read the documentation for this event.
* @param {Function|null} handler
*/
TreePanel.prototype.setOnBeforeAppendHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnBeforeAppendHandler(): The parameter must be a function or null.");
}
this.onBeforeAppend = handler;
return this;
};
/**
* Sets the callback function to be called everytime the
* {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
* read the documentation for this event.
* @param {Function|null} handler
*/
TreePanel.prototype.setOnAppendHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnAppendHandler(): The parameter must be a function or null.");
}
this.onAppend = handler;
return this;
};
/**
* Sets the callback function to be executed everytime the {@link #event-onNodeclick onNodeClick} is fired. For
* info about the parameters sent to the callback read the document for that event.
* @param {Function} handler It can be a function or null.
*/
TreePanel.prototype.setOnNodeClickHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnNodeClickHandler(): The parameter must be a function.");
}
this.onNodeClick = handler;
return this;
};
/**
* @inheritdoc
*/
TreePanel.prototype.clearItems = function () {
TreePanel.superclass.prototype.clearItems.call(this);
return this.showEmptyMessage();
};
/**
* Hides the title bar.
* @chainable
*/
TreePanel.prototype.hideTitle = function () {
this.visibleTitle = false;
if (this.dom.title) {
this.dom.title.style.display = 'none';
}
return this;
};
/**
* Shows the title.
* @chainable
*/
TreePanel.prototype.showTitle = function () {
this.visibleTitle = true;
if (this.dom.title) {
this.dom.title.style.display = '';
}
return this;
};
/**
* Sets the title for the tree panel.
* @param {String} title
* @chainable
*/
TreePanel.prototype.setTitle = function (title) {
if (typeof title !== 'string') {
throw new Error('showTitle(): The parameter must be a string.');
}
this.title = jQuery.trim(title);
if (this.dom.title) {
this.dom.title.textContent = this.title;
}
return this;
};
/**
* Sets the message to display when there are no items to display in the tree panel.
* @param {String|Function|null} emptyMessage It can be:
*
* - A String, in this case the string will be displayed in both cases, when the tree has no items and when no
* tree's items meet the filter.
* - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
* will receive two parameters: the {@link PMUI.panel.TreePanel tree panel}, a boolean If it is true it means the returned
* value will be used when a filter is applied, otherwise it means that the returned value will be used when
* there are no items in the tree panel.
* - null, in this case a default message will be used for each situation.
* @chainable
*/
TreePanel.prototype.setEmptyMessage = function (emptyMessage) {
if (!(emptyMessage === null || typeof emptyMessage === 'string' || typeof emptyMessage === 'function')) {
throw new Error("setEmptyMessage(): the parameter must be a string, a function or null.");
}
this.emptyMessage = emptyMessage;
return this;
};
/**
* Sets the filter for the text control's placeholder.
* @param {String} placeholder The text for the placeholder.
* @chainable
*/
TreePanel.prototype.setFilterPlaceholder = function (placeholder) {
this.filterControl.setPlaceholder(placeholder);
return this;
};
/**
* Returns the placeholder for the text control for filtering.
* @return {String}
*/
TreePanel.prototype.getFilterPlaceholder = function () {
return this.filterControl.placeholder;
};
/**
* Returns the handler to be executed when the filter changes.
* @return {Function}
* @private
*/
TreePanel.prototype.onFilterControlChangeHandler = function () {
var that = this;
return function () {
var nextFilter = this.getHTML().value;
if (that.filterCriteria !== nextFilter) {
that.filter(nextFilter);
}
};
};
/**
* Enables the filtering functionality.
* @chainable
*/
TreePanel.prototype.enableFiltering = function () {
this.filterable = true;
this.clearFilter().filterControl.setValue("");
if (this.dom.toolbar) {
this.dom.toolbar.style.display = '';
}
return this;
};
/**
* Disables the filtering functionality.
* @chainable
*/
TreePanel.prototype.disableFiltering = function () {
this.filterable = false;
this.clearFilter().filterControl.setValue("");
if (this.dom.toolbar) {
this.dom.toolbar.style.display = 'none';
}
return this;
};
/**
* Sets the default settings for the direct child nodes of the tree panel.
* @param {Object} settings The config options to be applied to new child nodes to be added in case they are
* missing any config option.
* @chainable
*/
TreePanel.prototype.setNodeDefaultSettings = function (settings) {
if (typeof settings !== 'object') {
throw new Error('setNodeDefaultSettings(): The parameter must be an object.');
}
this.nodeDefaultSettings = settings;
return this;
};
/**
* @inheritdoc
*/
TreePanel.prototype.setFactory = function () {
this.factory = new PMUI.util.Factory({
products: {
'node': PMUI.item.TreeNode
},
defaultProduct: 'node'
});
return this;
};
/**
* The private handler fot the {@link #event-onBeforeAppend onBeforeAppend} event.
* @private
* @chainable
*/
TreePanel.prototype.onBeforeAppendHandler = function (treeNode) {
if (typeof this.onBeforeAppend === 'function') {
this.onBeforeAppend(this, treeNode);
}
return this;
};
/**
* The private handler for the {@link #event-onAppend} event.
* @private
* @chainable
*/
TreePanel.prototype.onAppendHandler = function (treeNode) {
if (typeof this.onAppend === 'function') {
this.onAppend(this, treeNode);
}
return this;
};
/**
* Adds a new node to the tree panel.
* @param {Object|PMUI.item.TreeNode} item An instance of {@link PMUI.item.TreeNode TreeNode}, or an object with
* the config options to create a new one. In the latter case, if any config option is missing then will be used
* the respective one in the set by the {@link #method-setNodeDefaultSettings setNodeDefaultSettings()} method.
* @param {Number} [index] The position index in which will be added the new item. If not specified the will be
* added to the end.
* @chainable
*/
TreePanel.prototype.addItem = function (item, index) {
var settings = {},
itemToAdd;
if (item instanceof PMUI.item.TreeNode) {
item.setParent(this);
} else if (typeof item === 'object') {
jQuery.extend(true, settings, this.nodeDefaultSettings || {}, item);
settings.parent = this;
item = settings;
} else {
throw new Error('addItem(): The parameter must be an instance of PMUI.item.TreeNode or an object.');
}
itemToAdd = this.factory.make(item);
if (itemToAdd) {
this.hideEmptyMessage();
this.onBeforeAppendHandler(itemToAdd);
TreePanel.superclass.prototype.addItem.call(this, itemToAdd, index);
this.onAppendHandler(itemToAdd);
}
return this;
};
/**
* @inheritdoc
*/
TreePanel.prototype.removeItem = function (item) {
TreePanel.superclass.prototype.removeItem.call(this, item);
if (!this.items.getSize()) {
this.showEmptyMessage();
}
return this;
};
/**
* @inheritdoc
*/
TreePanel.prototype.setItems = function (items) {
if (this.onAppend !== undefined) {
TreePanel.superclass.prototype.setItems.call(this, items);
}
return this;
};
/**
* Expands the child nodes.
* @param {Boolean} [expandAll=false] If the expand function will be applied to all the sub child items or not.
* @chainable
*/
TreePanel.prototype.expand = function (expandAll) {
var i,
items = this.items.asArray();
for (i = 0; i < items.length; i += 1) {
items[i].expand(expandAll);
}
return this;
};
/**
* Collapse the child nodes.
* @param {Boolean} [collapseAll=false] If the collapse function will be applied to all the sub child items or
* not.
* @chainable
*/
TreePanel.prototype.collapse = function (collapseAll) {
var i,
items = this.items.asArray();
for (i = 0; i < items.length; i += 1) {
items[i].collapse(collapseAll);
}
return this;
};
/**
* Hides the message to show when there are no items to display.
* @chainable
* @private
*/
TreePanel.prototype.hideEmptyMessage = function () {
jQuery(this.html).find('.pmui-treepanel-emptymessage').hide();
return this;
};
/**
* Shows an empty message when there are no items to display.
* @chainable
* @private
*/
TreePanel.prototype.showEmptyMessage = function () {
var node,
message;
if (!this.html) {
return this;
}
node = jQuery(this.html).find('.pmui-treepanel-emptymessage').empty().get(0);
if (!node) {
node = PMUI.createHTMLElement('div');
node.className = 'pmui-treepanel-emptymessage';
}
if (typeof this.emptyMessage === 'function') {
message = this.emptyMessage(this, !!this.filterCriteria);
} else if (typeof this.emptyMessage === 'string') {
message = this.emptyMessage;
} else {
if (this.filterCriteria) {
message = 'No matches found for \"' + this.filterCriteria + '\"';
} else {
message = 'No items found.';
}
}
if (typeof message === 'string') {
node.appendChild(document.createTextNode(message));
} else if (PMUI.isHTMLElement(message)) {
node.appendChild(message);
}
node.style.display = '';
this.html.appendChild(node);
return this;
};
/**
* Clears the current filter.
* @chainable
*/
TreePanel.prototype.clearFilter = function () {
return this.filter("");
};
/**
* Filters the tree using a criteria.
* @param {String} filterCriteria The criteria.
* @chainable
*/
TreePanel.prototype.filter = function (filterCriteria) {
var i,
childNodes = this.items.asArray(),
partialRes,
res = false;
filterCriteria = filterCriteria.toString(10);
this.filterCriteria = filterCriteria;
jQuery(this.html).find('.pmui-treepanel-emptymessage').remove();
for (i = 0; i < childNodes.length; i += 1) {
childNodes[i].setVisible(partialRes = childNodes[i].filter(filterCriteria));
res = res || partialRes;
}
if (!res) {
this.showEmptyMessage();
}
return this;
};
/**
* @inheritdoc
*/
TreePanel.prototype.defineEvents = function () {
TreePanel.superclass.prototype.defineEvents.call(this);
this.filterControl.defineEvents();
return this;
};
/**
* @inheritdoc
*/
TreePanel.prototype.createHTML = function () {
var list,
toolbar,
title;
if (this.html) {
return this.html;
}
html = PMUI.createHTMLElement(this.elementTag || "div");
html.id = this.id;
PMUI.linkToPMUIObject(html, this);
this.html = html;
this.actionHTML = html;
this.applyStyle();
this.containmentArea = this.html;
title = PMUI.createHTMLElement('h2');
title.className = 'pmui-treepanel-title';
this.html.appendChild(title);
this.dom.title = title;
list = PMUI.createHTMLElement('ul');
list.className = 'pmui-treepanel-list';
this.dom.list = list;
this.containmentArea = list;
toolbar = PMUI.createHTMLElement('div');
toolbar.className = 'pmui-treepanel-toolbar';
toolbar.style.display = 'none';
toolbar.appendChild(this.filterControl.getHTML());
this.dom.toolbar = toolbar;
this.html.appendChild(toolbar);
this.html.appendChild(list);
this.setTitle(this.title);
if(this.items.getSize() > 0){
this.paintItems();
}
if (this.filterable) {
this.enableFiltering();
} else {
this.disableFiltering();
}
if (this.visibleTitle) {
this.showTitle();
} else {
this.hideTitle();
}
return this.html;
};
PMUI.extendNamespace('PMUI.panel.TreePanel', TreePanel);
if (typeof exports !== 'undefined') {
module.exports === TreePanel;
}
}());
(function () {
/**
* @class PMUI.panel.TabPanel
* @extends PMUI.core.Panel
* Is the container for every {@link PMUI.item.TabItem TabItem} class
*
* Example:
*
* @example
var a,p,f,tree,w;
//Form Panel*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
p = new PMUI.form.FormPanel({
width: 604,
height: 130,
fieldset: true,
legend: "my fieldset panel",
items: [
{
pmType: "text",
label: "Name",
id: "123",
value: "",
placeholder: "insert your name",
name: "name"
},{
pmType: "text",
label: "Last name",
value: "",
placeholder: "your lastname here asshole!",
name: "lastname"
}
],
layout: "vbox"
});
//treePanel *-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
tree = new PMUI.panel.TreePanel({
items: [
{
label: "America",
items: [
{
label: "North America",
items: [
{
label: "Canada"
},
{
label: "USA"
},
{
label: "Mexico"
}
]
}
]
}
]
});
//Layout Panel *-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
f = new PMUI.form.Form({
onSubmit: function() {
console.log("submitting...");
},
onChange: function(field, newValue, previousValue) {
console.log("The field " + field.getName() + " has changed from \"" + previousValue + "\" to \"" + newValue + "\"");
},
items: [
{
pmType: "dropdown",
label: "age",
options: [
{
label: "from 0 to 7 years old",
value: "0-7"
},
{
label: "from 8 to 13 years old",
value: "8-13"
}
],
name: "age",
helper: "Select one of the options",
required: true
},
{
pmType: "radio",
label: "Gender",
value: "m",
name: "gender",
required: true,
options: [
{
label: "Male",
value: "m"
},
{
label: "Female",
value: "f"
}
]
}
],
buttons: [
{
text: "Submit",
handler: function() {
f.submit();
console.log("submitting form...");
}
}
]
});
//Tab Item*--**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
a = new PMUI.panel.TabPanel({
height : 400,
width : 600,
items:[
{
icon :'pmui-gridpanel-engranaje',
title:'Tree Panel',
panel:tree
},
{
icon :'pmui-gridpanel-engranaje',
title:'Form Panel',
panel:p
},
{ icon :'pmui-gridpanel-engranaje',
title:'Form',
panel:f
}
],
itemsPosition : {position:"left",percentageWidth : 22}
//itemsPosition : 'top'
});
//document.body.appendChild(a.getHTML());
//a.defineEvents();
w = new PMUI.ui.Window({
title: "TabPanel",
width: 650,
height: 460,
footerHeight: 'auto',
bodyHeight: 'auto',
modal:true,
buttons: [
{
text: 'Saves',
handler: function() {
alert('save');
}
},
{
text: 'Close',
handler: function() {
alert('close');
}
},
{
text: "third button"
}
],
buttonsPosition : 'center',
spaceButtons : 30
});
w.open();
w.addItem(a);
w.defineEvents();
* In the example, we use as a treePanel panels, FormPanel and layoutPanel, each TabItem can also use a className
* to put an icon, see {@link PMUI.item.TabItem}, {@link PMUI.item.TabItem#cfg-icon icon}. The pocision of items [TabItem]
* can be "top", "left", "right" and "bottom", see the configuration options.
*
* Note: used a window for a best visualisation.
*
* @cfg {Array} [items = []]
* The items should be a Array, that Array should have Object JSON for config of the TabItems
* for more detail see {@link PMUI.item.TabItem}
*
* @cfg {String|Object} [itemsPosition = 'top']
* This option config can be of type:
*
* Object, when position is "left" or "right", example:
* itemsPosition: {position: "left", percentageWidth: 20}
* itemsPosition: {position: "right", percentageWidth: 20}
* JSON options:
* position: must be a string (can only be "left" or "right")
* percentageWidth: must be a number between 0 and 100
*
* String, when the position is "top" or "bottom", example:
* itemsPosition: "top"
* itemsPosition: "bottom"
* percentageWidth is not require
*/
var TabPanel = function (settings) {
TabPanel.superclass.call(this, settings);
/**
* Defines the tabpanel panelContainer HTML Element where is fix the content of the family Panel
* @type {HTMLElement}
*/
this.dom = {};
/**
* a property to assign events to see if the tabs is selected or not
* @type {PMUI.ite.TabItem}
*/
this.selectedTab = null;
/**
* a property that can be true or false values, when set to true indicates
* that the items will be collapsible
* @type {boolean}
*/
this.collapsible = null;
/**
* this posision items should be 'top', 'left', 'right' or 'bottom'
* @type {String}
*/
this.itemsPosition = null;
/**
* represents the space busy for the containerItems HTMLElement, default value
* this property is 0, when itemsPosition is "right" or "left " this value is different 0
* @type {Number}
*/
this.percentageWidth = null;
this.onTabClick = null;
TabPanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.core.Panel", TabPanel);
TabPanel.prototype.type = "tabpanel";
TabPanel.prototype.family = "tabpanel";
TabPanel.prototype.init = function (settings) {
var defaults = {
items: [],
itemsPosition: 'top',
onTabClick: null,
collapsible: true
};
jQuery.extend(true, defaults, settings);
this.setItems(defaults.items);
this.setItemsPosition(defaults.itemsPosition);
this.setOnTabClick(defaults.onTabClick);
this.setCollapsible(defaults.collapsible);
};
TabPanel.prototype.setCollapsible = function (collapsible) {
if (typeof collapsible !== 'boolean') {
throw new Error('setCollapsible(): the parameter collapsible should be type "boolean"');
}
this.collapsible = collapsible;
return this;
};
TabPanel.prototype.setOnTabClick = function (tabClick) {
if (typeof tabClick !== 'function' && tabClick !== null) {
throw new Error("setOnTabClick(): the parameter is not valid, should be a function.");
}
this.onTabClick = tabClick;
return this;
};
/**
* set in this method and call addItem to create the TabItem
* @param {Array} The JSON array with objects with parameters for creating TabItem
* @chainable
*/
TabPanel.prototype.setItems = function (items) {
var i;
if (jQuery.isArray(items)) {
this.clearItems();
for (i = 0; i < items.length; i += 1) {
this.addItem(items[i]);
}
} else {
throw new Error("setTabs(): the parameter is not a valid, should to be as Array");
}
for (i = 0; i < this.items.getSize(); i += 1) {
if (i == 0) {
this.items.get(0).select();
} else {
this.items.get(i).deselect();
}
}
return this;
};
/**
* [onTabClickHandler description]
* @return {[type]} [description]
* @private
*/
TabPanel.prototype.onTabClickHandler = function () {
var that = this;
return function (tabItem) {
if (typeof that.onTabClick === 'function') {
that.onTabClick(tabItem);
}
};
};
/**
* Add an item to the panel.
* @param {PMUI.item.TabItem} item
* It can be a valid JSON object or an object that inherits from {@link PMUI.item.TabItem}.
* @chainable
*/
TabPanel.prototype.addItem = function (item) {
var tabItem;
item.onSelect = this.onTabSelectHandler();
tabItem = new PMUI.item.TabItem(item);
tabItem.deselect();
tabItem.setOnClick(this.onTabClickHandler());
this.items.insert(tabItem);
if (this.html) {
this.dom.listContainer.appendChild(tabItem.getHTML());
this.updatePosition();
if (this.eventsDefined) {
tabItem.defineEvents();
}
}
return this;
};
/**
* @event
* defines the function to be determined in each tabItem when it is selected, to
* call the event set
* @chainable
*/
TabPanel.prototype.onTabSelectHandler = function () {
var that = this,
i;
return function () {
if (that.selectedTab) {
jQuery(that.selectedTab.panel.getHTML()).detach();
}
that.selectedTab = this;
for (i = 0; i < that.items.getSize(); i += 1) {
if (that.items.get(i) !== that.selectedTab) {
that.items.get(i).deselect();
}
}
jQuery(that.dom.panelContainer).append(this.getPanel().getHTML());
if (!this.getPanel().eventsDefined) {
this.getPanel().defineEvents();
}
};
};
/**
* @method setItemsPosition
* changes the position of the elements [PM UI.item.TabItem] within the sizes assigned to TabPanel.
* @param {String|Object}
* Position where items are positioned, the more info about input value see {@link PMUI.panel.TabPanel}
* config option: {@link PMUI.panel.TabPanel#cfg-itemsPosition itemsPosition}
* @chainable
*/
TabPanel.prototype.setItemsPosition = function (itemsPosition) {
var validPosition,
percentageWidth;
if (typeof itemsPosition == "string" || typeof itemsPosition == "object") {
if (typeof itemsPosition == "object") {
if (itemsPosition.position == "left" || itemsPosition.position == "right") {
this.itemsPosition = itemsPosition.position;
this.percentageWidth = itemsPosition.percentageWidth || 20;
} else {
throw new Error("setItemsPosition(): JSON no valid, send position : 'left' or 'right' with percentage width");
}
} else {
if (itemsPosition == "top" || itemsPosition == "bottom") {
this.itemsPosition = itemsPosition;
this.percentageWidth = 0;
} else {
throw new Error("setItemsPosition(): no valid, send position : 'top' or 'bottom' ");
}
}
this.style.removeClasses(['pmui-tabpanel-left', 'pmui-tabpanel-right', 'pmui-tabpanel-top', 'pmui-tabpanel-bottom']);
this.style.addClasses(['pmui-tabpanel-' + this.itemsPosition]);
if (this.html) {
this.updatePosition();
}
} else {
throw new Error("setItemsPosition(): the parameter is not a valid, should to be as string or Object JSON");
}
return this;
};
/**
* @private
* update the position for the HTMLElement.
* exists the next elements HTMLElements:
*
* - containerItems
* - listContainer
* - barCollaspible
* - panelContainer
*
* this elements should update your position in the DOM, also update dimensions respect to the property itemsPosition
* @chainable
*/
TabPanel.prototype.updatePosition = function () {
var i,
itemsDisplay;
switch (this.itemsPosition) {
case 'top' :
case 'left':
$(this.dom.containerItems).prepend(this.dom.listContainer);
$(this.html).prepend(this.dom.containerItems);
this.dom.containerItems.style.display = this.itemsPosition === 'left' ? "inline-block" : "block";
this.dom.listContainer.style.display = this.itemsPosition === 'left' ? "inline-block" : "block";
this.dom.collapsibleBar.style.display = this.itemsPosition === 'left' ? "inline-block" : "none";
this.dom.panelContainer.style.display = this.itemsPosition === 'left' ? "inline-block" : "block";
$(this.dom.containerItems).css("float", this.itemsPosition === 'left' ? "left" : "");
itemsDisplay = this.itemsPosition === 'left' ? "block" : "inline-block";
for (i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).setDisplay(itemsDisplay);
}
if (this.itemsPosition == 'left') {
this.dom.containerItems.style.width = this.getWidth() * (this.percentageWidth / 100) + 'px';
this.dom.containerItems.style.height = this.getHeight() + "px";
this.dom.panelContainer.style.width = this.getWidth() - (this.getWidth() * (this.percentageWidth / 100)) + 'px';
this.dom.panelContainer.style.height = this.getHeight() + 'px';
this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
jQuery(this.dom.listContainer).css("float", 'left');
for (i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).setWidth(this.getWidth() * (this.percentageWidth / 100) - 12);
}
} else {
this.dom.containerItems.style.width = this.getWidth() + 'px';
this.dom.containerItems.style.height = '40px';
this.dom.panelContainer.style.width = this.getWidth() + 'px';
this.dom.panelContainer.style.height = this.getHeight() - 40 + 'px';
this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
$(this.dom.listContainer).css("float", 'left');
for (i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).setWidth('auto');
}
}
break;
case 'right' :
case 'bottom' :
$(this.dom.containerItems).prepend(this.dom.collapsibleBar);
$(this.html).prepend(this.dom.panelContainer);
this.dom.containerItems.style.display = this.itemsPosition === 'right' ? "inline-block" : "block";
this.dom.listContainer.style.display = this.itemsPosition === 'right' ? "inline-block" : "block";
this.dom.collapsibleBar.style.display = this.itemsPosition === 'right' ? "inline-block" : "none";
this.dom.panelContainer.style.display = this.itemsPosition === 'right' ? "inline-block" : "block";
$(this.dom.containerItems).css("float", this.itemsPosition === 'right' ? "right" : "");
itemsDisplay = this.itemsPosition === 'right' ? "block" : "inline-block";
for (i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).setDisplay(itemsDisplay);
}
if (this.itemsPosition == 'right') {
this.dom.containerItems.style.width = this.getWidth() * (this.percentageWidth / 100) + 'px';
this.dom.containerItems.style.height = this.getHeight() + "px";
this.dom.panelContainer.style.width = this.getWidth() - (this.getWidth() * (this.percentageWidth / 100)) + 'px';
this.dom.panelContainer.style.height = this.getHeight() + 'px';
this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
$(this.dom.listContainer).css("float", 'right');
for (i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).setWidth(this.getWidth() * (this.percentageWidth / 100));
}
} else {
this.dom.containerItems.style.width = this.getWidth() + 'px';
this.dom.containerItems.style.height = '40px';
this.dom.panelContainer.style.width = this.getWidth() + 'px';
this.dom.panelContainer.style.height = this.getHeight() + 'px';
this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
$(this.dom.listContainer).css("float", 'none');
for (i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).setWidth('auto');
}
}
break;
}
return this;
};
/**
* Create html for each item to container
* @return {Object} the current object html
*/
TabPanel.prototype.createHTML = function () {
var containerItems,
panelContainer,
collapsibleBar,
listContainer,
i;
if (this.html) {
return html;
}
this.html = PMUI.createHTMLElement('div');
this.html.className = 'pmui-tabpanel';
this.html.id = this.id;
containerItems = PMUI.createHTMLElement('div');
listContainer = PMUI.createHTMLElement('ul');
collapsibleBar = PMUI.createHTMLElement('div');
panelContainer = PMUI.createHTMLElement('div');
containerItems.className = "pmui-tabpanel-tabs_container";
listContainer.className = "pmui-tabpanel-tabs";
collapsibleBar.className = "pmui-collapsibleBar";
panelContainer.className = "pmui-tabpanel-container";
this.dom.containerItems = containerItems;
this.dom.listContainer = listContainer;
this.dom.collapsibleBar = collapsibleBar;
this.dom.panelContainer = panelContainer;
this.dom.panelContainer.style.overflow = 'auto';
this.setItems(this.items.asArray().slice(0));
this.dom.containerItems.appendChild(this.dom.listContainer);
this.dom.containerItems.appendChild(this.dom.collapsibleBar);
this.html.appendChild(this.dom.containerItems);
this.html.appendChild(this.dom.panelContainer);
if (this.itemsPosition == 'left' || this.itemsPosition == 'right') {
this.setItemsPosition({position: this.itemsPosition, percentageWidth: this.percentageWidth});
} else {
this.setItemsPosition(this.itemsPosition);
}
this.applyStyle();
if (this.eventsDefined) {
this.defineEvents();
}
for (i = 0; i < this.items.getSize(); i += 1) {
if (!this.getItems()[i].visible) {
this.hideTab(i)
}
}
return this.html;
};
/**
* Executes children events defined
* @chainable
*/
TabPanel.prototype.defineEvents = function () {
var that = this,
i,
auxPercentageWidth;
this.removeEvents().eventsDefined = true;
if (this.dom.collapsibleBar && this.collapsible) {
this.addEvent('click').listen(this.dom.collapsibleBar, function (e) {
e.preventDefault();
e.stopPropagation();
auxPercentageWidth = that.percentageWidth;
if (that.collapsible) {
that.setItemsPosition({position: that.itemsPosition, percentageWidth: 9});
that.collapsible = false;
that.style.addClasses(['pmui-collipsable']);
} else {
that.setItemsPosition({position: that.itemsPosition, percentageWidth: auxPercentageWidth});
that.style.removeClasses(['pmui-collipsable']);
that.collapsible = true;
}
that.percentageWidth = auxPercentageWidth;
if (typeof that.onTabClick == 'function') {
}
});
for (i = 0; i < that.items.getSize(); i += 1) {
that.items.get(i).defineEvents();
}
}
return this;
};
TabPanel.prototype.setWidth = function (width) {
TabPanel.superclass.prototype.setWidth.call(this, width);
this.updatePosition();
return this;
};
TabPanel.prototype.setHeight = function (height) {
TabPanel.superclass.prototype.setHeight.call(this, height);
this.updatePosition();
return this;
};
/**
* hides a TabItem, if hidden the last TabItem passes focus to the previous,
* otherwise the selection will move to the next always
* @param {Number} index Is the selection of the TabItem that is concealed,
* must be within the range of the length of existing items
* @param {[type]} current [when hidden a tabItem, can fix focus in other tabItem]
* @chainable
*/
TabPanel.prototype.hideTab = function (index, current) {
var item,
currentTab,
i,
len;
for (i = 0; i < this.getItems().length; i += 1) {
if (i === index) {
item = this.getItems()[i];
}
}
if (!current) {
if (item.selected) {
len = index - 1;
while (len > -1) {
if (this.getItems()[len].visible === true) {
currentTab = this.getItems()[len];
len = -1;
} else {
len--;
}
}
if (!currentTab) {
i = index + 1;
while (i < this.items.getSize()) {
if (this.getItems()[i].visible === true) {
currentTab = this.getItems()[i];
i = this.items.getSize();
} else {
i++;
}
}
}
}
} else {
currentTab = this.getItems()[current];
}
item.setVisible(false);
item.panel.setVisible(false);
if (currentTab) {
currentTab.select();
}
return this;
}
/**
* tab shows a hidden item
* @param {Number} index Este valor es necesario para mostrar TabItem, debe estar dentro de un rango de la longitud de los elementos
* @chainable
*/
TabPanel.prototype.showTab = function (index) {
if (typeof index == 'number') {
if (!this.getItems()[index].visible) {
this.getItems()[index].setVisible(true);
this.getItems()[index].panel.setVisible(true);
}
}
return this;
};
TabPanel.prototype.itemClick = function (index) {
$($(this.dom.listContainer).find('a')[index]).trigger('click');
return this;
};
if (typeof exports != 'undefined') {
module.exports = TabPanel;
}
PMUI.extendNamespace("PMUI.panel.TabPanel", TabPanel);
}());
(function () {
/**
* @class PMUI.panel.AccordionPanel
* @extends PMUI.core.Panel
*
* Class to handle an Accordion component, this is a container for {@link PMUI.item.AccordionItem AccordionItem}.
*
* The way of usage the class is showed on the example above
*
* @example
* accordion = new PMUI.panel.AccordionPanel({
* width: 400,
* height: 500,
* hiddenTitle: true,
* //multipleSelection : true,
* title: "My accordion",
* items: [
* {
* iconClass: "",
* title: "First item",
* selected: true
* },
* {
* iconClass: "pmui-icon pmui-icon-warning",
* title: "Second item",
* body: "Google Link",
* style: {
* cssProperties: {
* "background-color": "#f2eaea"
* }
* }
* },
* {
* title: "Third item "
* },
* {
* title: "Fourth"
* }, new PMUI.item.AccordionItem({
* title: "Five"
* })
* ],
* listeners: {
* select: function (obj, event) {
* console.log("Selected...", obj);
* }
* }
* });
* document.body.appendChild(accordion.getHTML());
* accordion.defineEvents();
*
*
* @constructor
* Creates a new component
* @param {Object} [settings] settings The configuration options may be specified as the follow sentence
* {
* items: [
* {
* title: "First element",
* body: item // This item can be any element that inherit from {@link PMUI.core.Panel Panel}
* },
* {
* title: "Second element"
* }
* ]
* }
* @cfg {String} name The name for the field.
*
* @cfg {Boolean} [hiddenTitle=true] Represents wether the title of the component will be showed, the
* otherwise the panel will rendered without title.
* @cfg {Array} [items=[]] The array contains the basic elements for {@PMUI.panel.AccordionPanel AccordionPanel class}
* multipleSelection.
* @cfg {String} [title=""] The title for the panel.
* @cfg {Number} [heightItem = 'current height * 0.3'] Represents the height for every item
* @cfg {Object} factory If it necessary is possible replace the current elements by default.
* factory: {
* products: {
* 'accordionitem': PMUI.item.AccordionItem
* },
* defaultProduct: "accordionitem"
* }
*
*/
var AccordionPanel = function (settings) {
AccordionPanel.superclass.call(this, settings);
/**
* @property {String} [title= ""] The text represents the title of the panel,
* Whether the {PMUI.panel.AccordionPanel this.hiddenTitle} is false.
*/
this.title = null;
/**
* @property {Boolean} [hiddenTitle=false] The property represents whether the title
* will be showed.
*/
this.hiddenTitle = null;
/**
* @property {Boolean} [multipleSelection=false] If the property is enabled, the {@link PMUI.item.AccordionItem items} can
* be selected more than one time
*/
this.multipleSelection = null;
/**
* @property {Object} [listeners] A config object containing one or more handlers to be added
* to this object during initialization. by default the 'select' handler is enabled
*/
this.listeners = {
/**
* @event select
* Fires when a {@link PMUI.item.AccordionItem component} has been selected.
* @param {Object} accordionItem {@link PMUI.item.AccordionItem AccordionItem}
* @param {Object} event event
*/
select: function () {
}
};
/**
* @property {HTMLElement} [header=null]
* The property encapsulate the HTMLElement header for the panel.
*/
this.header = null;
/**
* @property {HTMLElement} [body=null]
* The property is related to HTMLElement body that will be used by
* {@link PMUI.item.AccordionItem items}
*/
this.body = null;
/**
* @property {HTMLElement} [footer=null]
* The property encapsulate the HTMLElement footer for the panel.
*/
this.footer = null;
/**
* @property {String} [iconClass='pmui-accordion-panel-icon']
* Represents the class name for the icon.
* It can be replaced on the config JSON if it is necessary
* @private
*/
this.iconClass = null;
/**
* @property {String} [headerClass='pmui-accordion-panel-header']
* Represents the class name for the header of the panel.
* It can be replaced on the config JSON if it is necessary
* @private
*/
this.headerClass = null;
/**
* @property {String} [bodyClass='pmui-accordion-panel-body']
* Represents the class name for the body of the panel.
* It can be replaced on the config JSON if it is necessary
* @private
*/
this.bodyClass = null;
/**
* @property {String} [footerClass='pmui-accordion-panel-footer']
* Represents the class name for footer of the panel.
* It can be replaced on the config JSON if it is necessary
* @private
*/
this.footerClass = null;
/**
* @property {String} [containerClass='pmui-accordion-panel-container']
* Represents the class name for the father container.
* It can be replaced on the config JSON if it is necessary
* @private
*/
this.containerClass = null;
/**
* @property {Number} [heightItem = 'current height * 0.3']
* Represents the size height for every {@link PMUI.item.AccordionItem item}
*/
this.heightItem = null;
/**
* determines whether the items of the accordion panel is automatically set when adding a new item
* @type {Boolean}
*/
this.selfAdjusting = null;
AccordionPanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Panel', AccordionPanel);
/**
* Defines the object type of the element
*/
AccordionPanel.prototype.type = 'Accordion';
/**
* Defines the object family of the element
*/
AccordionPanel.prototype.family = 'Panel';
AccordionPanel.prototype.init = function (settings) {
var defaults = {
multipleSelection: false,
hiddenTitle: false,
title: '',
iconClass: 'pmui-accordion-panel-icon',
headerClass: 'pmui-accordion-panel-header',
bodyClass: 'pmui-accordion-panel-body',
footerClass: 'pmui-accordion-panel-footer',
containerClass: 'pmui-accordion-panel-container',
heightItem: this.height * 0.3,
items: [],
factory: {
products: {
'accordionitem': PMUI.item.AccordionItem
},
defaultProduct: "accordionitem"
},
listeners: {
select: function () {
}
},
selfAdjusting: false
};
jQuery.extend(true, defaults, settings);
this.setFactory(defaults.factory)
.setMultipleSelection(defaults.multipleSelection)
.setHiddenTitle(defaults.hiddenTitle)
.setTitle(defaults.title)
.setIconClass(defaults.iconClass)
.setHeaderClass(defaults.headerClass)
.setBodyClass(defaults.bodyClass)
.setFooterClass(defaults.footerClass)
.setContainerClass(defaults.containerClass)
.setHeightItem(defaults.heightItem)
.setChildren(defaults.items)
.setListeners(defaults.listeners)
.setSelfAdjusting(defaults.selfAdjusting);
};
AccordionPanel.prototype.adjustHeightItems = function () {
var maxHeight = this.height,
headPanel = jQuery(this.header).outerHeight(),
footerPanel = jQuery(this.footer).outerHeight(),
headItems = 0,
footerItems = 0,
bodyHeight = 0,
items,
i,
j;
items = this.getItems();
for (i = 0; i < items.length; i += 1) {
headItems += jQuery(items[i].header.html).outerHeight() + 2;
if (items[i].footer) {
footerItems += jQuery(items[i].footer.html).outerHeight();
}
}
bodyHeight = maxHeight - (headPanel + footerPanel + headItems + footerItems);
for (j = 0; j < items.length; j += 1) {
jQuery(items[j].body.html).height(bodyHeight);
}
return this;
};
/**
* Clear all the object's current child items and add new ones
* @param {Array} items An array where each element can be a {Element} object or a JSON object
* where is specified the setting for the child item to be added
* @chainable
*/
AccordionPanel.prototype.setChildren = function (items) {
var i;
if (jQuery.isArray(items)) {
this.clearItems();
for (i = 0; i < items.length; i += 1) {
this.addItem(items[i]);
}
}
return this;
};
/**
* Defines and sets whether the {@link PMUI.item.AccordionItem items} from {@link PMUI.panel.AccordionPanel Accordion}
* will be selectable
* @param {Boolean} parameter
* @return {PMUI.panel.AccordionPanel} this
*/
AccordionPanel.prototype.setMultipleSelection = function (parameter) {
if (typeof parameter === 'boolean') {
this.multipleSelection = parameter;
}
return this;
};
/**
* Sets whether the title will be rendered or not inside the header
* @param {Boolean} enabled
*/
AccordionPanel.prototype.setHiddenTitle = function (enabled) {
if (typeof enabled === 'boolean') {
this.hiddenTitle = enabled;
if (this.html) {
if (this.hiddenTitle === true) {
this.header.setAttribute("style", "display:none")
} else {
this.header.removeAttribute("style");
}
this.adjustHeightItems();
}
}
return this;
};
/**
* Sets the label for the Accordion title
* @param {String} title
*/
AccordionPanel.prototype.setTitle = function (title) {
if (typeof title === "string") {
this.title = title;
if (this.html) {
this.header.children[1].innerHTML = title;
}
}
return this;
};
/**
* Sets the class name for the header
* @param {String} iconCls
* @private
*/
AccordionPanel.prototype.setIconClass = function (iconCls) {
this.iconClass = iconCls;
return this;
};
/**
* Sets the class name for the Accordion's header
* @param {String} class name
* @private
*/
AccordionPanel.prototype.setHeaderClass = function (className) {
if (typeof className === 'string') {
this.headerClass = className;
}
return this;
};
/**
* Sets the class name for the Accordion's body
* @param {String} class name
* @private
*/
AccordionPanel.prototype.setBodyClass = function (className) {
if (typeof className === 'string') {
this.bodyClass = className;
}
return this;
};
/**
* Sets the class name for the Accordion's footer
* @param {String} class name
* @private
*/
AccordionPanel.prototype.setFooterClass = function (className) {
if (typeof className === 'string') {
this.footerClass = className;
}
return this;
};
/**
* Sets the class name for the object's container
* @param {String} class name
* @private
*/
AccordionPanel.prototype.setContainerClass = function (className) {
if (typeof className === 'string') {
this.containerClass = className;
}
return this;
};
/**
* Sets the height for every {@link PMUI.item.AccordionItem item}
* @param {String} height
*/
AccordionPanel.prototype.setHeightItem = function (height) {
this.heightItem = height;
return this;
};
/**
* Sets the listeners for the object
* @param {Object} events
*/
AccordionPanel.prototype.setListeners = function (events) {
if (typeof events === 'object') {
this.listeners = events;
}
return this;
};
/**
* Creates all the elements with their corresponding attributes
* for make a container that encapsulate {@link PMUI.item.AccordionItem items}
* @return {HTMLElement} HTMLElement
*/
AccordionPanel.prototype.createHTML = function () {
var container,
header,
body,
footer,
title,
icon,
itemsLen,
i;
if (this.html) {
return this.html;
}
container = AccordionPanel.superclass.prototype.createHTML.call(this);
header = PMUI.createHTMLElement("div");
header.setAttribute('class', this.headerClass);
body = PMUI.createHTMLElement("div");
body.setAttribute('class', this.bodyClass);
footer = PMUI.createHTMLElement("div");
footer.setAttribute('class', this.footerClass);
title = PMUI.createHTMLElement("span");
title.innerHTML = this.title;
icon = PMUI.createHTMLElement("span");
icon.setAttribute('class', this.iconClass);
header.appendChild(icon);
header.appendChild(title);
if (this.hiddenTitle) {
header.setAttribute("style", "display:none;");
}
itemsLen = this.items.getSize();
if (typeof itemsLen === 'number') {
for (i = 0; i < itemsLen; i += 1) {
body.appendChild(this.getItems()[i].getHTML());
}
}
this.header = header;
this.body = body;
this.footer = footer;
container.appendChild(header);
container.appendChild(body);
container.appendChild(footer);
container.className = this.containerClass;
this.containmentArea = body;
this.html = container;
return this.html;
};
/**
* Defines events related to the component and call to every DefineEvents method from
* {@link PMUI.item.AccordionItem items}
* @return {Object} {@link PMUI.panel.AccordionPanel Accordion}
*/
AccordionPanel.prototype.defineEvents = function () {
var j,
children,
that = this;
if (that.items.getSize() > 0 && this.eventsDefined !== true) {
children = that.getItems();
for (j = 0; j < children.length; j += 1) {
children[j].defineEvents();
}
if (this.selfAdjusting) {
this.adjustHeightItems();
}
this.eventsDefined = true;
}
return this;
};
AccordionPanel.prototype.setSelfAdjusting = function (value) {
if (typeof value === "boolean") {
this.selfAdjusting = value;
}
return this;
};
PMUI.extendNamespace('PMUI.panel.AccordionPanel', AccordionPanel);
if (typeof exports !== "undefined") {
module.exports = AccordionPanel;
}
}());
(function () {
/**
* @class PMUI.panel.ButtonPanel
* A panel that contains instances of {@link PMUI.ui.Button Button} and instances of {@link PMUI.ui.TextLabel Label}.
* @extends {PMUI.core.Panel}
*
* Usage example:
*
* @example
* var a = new PMUI.panel.ButtonPanel({
* items: [
* {
* pmType: 'button',
* text: 'Click Me'
* },
* {
* pmType: 'label',
* text: 'and then'
* },
* {
* pmType: 'button',
* text: 'Click Me'
* }
* ]
* });
* document.body.appendChild(a.getHTML());
* a.defineEvents();
*
* @constructor
* Creates a new instance of the class.
* @param {Object} [settings={}] An object literal with the config options for the class.
*
* @cfg {Array} [items=[]] An array in which each element can be:
*
* - An object literal, in this case it can have the config options for create a {@link PMUI.ui.Button Button} or a
* {@link PMUI.ui.TextLabel Label}, additionally it must include the respective pmType ('button' for Button and 'label'
* for Label).
*
* -A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of {@link
* PMUI.ui.TextLabel Label}.
*/
var ButtonPanel = function (settings) {
ButtonPanel.superclass.call(this, settings);
this.alignment = null;
ButtonPanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Panel', ButtonPanel);
/**
* The object's type.
* @type {String}
*/
ButtonPanel.prototype.type = 'ButtonPanel';
/**
* Initializes the object.
* @param {Object} [settings={}] An object literal with the config options the new object will be initialized
* with.
* @chainable
* @private
*/
ButtonPanel.prototype.init = function (settings) {
var defaults = {
items: [],
factory: {
products: {
'button': PMUI.ui.Button,
'label': PMUI.ui.TextLabel
},
defaultProduct: 'button'
},
layout: 'box',
alignment: 'center'
};
jQuery.extend(true, defaults, settings);
this.setFactory(defaults.factory)
.setLayout(defaults.layout)
.setItems(defaults.items)
.setAlignment(defaults.alignment);
return this;
};
/**
* Sets the alignment for the items in the panel.
* @param {String} alignment A string which can take the values of 'center', 'left', 'right'.
*/
ButtonPanel.prototype.setAlignment = function (alignment) {
if (alignment === 'center' || alignment === 'left' || alignment === 'right') {
this.alignment = alignment;
this.style.addProperties({'text-align': alignment});
return this;
}
throw new Error('setAlignment(): The parameter must be "center" or "left" or "right".');
};
PMUI.extendNamespace('PMUI.panel.ButtonPanel', ButtonPanel);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ButtonPanel;
}
}());
(function () {
/**
* @class PMUI.panel.List
*/
var ListPanel = function (settings) {
this.dom = {};
ListPanel.superclass.call(this, settings);
this.orientation = null;
this.actions = null;
this.dataItems = null;
this.filterable = null;
this.filterCriteria = null;
this.filterControl = null;
this.filterPlaceholder = null;
this.statusBarMessage = null;
this.visibleHeader = null;
this.visibleStatusBar = null;
this.listHeight = null;
this.onItemClick = null;
this.filteredItems = null;
ListPanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Container', ListPanel);
ListPanel.prototype.type = "ListPanel";
ListPanel.prototype.init = function (settings) {
var defaults = {
orientation: 'vertical',
actions: [],
dataItems: null,
itemTextDataBind: null,
statusBarMessage: null,
title: "[untitled list]",
filterPlaceholder: "Search",
visibleHeader: true,
visibleStatusBar: true,
filterable: true,
listHeight: "auto",
onItemClick: null
};
this.filteredItems = [];
jQuery.extend(true, defaults, settings);
this.filterControl = new PMUI.control.TextControl({
onKeyUp: this.onFilterControlChangeHandler(),
style: {
cssClasses: [
"pmui-listpanel-search"
]
}
});
this.setItemTextDataBind(defaults.itemTextDataBind)
.setOrientation(defaults.orientation)
.setElementTag(defaults.elementTag)
.setActions(defaults.actions)
.setStatusBarMessage(defaults.statusBarMessage)
.setFilterPlaceholder(defaults.filterPlaceholder)
.setTitle(defaults.title)
.setListHeight(defaults.listHeight)
.setOnItemClickHandler(defaults.onItemClick);
if (defaults.filterable) {
this.enableFiltering();
} else {
this.disableFiltering();
}
if (defaults.visibleHeader) {
this.showHeader();
} else {
this.hideHeader();
}
if (defaults.visibleStatusBar) {
this.showStatusBar();
} else {
this.hideStatusBar();
}
};
ListPanel.prototype.onItemClickHandler = function (listItem) {
if (typeof this.onItemClick === 'function') {
this.onItemClick(this, listItem);
}
return this;
};
ListPanel.prototype.setOnItemClickHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnItemClickHandler(): The parameter must be a function or a handler.");
}
this.onItemClick = handler;
return this;
};
ListPanel.prototype.setListHeight = function (height) {
if (typeof height === 'number') {
this.listHeight = height;
} else if (/^\d+(\.\d+)?px$/.test(height)) {
this.listHeight = parseInt(height, 10);
} else if (/^\d+(\.\d+)?%$/.test(height)) {
this.listHeight = height;
} else if (/^\d+(\.\d+)?em$/.test(height)) {
this.listHeight = height;
} else if (height === 'auto' || height === 'inherit') {
this.listHeight = height;
} else {
throw new Error('setHeight: height param is not a number');
}
if (this.dom.list) {
this.dom.list.style.height = height + (typeof height === 'number' ? 'px' : '');
}
return this;
};
ListPanel.prototype.hideStatusBar = function () {
this.visibleStatusBar = false;
if (this.dom.statusBar) {
this.dom.statusBar.style.display = 'none';
}
return this;
};
ListPanel.prototype.showStatusBar = function () {
this.visibleStatusBar = true;
if (this.dom.statusBar) {
this.dom.statusBar.style.display = '';
}
return this;
};
ListPanel.prototype.showHeader = function () {
this.visibleHeader = true;
if (this.dom.header) {
this.dom.header.style.display = '';
}
return this;
};
ListPanel.prototype.hideHeader = function () {
this.visibleHeader = false;
if (this.dom.header) {
this.dom.header.style.display = 'none';
}
return this;
};
ListPanel.prototype.enableFiltering = function () {
this.filterable = true;
if (this.dom.toolbar) {
this.dom.toolbar.style.display = '';
}
return this;
};
ListPanel.prototype.disableFiltering = function () {
this.filterable = false;
if (this.dom.toolbar) {
this.dom.toolbar.style.display = 'none';
}
this.clearFilter();
return this;
};
ListPanel.prototype.setTitle = function (title) {
if (typeof title !== 'string') {
throw new Error("setTitle(): The parameter must be a string.");
}
this.title = title;
if (this.html) {
this.dom.header.textContent = title;
}
return this;
};
ListPanel.prototype.setFilterPlaceholder = function (filterPlaceholder) {
this.filterControl.setPlaceholder(filterPlaceholder);
return this;
};
ListPanel.prototype.onFilterControlChangeHandler = function () {
var that = this;
return function () {
var nextFilter = this.getHTML().value;
if (that.filterCriteria !== nextFilter) {
that.filter(nextFilter);
}
};
};
ListPanel.prototype.setStatusBarMessage = function (statusBarMessage) {
if (!(statusBarMessage === null || typeof statusBarMessage === 'function')) {
throw new Error("setStatusBarMessage(): The parameter must be a function or null.")
}
this.statusBarMessage = statusBarMessage;
return this;
};
ListPanel.prototype.setItemTextDataBind = function (itemTextDataBind) {
var i,
size;
if (!(itemTextDataBind === null || typeof itemTextDataBind === 'string')) {
throw new Error("setItemTextDataBind(): the parameter must be a string or null.");
}
this.itemTextDataBind = itemTextDataBind;
size = this.items.getSize();
for (i = 0; i < size; i += 1) {
this.items.get(i).setTextDataBind(itemTextDataBind);
}
return this;
};
ListPanel.prototype.setActions = function (actions) {
if (!jQuery.isArray(actions)) {
throw new Error("setActions(): The parameter must be an array.");
}
this.actions = actions;
return this;
};
ListPanel.prototype.addItem = function (item) {
if (item instanceof PMUI.item.ListItem) {
item.setActions(this.actions).setTextDataBind(this.itemTextDataBind);
} else if (!item.actions) {
item.actions = this.actions;
item.setTextDataBind = this.itemTextDataBind;
}
ListPanel.superclass.prototype.addItem.call(this, item);
return this;
};
ListPanel.prototype.setItems = function (items) {
if (this.actions !== null) {
ListPanel.superclass.prototype.setItems.call(this, items);
this.updateStatusBar();
}
return this;
};
ListPanel.prototype.updateStatusBar = function () {
var msg,
size;
if (this.dom.statusBar) {
size = this.filterCriteria ? jQuery(this.containmentArea).find('>li').length : this.items.getSize();
if (typeof this.statusBarMessage === 'function') {
msg = this.statusBarMessage(this, size, !!this.filterCriteria, this.filterCriteria);
} else {
msg = size + (this.filterCriteria ? " result(s) matching \"" + this.filterCriteria + "\"" : " item(s).");
}
this.dom.statusBar.textContent = msg;
}
return this;
};
ListPanel.prototype.setOrientation = function (orientation) {
var items,
displayBlock,
i;
if (orientation !== 'horizontal' && orientation !== 'vertical') {
throw new Error("setOrientation(): The parameter must be \"horizontal\" or \"vertical\"");
}
this.orientation = orientation;
displayBlock = orientation === 'vertical' ? 'block' : 'inline-block';
items = this.items.asArray();
for (i = 0; i < items.length; i += 1) {
items[i].setDisplay(displayBlock);
}
return this;
};
ListPanel.prototype.setFactory = function () {
this.factory = new PMUI.util.Factory({
products: {
'listitem': PMUI.item.ListItem
},
defaultProduct: 'listitem'
});
return this;
};
ListPanel.prototype.getData = function () {
var i,
items = this.items.asArray(),
data = [];
for (i = 0; i < items.length; i += 1) {
data.push(items[i].getData());
}
return data;
};
ListPanel.prototype.clearFilter = function () {
var i,
items = this.items.asArray();
this.filterCriteria = "";
if (this.html) {
for (i = 0; i < items.length; i += 1) {
jQuery(items[i].html).detach();
}
for (i = 0; i < items.length; i += 1) {
this.containmentArea.appendChild(items[i].getHTML());
}
}
this.updateStatusBar();
return this;
};
ListPanel.prototype.getFilteredItems = function () {
return this.filteredItems;
};
ListPanel.prototype.filter = function (criteria) {
var i,
regExp,
itemsCopy;
this.filteredItems = [];
if (typeof criteria === 'string') {
if (criteria === "") {
this.clearFilter();
return this;
}
this.filterCriteria = criteria;
} else if (typeof criteria === 'number') {
this.filterCriteria = criteria.toString(10);
} else {
throw new Error("filter(): The parameter must be a string or number");
}
if (!this.containmentArea) {
return this;
}
regExp = new RegExp(this.filterCriteria.replace(/([\\\.\[\]\^\$\(\)\?\*\+\|\{\}])/g, "\\\$1"), "i");
itemsCopy = this.items.asArray();
for (i = 0; i < itemsCopy.length; i += 1) {
jQuery(itemsCopy[i].html).detach();
}
for (i = 0; i < itemsCopy.length; i += 1) {
if (regExp.test(itemsCopy[i].text)) {
this.containmentArea.appendChild(itemsCopy[i].getHTML());
this.filteredItems.push(itemsCopy[i]);
}
}
this.updateStatusBar();
return this;
};
ListPanel.prototype.defineEvents = function () {
this.removeEvents().eventsDefined = true;
this.filterControl.defineEvents();
return this;
};
ListPanel.prototype.createHTML = function () {
var list,
toolbar,
statusBar,
header;
if (this.html) {
return this.html;
}
ListPanel.superclass.prototype.createHTML.call(this);
list = PMUI.createHTMLElement('ul');
list.className = 'pmui-listpanel-list';
toolbar = PMUI.createHTMLElement('div');
toolbar.className = 'pmui-listpanel-toolbar';
statusBar = PMUI.createHTMLElement('div');
statusBar.className = 'pmui-listpanel-statusbar';
header = PMUI.createHTMLElement('div');
header.className = 'pmui-listpanel-title';
this.containmentArea = list;
this.dom.list = list;
this.dom.toolbar = toolbar;
this.dom.statusBar = statusBar;
this.dom.header = header;
toolbar.appendChild(this.filterControl.getHTML());
this.html.appendChild(header);
this.html.appendChild(toolbar);
this.html.appendChild(list);
this.html.appendChild(statusBar);
this.setTitle(this.title)
.setListHeight(this.listHeight);
if (this.filterable) {
this.enableFiltering();
this.filter(this.filterCriteria || "");
} else {
this.disableFiltering();
}
if (this.visibleHeader) {
this.showHeader();
} else {
this.hideHeader();
}
if (this.visibleStatusBar) {
this.showStatusBar();
} else {
this.hideStatusBar();
}
this.defineEvents();
return this.html;
};
PMUI.extendNamespace('PMUI.panel.ListPanel', ListPanel);
}());
(function () {
/**
* @class PMUI.grid.GridPanel
* Class to display data in a grid format.
* @extends PMUI.core.Container
*
*
* Single grid example:
*
* @example
* var g;
*
* $(function(){
* g = new PMUI.grid.GridPanel({
* pageSize: 6,
* columns:[
* {
* title:'columna1',
* dataType:'string',
* columnData: "name"
* },
* {
* title:'columna2',
* dataType:'number',
* width : 150,
* columnData: "lastName",
* sortable: true
* },
* {
* title:'columna3',
* dataType:'button',
* width : "200px",
* onButtonClick: function(row, grid) {
* console.log(row, grid);
* }
* }
* ],
* dataItems: [
* {
* name: "Daniel",
* lastName: "Canedo"
* }, {
* name: "John",
* lastName: "McAllister"
* }, {
* name: "Ximena",
* lastName: "Jimenez"
* }, {
* name: "Fernando",
* lastName: "Fernandez"
* }, {
* name: "Rodrigo",
* lastName: "Rodriguez"
* }, {
* name: "Andrea",
* lastName: "Postigo"
* }, {
* name: "Hernando",
* lastName: "Hernandez"
* }, {
* name: "Ramiro",
* lastName: "Ramirez"
* }, {
* name: "Domingo",
* lastName: "Dominguez"
* }, {
* name: "Gonzalo",
* lastName: "Gonzales"
* }
* ]
* });
* document.body.appendChild(g.getHTML());
* g.defineEvents();
* });
*
* Example with two grids with interchangeable rows.
*
* @example
* var g, g2;
*
* $(function(){
* g2 = new PMUI.grid.GridPanel({
* behavior: 'dragdropsort',
* pageSize: 0,
* columns: [
* {
* title: 'lastName',
* columnData: 'lastName',
* sortable: true
* }
* ],
* dataItems: [
* {
* name: "Ramiro",
* lastName: "Ramirez"
* }, {
* name: "Pedro",
* lastName: "Pedraza"
* }, {
* name: "Domingo",
* lastName: "Dominguez"
* }, {
* name: "Oliva",
* lastName: "Olivares"
* }, {
* name: "Gonzalo",
* lastName: "Gonzales"
* }, {
* name: "Enrique",
* lastName: "Enriquez"
* }
* ],
* onDrop: function(container, draggableItem) {
* var subject = draggableItem.getData();
* subject = subject.name + " " + subject.lastName;
* console.log(subject + " was dropped on grid 1");
* },
* onSort: function(container, item, index) {
* var subject = item.getData();
* subject = subject.name + " " + subject.lastName;
* console.log(subject + "'s index has changed to " + index);
* }
* });
* g = new PMUI.grid.GridPanel({
* behavior: 'dragdropsort',
* pageSize:6,
* columns:[
* {
* title: 'Initials',
* columnData: function(data) {
* return data.name.substr(0, 1) + " " + data.lastName.substr(0, 1);
* }
* },
* {
* title:'Name',
* dataType:'string',
* columnData: "name"
* },
* {
* title:'LastName',
* dataType:'number',
* width : 150,
* columnData: "lastName",
* sortable: true
* },
* {
* title:'columna3',
* dataType:'button',
* width : "200px",
* onButtonClick: function(row, grid) {
* console.log(row, grid);
* }
* }
* ],
* dataItems: [
* {
* name: "Daniel",
* lastName: "Canedo"
* }, {
* name: "John",
* lastName: "McAllister"
* }, {
* name: "Ximena",
* lastName: "Jimenez"
* }, {
* name: "Fernando",
* lastName: "Fernandez"
* }, {
* name: "Rodrigo",
* lastName: "Rodriguez"
* }, {
* name: "Andrea",
* lastName: "Postigo"
* }, {
* name: "Hernando",
* lastName: "Hernandez"
* }
* ],
* onDrop: function(container, draggableItem) {
* var subject = draggableItem.getData();
* subject = subject.name + " " + subject.lastName;
* console.log(subject + " was dropped on grid 2");
* },
* onSort: function(container, item, index) {
* var subject = item.getData();
* subject = subject.name + " " + subject.lastName;
* console.log(subject + "'s index has changed to " + index);
* }
* });
* document.body.appendChild(g.getHTML());
* document.body.appendChild(g2.getHTML());
* g.defineEvents();
* g2.defineEvents();
* });
*
* @constructor
* Creates a new instance of GridPanel class.
* @param {Object} [settings=null] A JSON object with the config options.
*
* @cfg {Array} [columns=[]] An Array where each element is an object literal or an instance of
* {@link PMUI.grid.GridPanelColumn}. In case to be an object literal it must have {@link PMUI.grid.GridPanelColumn
* the config options for GridPanelColumn creation}.
* @cfg {Array} [items=[]] An Array where each of its elements can be an instance of PMUI.grid.GridPanelRow or a
* JSON object with the config options to build a new one.
* @cfg {Array} [dataItems=[]] An Array where each of its elements can be a JSON object with the data available for
* the item (row) that will be created.
* @cfg {Number} [pageSize=0] The size for every page in the table, if it is set to 0 (default) then there will no
* exists pagination.
* @cfg {Boolean} [draggable=false] If the grid will apply the row dragging behavior.
* @cfg {Boolean} [filterable=true] If the grid will have the filtering function enabled or not.
* @cfg {Number} [initialPage=0] The page number to show initially.
* @cfg {Function} [customStatusBar=null] The function to generate a custom message in the grid's status bar. This
* function must return a string with the text to be used as the message to be displayed in thegrid's status bar.
* When the function is called 5 parameters will be sent:
*
* - The current page (Number).
* - The page size (Number).
* - The number of items (Number).
* - If a criteria is being applied to the grid (Boolean).
* - The filter criteria applied to the grid (String).
*
* If you need access more grid's properties you can use 'this' to refer the grid since the function is called in
* the grid's context.
* @cfg {Object|PMUI.util.Style|null} [rowStyle=null] The style to be applied to the grid's rows. It can be:
*
* - An object literal: in this case the object can have the properties:
* - cssClasses: an array of strings, each string is a css class that will be applied to each row's html.
* - cssProperties: an object literal in which each property is a valid css property and its value is a valid
* css value for the respective css property.
*
* - An instance of the {@link PMUI.util.Style Style} class.
* - null, in this case no custom style will be applied to the html of the grid's rows.
*
* @cfg {String} [filterPlaceholder] Sets the placeholder for the filter textbox.
* @cfg {Boolean} [visibleHeaders=true] Turns on/off the visibility of the column headers.
* @cfg {String} [sortableItems='> tr'] Specifies which items inside the element should be sortable in case a
* sort behavior is applied. It must be a jQuery selector.
* @cfg {Function|null} [onEmpty=null] A callback function to be called everytime the grid's last item is removed.
* The value for this config option can be a function or the null constant for no callback execution. For more
* info about the paramerters sent to the function please read the {@link #event-onEmpty onEmpty} event
* documentation.
* @cfg {Function|null} [onRowClick=null] The callback function to be executed everytime the
* {@link #event-onRowClick onRowClick} event is fired. Read about this event to know about the parameters received
* by the callback function.
* @cfg {String|Function|null} [emptyMessage=null] The message to show when the grid is showing no items. It
* can be:
*
* - A String, in this case the string will be displayed in both cases, when the grid has no items and when no
* grid's item meet the filter.
* - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
* will receive two parameters: the {@link PMUI.grid.Grid grid}, a boolean If it is true it means the returned
* value will be used when a filter is applied, otherwise it means that the returned value will be used when
* there are no items in the grid.
* - null, in this case a default message will be used for each situation.
*/
var GridPanel = function (settings) {
/**
* A JSON object that contains the object's DOM components.
* @type {Object}
* @private
*/
this.dom = {};
/**
* The grid's list of {@link PMUI.grid.GridPanelColumn column objects}.
* @type {PMUI.ui.ArrayList}
* @private
*/
this.columns = new PMUI.util.ArrayList();
GridPanel.superclass.call(this, jQuery.extend({
factory: {
products: {
"GridPanelRow": PMUI.grid.GridPanelRow
},
defaultProduct: "GridPanelRow"
}
}, settings, {
elementTag: "div"
}));
this.pages = null;
this.displayedPages = null;
this.prevText = null;
this.nextText = null;
this.invertPageOrder = null;
this.labelMap = null;
this.hrefTextPrefix = null;
this.hrefTextSuffix = null;
this.nextAtFront = null;
this.edges = null;
this.useStartEdge = null;
this.ellipseText = null;
this.selectOnClick = null;
this.useEndEdge = null;
/**
* The number of rows to show in every page, 0 means no limit.
* @type {Number}
* @readonly
*/
this.pageSize = null;
/**
* If the grid has the filtering functiionality enabled or not.
* @type {Boolean}
* @readonly
*/
this.filterable = null;
/**
* The current filter criteria.
* @type {String}
* @readonly
*/
this.filterCriteria = null;
/**
* The current page being displayed.
* @type {Boolean}
* @readonly
*/
this.currentPage = null;
/**
* If the column sorting functionloty is enabled or not.
* @type {Boolean}
* @readonly
*/
this.sortable = null;
/**
* The {@link PMUI.control.TextControl TextControl} object that is playing the filter text input control role.
* @type {PMUI.control.TextControl}
* @private
*/
this.filterControl = null;
/**
* The {@link PMUI.ui.Button Button} to executes the filtering.
* @type {[type]}
*/
this.filterButton = null;
/**
* An {@link PMUI.util.ArrayList ArrayList} that contains the rows ordered by the
* {@link #property-filterCriteria filterCriteria}.
* @type {PMUI.util.ArrayList}
* @private
*/
this.usableItemsList = null;
/**
* An {@link PMUI.util.ArrayList ArrayList} that contains only the rows that meet the filter criteria.
* @type {PMUI.util.ArrayList}
* @private
*/
this.filteredItems = null;
/**
* @property {Function} statusBarHandler The function to generate a custom message in the grid's status bar.
*/
this.customStatusBar = null;
/**
* The style to be applied to all the rows in the grid.
* @type {PMUI.util.Style}
* @private
*/
this.rowStyle = null;
/**
* The placeholder to be used in the text control for filtering. It is set by the
* {@link #cfg-filterPlaceholder filterPlaceholder} config option and the
* {@link #method-setFilterPlaceholer setFilterPlaceholer()} method.
* @type {String}
*/
this.filterPlaceholder = null;
/**
* The data about the sorting that is currently applied to the grid.
* @type {Object}
*/
this.sortingData = null;
/**
* @event onRowClick
* Fired everytime a grid's row is clicked.
* @param {PMUI.grid.GridPanelRow} row The row object the click was performed on.
* @param {Object} data The row's data
*/
this.onRowClick = null;
/**
* @event onEmpty
* Fired everytime the Grid removes its last item.
* @param {PMUI.grid.GridPanel} grid The GridPanel object.
*/
this.onEmpty = null;
/**
* If the column headers are visible or not, it is set by the {@link #cfg-visibleHeaders visibleHeaders} config
* option and the {@link #method-hideHeaders hideHeaders()} and {@link #method-showHeaders showHeaders()}.
* @type {Boolean}
*/
this.visibleHeaders = null;
/**
* [selectedRow description]
* @type {[type]}
*/
this.selectedRow = null;
/**
* [selectable description]
* @type {[type]}
*/
this.selectable = null;
/**
* If the footer is visible or not.
* @type {Boolean}
*/
this.visibleFooter = null;
/**
* The message to be displayed in the grid when there are no items to display. Set by the
* {@link #cfg-emptyMessage emptyMessage config option} and the
* {@link #method-setEmptyMessage setEmptyMessage()} method.
* @type {String|Function}
*/
this.emptyMessage = null;
this._dynamicLoad = null;
this.keys = null;
this.customDataRest;
/**
* Height for the HTML element tableContainer, it can be a number or a string with the following format:
##px when ## is a number.
* @type {Number|String}
* @readonly
*/
this.tableContainerHeight = "";
GridPanel.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.core.Container", GridPanel);
/**
* The object type.
* @type {String}
*/
GridPanel.prototype.type = "GridPanel";
/**
* The object's family.
* @type {String}
*/
GridPanel.prototype.family = "GridPanel";
/**
* Initialize the object.
* @param {Object} settings A JSON object with the setting options.
* @private
*/
GridPanel.prototype.init = function (settings) {
var defaults = {
columns: [],
items: [],
dataItems: null,
pages: 0,
displayedPages: 3,
prevText: 'Prev'.translate(),
nextText: 'Next'.translate(),
invertPageOrder: false,
labelMap: [],
hrefTextPrefix: '#page-',
hrefTextSuffix: '',
nextAtFront: false,
edges: 2,
useStartEdge: true,
ellipseText: '…',
selectOnClick: true,
useEndEdge: true,
pageSize: 0,
filterable: true,
inititalPage: 0,
customStatusBar: null,
rowStyle: null,
visibleHeaders: true,
filterPlaceholder: "",
onRowClick: null,
sortableItems: '> tr:not(.pmui-nodrag)',
onEmpty: null,
emptyMessage: null,
selectable: false,
visibleFooter: true,
dynamicLoad: false,
keys: {},
tableContainerHeight: "auto"
};
jQuery.extend(true, defaults, settings);
this.totalRows = 0;
this.usableItemsList = new PMUI.util.ArrayList();
this.sortingData = {
criteria: null,
type: null
};
this.rowStyle = new PMUI.util.Style();
this.filterControl = new PMUI.control.TextControl({
onKeyUp: this.onFilterControlChangeHandler(),
style: {
cssClasses: [
"pmui-gridpanel-search"
]
}
});
this.filterButton = new PMUI.ui.Button({
text: 'Search',
style: {
cssClasses: [
"pmui-gridpanel-buttonsearch"
]
}
});
this.filteredItems = new PMUI.util.ArrayList();
this.setPageSize(defaults.pageSize)
.setCurrentPage(defaults.inititalPage);
this.setSortableItems(defaults.sortableItems)
.setSelectable(defaults.selectable)
.setEmptyMessage(defaults.emptyMessage)
.setOnEmptyHandler(defaults.onEmpty)
.setRowStyle(defaults.rowStyle)
.setColumns(defaults.columns)
.setCustomStatusBar(defaults.customStatusBar)
.setFilterPlaceholder(defaults.filterPlaceholder)
.setOnRowClick(defaults.onRowClick)
.setVisibleFooter(defaults.visibleFooter)
.setDisplayedPages(defaults.displayedPages)
.setPages(defaults.pages)
.setPrevText(defaults.prevText)
.setNextText(defaults.nextText)
.setInvertPageOrder(defaults.invertPageOrder)
.setLabelMap(defaults.labelMap)
.setHrefTextPrefix(defaults.hrefTextPrefix)
.setHrefTextSuffix(defaults.hrefTextSuffix)
.setNextAtFront(defaults.nextAtFront)
.setEdgesPagination(defaults.edges)
.setUseStartEdge(defaults.useStartEdge)
.setEllipseText(defaults.ellipseText)
.setSelectOnClick(defaults.selectOnClick)
.setUseEndEdge(defaults.useEndEdge)
.setDynamicLoad(defaults.dynamicLoad)
.setCustomDataRest(defaults.customDataRest)
.setTableContainerHeight(defaults.tableContainerHeight);
if (jQuery.isArray(defaults.dataItems)) {
this.setDataItems(defaults.dataItems);
} else {
this.setItems(defaults.items);
}
if (defaults.filterable) {
this.enableFiltering();
} else {
this.disableFiltering();
}
if (defaults.visibleHeaders) {
this.showHeaders();
} else {
this.hideHeaders();
}
this.updateUsableItemsList();
};
/**
* Set the height for the HTML element tableContainer
* @param {Number|String} height it can be a number or a string.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
* @chainable
*/
GridPanel.prototype.setTableContainerHeight = function(height){
if (typeof height === 'number') {
height = height+"px";
} else if (/^\d+(\.\d+)?px$/.test(height)) {
height = height;
} else if (/^\d+(\.\d+)?%$/.test(height)) {
height = height;
} else if (/^\d+(\.\d+)?em$/.test(height)) {
height = height;
} else if (height === 'auto' || height === 'inherit') {
height = height;
} else {
throw new Error('setHeight: height param is not a number');
}
this.tableContainerHeight = height;
return this;
};
GridPanel.prototype.setCustomDataRest = function (handlder) {
if (typeof handlder === "function") {
this.customDataRest = handlder;
}
return this;
};
GridPanel.prototype.setKeys = function (keys) {
if (!(keys.server && keys.projectID && keys.workspace && keys.accessToken && keys.endPoint)) {
throw new Error('setKeys(): The parameter, in case of being an object, must define server, projectId, authBearer, URL, pageParam, totalPages;');
}
this.keys = keys || {};
this.url = keys.server;
this.url = this.url + "/api/1.0/" + keys.workspace + "/";
this.url = this.url + keys.endPoint;
return this;
};
GridPanel.prototype.setDynamicLoad = function (dynamicLoad) {
this._dynamicLoad = !!dynamicLoad ? dynamicLoad : false;
if (this._dynamicLoad.hasOwnProperty("keys")) {
this.setKeys(this._dynamicLoad["keys"]);
}
return this;
};
/**
* @inherirdoc
*/
GridPanel.prototype.setDataItems = function (dataItems) {
if (this.usableItemsList) {
GridPanel.superclass.prototype.setDataItems.call(this, dataItems);
}
return this;
};
/**
* @inheritdoc
*/
GridPanel.prototype.setItems = function (items) {
if (this.usableItemsList) {
GridPanel.superclass.prototype.setItems.call(this, items);
}
return this;
};
/**
* Sets the message to display when there are no items to display in the grid.
* @param {String|Function|null} emptyMessage It can be:
*
* - A String, in this case the string will be displayed in both cases, when the grid has no items and when no
* grid's item meet the filter.
* - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
* will receive two parameters: the {@link PMUI.grid.Grid grid}, a boolean If it is true it means the returned
* value will be used when a filter is applied, otherwise it means that the returned value will be used when
* there are no items in the grid.
* - null, in this case a default message will be used for each situation.
* @chainable
*/
GridPanel.prototype.setEmptyMessage = function (emptyMessage) {
if (!(emptyMessage === null || typeof emptyMessage === 'string' || typeof emptyMessage === 'function')) {
throw new Error("setEmptyMessage(): the parameter must be a string, a function or null.");
}
this.emptyMessage = emptyMessage;
return this;
};
/**
* Disables the behaviors and column's buttons.
* @chainable
*/
GridPanel.prototype.disable = function () {
this.disableColumn();
return GridPanel.superclass.prototype.disable.call(this);
};
/**
* Enables the behaviors and column's buttons.
* @chainable
*/
GridPanel.prototype.enable = function () {
this.enableColumn();
return GridPanel.superclass.prototype.enable.call(this);
};
/**
* @inheritdoc
*/
GridPanel.prototype.getBehavioralItems = function () {
var items = [],
initialIndex = this.currentPage * this.pageSize,
finalIndex = initialIndex + this.pageSize,
i;
for (i = initialIndex; i < finalIndex && this.getItem(i); i += 1) {
items.push(this.getItem(i));
}
return items;
};
/**
* Enables one or all the columns.
* @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
* enabled. It supports the following data types:
*
* - String, in this case the parameter is used as the column's id.
* - Number, in this case the parameter is used as the column's index.
* - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
* a child of the grid.
* - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
* be enabled.
* @chainable
*/
GridPanel.prototype.enableColumn = function (column) {
var i,
targetColumn = this.getColumns(column);
for (i = 0; i < targetColumn.length; i += 1) {
if (targetColumn[i].dataType === 'button') {
targetColumn[i].enable();
}
}
this.disabled = false;
this.style.removeClasses(['pmui-disabled']);
return this;
};
/**
* Disables one or all the columns.
* @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
* disabled. It supports the following data types:
*
* - String, in this case the parameter is used as the column's id.
* - Number, in this case the parameter is used as the column's index.
* - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
* a child of the grid.
* - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
* be disabled.
* @chainable
*/
GridPanel.prototype.disableColumn = function (column) {
var i,
targetColumn = this.getColumns(column);
for (i = 0; i < targetColumn.length; i += 1) {
if (targetColumn[i].dataType === 'button') {
targetColumn[i].disable();
}
}
return this;
};
/**
* Sets the width to one or all the columns.
* @param {Number|String} width The width to be applied to the column(s).
* @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column the width will be
* applied. It supports the following data types:
*
* - String, in this case the parameter is used as the column's id.
* - Number, in this case the parameter is used as the column's index.
* - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
* a child of the grid.
* - [no value], since this parameter is optional, you can skip it. In this case the width will be applied to all
* the columns in the grid.
* @chainable
*/
GridPanel.prototype.setColumnWidth = function (width, column) {
var targetColumn = [],
aux,
i;
if (typeof column === 'string') {
aux = this.columns.find("id", column);
} else if (typeof column === 'number') {
aux = this.columns.get(column);
} else if (column instanceof PMUI.grid.GridPanelColumn && this.isDirectParentOf(column)) {
targetColumn.push(column);
} else if (column === undefined) {
targetColumn = this.column.asArray();
}
if (aux) {
targetColumn.push(aux);
}
for (i = 0; i < targetColumn.length; i += 1) {
targetColumn[i].setWidth(width);
}
if (i > 0) {
this.goToPage(this.currentPage);
}
return this;
};
/**
* Sets the function callback to be called everytime the grid's last item is removed.
* @param {Function|null} handler The callback function or the null constant for no callback execution. For more
* info about the paramerters sent to the function please read the {@link #event-onEmpty onEmpty} event
* documentation.
* @chainable
*/
GridPanel.prototype.setOnEmptyHandler = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error('setOnEmptyHandler(): The parameter must be a function or null.');
}
this.onEmpty = handler;
return this;
};
/**
* Shows the column headers.
* @chainable
*/
GridPanel.prototype.showHeaders = function () {
this.visibleHeaders = true;
if (this.html) {
this.dom.thead.style.display = '';
}
return this;
};
/**
* Hide the column headers.
* @chainable
*/
GridPanel.prototype.hideHeaders = function () {
this.visibleHeaders = false;
if (this.html) {
this.dom.thead.style.display = 'none';
}
return this;
};
/**
* Updates the usable items list.
* @chainable
* @private
*/
GridPanel.prototype.updateUsableItemsList = function () {
if (this.usableItemsList) {
this.usableItemsList.set(this.items.asArray());
}
return this;
};
/**
* Sets the placeholder for the filter textbox.
* @param {String} filterPlaceholder
* @chainable
*/
GridPanel.prototype.setFilterPlaceholder = function (filterPlaceholder) {
if (typeof filterPlaceholder !== 'string') {
throw new Error('setFilterPlaceholder(): The parameter must be a string.');
}
this.filterControl.setPlaceholder(this.filterPlaceholder = filterPlaceholder);
return this;
};
/**
* Sets the custom style to be applied to the grid's rows.
* @param {Object|PMUI.util.Style|null} rowStyle It can be an object literal (with the properties
* cssProperties and/or cssClasses), an instance of PMUI.util.Style or null (in this case no custom style will be
* applied to the rows).
*/
GridPanel.prototype.setRowStyle = function (rowStyle) {
var i,
rows,
rowsLength;
if (rowStyle !== null && typeof rowStyle !== 'object' && !(rowStyle instanceof PMUI.util.Style)) {
throw new Error("setRowStyle(): The parameter must be null or an object literal or an instance of "
+ "PMUI.grid.GridPanelRow.");
}
this.rowStyle.clear();
if (rowStyle instanceof PMUI.util.Style) {
this.rowStyle = rowStyle;
} else if (rowStyle) {
this.rowStyle.addProperties(rowStyle.cssProperties || {})
.addClasses(rowStyle.cssClasses || []);
}
rows = this.items.asArray();
rowsLength = rows.length;
for (i = 0; i < rowsLength; i += 1) {
rows[i].setStyle({
cssProperties: this.rowStyle.cssProperties,
cssClasses: this.rowStyle.cssClasses
});
}
return this;
};
/**
* Sets a function to generate a custom message in the grid's status bar.
* @param {Function|NULL} handler The function for generate the message to be shown in the grid's status bar. It
* also can be NULL, in this case the message shown in the grid's status bar wiil be the default one.
* For more info about the sent parameters to the function read the {@link #cfg-customStatusBar customStatusBar}
* config option.
*/
GridPanel.prototype.setCustomStatusBar = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error('setCustomStatusBar(): this method only accepts as parameter a function or null.');
}
this.customStatusBar = handler;
return this;
};
/**
* Enables the filter functionality.
* @chainable
*/
GridPanel.prototype.enableFiltering = function () {
this.filterable = true;
if (this.dom.toolbar) {
this.dom.toolbar.style.display = '';
if (!this._dynamicLoad) {
this.goToPage(this.currentPage);
}
}
return this;
};
/**
* Disables the filter functionality.
* @chainable
*/
GridPanel.prototype.disableFiltering = function () {
this.filterable = false;
if (this.dom.toolbar) {
this.dom.toolbar.style.display = 'none';
this.filterCriteria = "";
this.filterControl.setValue("");
if (!this._dynamicLoad) {
this.goToPage(this.currentPage);
}
}
return this;
};
/**
* Returns the index in page for the specified row.
* @param {PMUI.grid.GridPanelRow|Number|String} row It can be:
*
* - {@link PMUI.grid.GridPanelRow}.
* - Number, in this case it is interpreted as the global index for the row.
* - String, it will be interpreted as the row's id.
* @return {Number}
*/
GridPanel.prototype.indexInPage = function (row) {
var globalIndex = this.getItemIndex(row);
if (this.pageSize !== 0 && globalIndex >= 0) {
globalIndex = globalIndex % this.pageSize;
}
return globalIndex;
};
/**
* Returns the handler to be executed when the filter changes.
* @return {Function}
* @private
*/
GridPanel.prototype.onFilterControlChangeHandler = function () {
var that = this;
return function () {
var nextFilter = this.getHTML().value;
if (that.filterCriteria !== nextFilter) {
that.filter(nextFilter);
}
};
};
/**
* Sets the columns.
* @param {Array} columns An Array where each element is an object literal with the
* {@link PMUI.grid.GridPanelColumn the config options for GridPanelColumn creation} or a
* {@link PMUI.grid.GridPanelColumn GridPanelColumn object}.
* @chainable
*/
GridPanel.prototype.setColumns = function (columns) {
var i;
this.clearColumns();
if (!jQuery.isArray(columns)) {
throw new Error("setColumns(): The parameter must be an Array");
}
for (i = 0; i < columns.length; i += 1) {
this.addColumn(columns[i]);
}
return this;
};
/**
* Returns one or all the grid's columns.
* @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
* returned. It supports the following data types:
*
* - String, in this case the parameter is used as the column's id.
* - Number, in this case the parameter is used as the column's index.
* - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
* a child of the grid.
* - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
* be returned.
* @return {Array}
*/
GridPanel.prototype.getColumns = function (column) {
var targetColumn = [],
aux;
if (typeof column === 'string') {
aux = this.columns.find("id", column);
} else if (typeof column === 'number') {
aux = this.columns.get(column);
} else if (column instanceof PMUI.grid.GridPanelColumn && this.isDirectParentOf(column)) {
targetColumn.push(column);
} else if (column === undefined) {
targetColumn = this.columns.asArray().slice(0);
}
if (aux) {
targetColumn.push(aux);
}
return targetColumn;
};
/**
* Paints a column into the grid's head.
* @param {PMUI.grid.GridPanelColumn} column
* @chainable
* @private
*/
GridPanel.prototype.paintColumn = function (column) {
if (this.dom.thead) {
this.dom.thead.appendChild(column.getHTML());
}
return this;
};
GridPanel.prototype.paintColumns = function () {
var i,
columns = this.columns.asArray();
for (i = 0; i < columns.length; i += 1) {
this.paintColumn(columns[i]);
}
return this;
};
/**
* Adds a column.
* @param {PMUI.grid.GridPanelColumn|JSON} column A {@link PMUI.grid.GridPanelColumn} or a JSON Object with the
* {@link PMUI.grid.GridPanelColumn#cfg config options} for the new column.
*/
GridPanel.prototype.addColumn = function (column) {
var that = this,
columnToAdd,
rows,
i,
defaults = {
grid: this,
title: '[untitled]',
type: 'string',
sortable: false,
searchable: true,
onSort: function (order) {
var criteria = this.columnData;
that.sort(criteria, order);
}
};
if (column instanceof PMUI.grid.GridPanelColumn) {
columnToAdd = column;
columnToAdd.clearCells();
columnToAdd.setGrid(this);
} else if (typeof column === 'object') {
jQuery.extend(true, defaults, column);
columnToAdd = new PMUI.grid.GridPanelColumn(defaults);
} else {
throw new Error('addColumn(): The method only accepts an object or an instace of PMUI.grid.GridPanelColumn'
+ ' as parameter.');
}
//TODO control align and width
//console.log("\tCOLUMN insert " + columnToAdd.title/*isd*/);
this.columns.insert(columnToAdd);
this.paintColumn(columnToAdd);
rows = this.items.asArray();
for (i = 0; i < rows.length; i += 1) {
rows[i].addCell(columnToAdd);
}
return this;
};
/**
* Clears all the columns for the grid.
* @chainable
*/
GridPanel.prototype.clearColumns = function () {
while (this.columns.getSize()) {
this.removeColumn(0);
}
return this;
};
/**
* Removes a column and all its cells from the grid.
* @param {Number|String|PMUI.grid.GridPanelColumn} column This value is used to determine the row to remove:
*
* - String, in this case the string must be the id of a column in the grid.
* - Number, in this case the number is the index of the column to remove.
* - a {@link PMUI.grid.GridPanelColumn GridPanelColumn object}, if it's a column that belongs to the grid then
* this column will be the one to remove.
* @chainable
*/
GridPanel.prototype.removeColumn = function (column) {
var columnToRemove,
i,
row,
size,
index;
if (column instanceof PMUI.grid.GridPanelColumn) {
columnToRemove = column;
} else if (typeof column === 'number') {
columnToRemove = this.columns.get(column);
} else if (typeof column === 'string') {
columnToRemove = this.columns.find('id', column);
}
if (columnToRemove) {
index = this.columns.indexOf(columnToRemove);
size = this.items.getSize();
for (i = 0; i < size; i += 1) {
row = this.items.get(i);
row.removeCell(index);
}
columnToRemove.grid = null;
columnToRemove.clearCells();
this.columns.remove(columnToRemove);
jQuery(columnToRemove.getHTML()).remove();
}
return this;
};
/**
* Adds a new Item
* @param {Object|PMUI.grid.GridPanelRow} item An instance of {@link PMUi.grid.GridPanelRow GridPanelRow} or an
* object with the properties to be used to create a new one.
* @param {Number} [index] The insertion index position.
*/
GridPanel.prototype.addItem = function (item, index) {
var itemToBeAdded;
if (typeof item !== 'object') {
throw new Error('addItem(): The first parameter must be an object or an instance of PMUI.grid.GridPanel.');
}
item.onSelect = this.onRowSelectHandler();
if (this.factory) {
itemToBeAdded = this.factory.make(item);
}
if (itemToBeAdded && !this.isDirectParentOf(itemToBeAdded)) {
itemToBeAdded.parent = this;
itemToBeAdded.style.addClasses(['pmui-' + (this.items.getSize() % 2 ? 'odd' : 'even')]);
if (itemToBeAdded.html || this.columns.asArray()) {
itemToBeAdded.setCells();
}
if (typeof index === 'number') {
this.items.insertAt(itemToBeAdded, index);
if (this.usableItemsList) {
this.usableItemsList.insertAt(itemToBeAdded, index);
}
if (this.filterCriteria) {
this.filter(this.filterCriteria);
} else {
this.goToPage(this.currentPage);
}
} else {
this.items.insert(itemToBeAdded);
this.usableItemsList.insert(itemToBeAdded);
if (this.dom.tbody && !this.massiveAction) {
this.goToPage(this.currentPage);
}
}
if (this.eventsDefined) {
itemToBeAdded.defineEvents();
}
}
return this;
};
/**
* [onRowSelectHandler description]
* @return {[type]} [description]
*/
GridPanel.prototype.onRowSelectHandler = function () {
var that = this;
return function () {
if (that.selectedRow) {
that.selectedRow.deselectRow();
}
that.selectedRow = this;
};
};
/**
* Sets the callback function to be executed everytime the {@link #event-onRowClick onRowClick} event is fired.
* Read about this event to know about the parameters received by the callback function.
* @param {Function|null} handler
* @chainable
*/
GridPanel.prototype.setOnRowClick = function (handler) {
if (typeof handler !== 'function' && handler !== null) {
throw new Error("sdfsdfgsd");
}
this.onRowClick = handler;
return this;
};
/**
* Shows an empty row in grid.
* @param sizeItems
* @chainable
* @private
*/
GridPanel.prototype.showEmptyCell = function (sizeItems) {
var tr,
td,
message;
if (this.dom.tbody) {
tr = PMUI.createHTMLElement('tr');
td = PMUI.createHTMLElement('td');
tr.className = 'pmui-gridpanel-emptyrow pmui-nodrag';
td.colSpan = this.columns.getSize();
tr.appendChild(td);
$(this.dom.tbody).find('.pmui-gridpanel-emptyrow').remove();
if (!sizeItems) {
// the empty row will be added only if there is not items in the container
$(this.dom.tbody).append(tr);
if (typeof this.emptyMessage === 'function') {
message = this.emptyMessage(this, !!this.filterCriteria);
} else if (typeof this.emptyMessage === 'string') {
message = this.emptyMessage;
} else {
if (this.filterCriteria) {
message = 'No matches found for \"' + this.filterCriteria + '\"';
} else {
message = 'No records found.';
}
}
if (typeof message === 'string') {
td.appendChild(document.createTextNode(message));
} else if (PMUI.isHTMLElement(message)) {
td.appendChild(message);
}
if (this.items.getSize() === 0 && typeof this.onEmpty === 'function') {
this.onEmpty(this);
}
}
}
return this;
};
/**
* Removes a row from the object.
* @param {PMUI.core.Element|String|Number} item It can be a string (id of the child to remove),
a number (index of the child to remove) or a {Element} object
* @chainable
*/
GridPanel.prototype.removeItem = function (item) {
var currentPage = this.currentPage;
if (item.selected) {
item.deselectRow();
}
GridPanel.superclass.prototype.removeItem.call(this, item);
if (!this.massiveAction) {
this.updateUsableItemsList();
this.filteredItems.remove(item);
this.showEmptyCell(this.items.getSize());
if (currentPage >= this.getTotalPages()) {
currentPage -= 1;
this.currentPage = currentPage < 0 ? 0 : currentPage;
}
if (this.dom.tbody) {
this.goToPage(this.currentPage, true);
}
}
return this;
};
/**
* @inheritdoc
*/
GridPanel.prototype.clearItems = function () {
var currentPage = this.currentPage;
GridPanel.superclass.prototype.clearItems.call(this);
this.updateUsableItemsList();
this.showEmptyCell(this.items.getSize());
if (currentPage >= this.getTotalPages()) {
currentPage -= 1;
this.currentPage = currentPage < 0 ? 0 : currentPage;
}
if (this.dom.tbody) {
this.goToPage(this.currentPage);
}
return this;
};
/**
* Remove all columns´s cells
* @returns {PMUI.grid.GridPanel}
*/
GridPanel.prototype.clearItemsColumns = function () {
var i,
max = this.getColumns().length;
for (i = 0; i < max; i += 1) {
this.getColumns()[i].clearCells();
}
return this;
};
/**
* Moves a row from its current position to another.
* @param {PMUI.grid.GridPanelRow|Number|String} item It can be:
*
* - An instance of PMUI.grid.GridPanelRow, in this case this object must be a item of the object.
* - A Number, in this case the number is interpereted as the index of the item to move.
* - A String, in this case the string is interpreted as the id of the item to move.
* @param {Number} index The index in which the item will be moved to.
* @chainable
*/
GridPanel.prototype.moveItem = function (item, index) {
var items = this.items,
currentIndex,
referenceObject;
item = this.getItem(item);
if (item instanceof PMUI.core.Element && this.isDirectParentOf(item)) {
currentIndex = items.indexOf(item);
items = items.asArray();
referenceObject = this.items.get(index + (currentIndex < index ? 1 : 0));
item = items.splice(currentIndex, 1)[0];
if (this.items.indexOf(item) > -1) {
this.items.remove(item);
}
this.items.insertAt(item, index);
this.updateUsableItemsList();
if (this.html) {
if (!referenceObject) {
this.goToPage(this.currentPage);
} else if (index === (this.currentPage * this.pageSize + this.pageSize - 1)
|| (this.getTotalPages() === (this.currentPage + 1)
&& index === (this.items.getSize() % this.pageSize) - 1)) {
this.containmentArea.appendChild(item.getHTML());
} else {
this.containmentArea.insertBefore(item.getHTML(), referenceObject.getHTML());
}
}
}
return this;
};
/**
* Returns the data for every rowe in the grid.
* @return {Array}
*/
GridPanel.prototype.getData = function () {
var i,
items = this.items.asArray(),
data = [];
for (i = 0; i < items.length; i += 1) {
data.push(items[i].getData());
}
return data;
};
/**
* @inheritdoc
*/
GridPanel.prototype.paintItem = function (item, index) {
var intitalItem = this.currentPage * this.pageSize,
finalIndex = this.pageSize ? this.currentPage + this.pageSize - 1 : null;
if (index === undefined) {
index = this.items.getSize();
}
if (index >= initialIndex || (index <= finalIndex || finalIndex === null)) {
this.goToPage(this.currentPage);
}
return this;
};
/**
* @inheritdoc
*/
GridPanel.prototype.paintItems = function () {
return this.filterCriteria ? this.filter(this.filterCriteria) : this.goToPage(this.currentPage);
};
/**
* Creates the HTML for the Grid.
* @return {HTMLElement}
*/
GridPanel.prototype.createHTML = function () {
var table,
thead,
tbody,
toolbarDiv,
footer,
pager,
statusBar,
tableContainer,
pager2;
if (this.html) {
return this.html;
}
GridPanel.superclass.superclass.prototype.createHTML.call(this);
table = PMUI.createHTMLElement("table");
table.className = 'pmui-gridpanel-table';
table.cellSpacing = 0;
table.cellPadding = 0;
thead = PMUI.createHTMLElement("thead");
tbody = PMUI.createHTMLElement("tbody");
tableContainer = PMUI.createHTMLElement("div");
tableContainer.className = "pmui-gridpanel-tableContainer";
tbody.className = 'pmui-gridpanel-tbody';
this.containmentArea = tbody;
tableContainer.style.overflow = 'auto';
toolbarDiv = PMUI.createHTMLElement("div");
toolbarDiv.className = 'pmui-gridpanel-toolbar';
toolbarDiv.appendChild(this.filterControl.getHTML());
if (this._dynamicLoad) {
var searchLoad = PMUI.createHTMLElement("span");
searchLoad.className = "pmui-gridpanel-searchload";
toolbarDiv.appendChild(searchLoad);
}
this.html.appendChild(toolbarDiv);
this.dom.tableContainer = tableContainer;
this.dom.thead = thead;
this.dom.tbody = tbody;
this.dom.table = table;
this.dom.toolbar = toolbarDiv;
table.appendChild(thead);
table.appendChild(tbody);
tableContainer.appendChild(table);
this.html.appendChild(tableContainer);
pager = PMUI.createHTMLElement('ul');
pager.className = 'pmui-gridpanel-pager';
statusBar = PMUI.createHTMLElement('div');
statusBar.className = 'pmui-gridpanel-statusbar';
footer = PMUI.createHTMLElement("div");
footer.className = 'pmui-gridpanel-footer';
pager2 = PMUI.createHTMLElement("ul");
pager2.className = 'pmui-gridpanel-pager';
if (this._dynamicLoad) {
footer.appendChild(pager);
} else {
footer.appendChild(pager2);
}
this.html.appendChild(footer);
this.dom.pager2 = pager2;
this.dom.pager = pager;
this.dom.statusBar = statusBar;
this.dom.footer = footer;
this.setVisibleFooter(this.visibleFooter);
this.paintColumns()
.setHeight(this.height);
tableContainer.style.height = this.tableContainerHeight;
this.style.applyStyle();
if (this.filterable) {
this.enableFiltering();
} else {
this.disableFiltering();
}
if (this.visibleHeaders) {
this.showHeaders();
} else {
this.hideHeaders();
}
this.showEmptyCell(this.items.getSize());
if (this.eventsDefined) {
this.defineEvents();
}
if (this.sortingData.sortingCriteria) {
this.sort(this.sortingData.criteria, this.sortingData.type);
}
return this.html;
};
/**
* [updateStatusBar description]
* @return {[type]} [description]
*/
GridPanel.prototype.updateStatusBar = function () {
var itemsLength,
filterCriteria,
b,
currentPage,
filtered,
totalPages;
filterCriteria = this.filterCriteria;
currentPage = this.currentPage;
filtered = !!(filterCriteria && this.filterable);
itemsLength = filtered ? this.filteredItems.getSize() : this.items.getSize();
jQuery(this.dom.statusBar).empty();
totalPages = this.getTotalPages(filtered);
if (typeof this.customStatusBar === 'function') {
this.dom.statusBar.textContent =
this.customStatusBar(currentPage, this.pageSize, itemsLength, filtered, filterCriteria || "");
} else if (totalPages === 0) {
b = PMUI.createHTMLElement('b');
b.appendChild(document.createTextNode(""));
this.dom.statusBar.appendChild(b);
} else {
b = PMUI.createHTMLElement('b');
b.appendChild(document.createTextNode("PAGE " + (currentPage + 1)));
this.dom.statusBar.appendChild(b);
this.dom.statusBar.appendChild(document.createTextNode(" of " + totalPages
+ (filtered ? " filtered pages" : "")));
}
return this;
};
/**
* [param] this-GridPanel
* @return Object
*/
GridPanel.prototype._getInterval = function (that) {
return {
start: Math.ceil(that.currentPage > that.halfDisplayed ? Math.max(Math.min(that.currentPage - that.halfDisplayed, (that.pages - that.displayedPages)), 0) : 0),
end: Math.ceil(that.currentPage > that.halfDisplayed ? Math.min(that.currentPage + that.halfDisplayed, that.pages) : Math.min(that.displayedPages, that.pages))
};
};
/**
* [number] pageIndex
* [object] opts
*/
GridPanel.prototype._appendItem = function (pageIndex, opts) {
var self = this.dom.footer,
options,
$link,
$linkWrapper = $(''),
$ul = $(this.dom.footer).find('ul');
pageIndex = pageIndex < 0 ? 0 : (pageIndex < this.pages ? pageIndex : this.pages - 1);
options = {
text: pageIndex + 1,
classes: ''
};
if (this.labelMap.length && this.labelMap[pageIndex]) {
options.text = this.labelMap[pageIndex];
}
options = $.extend(options, opts || {});
if (pageIndex == this.currentPage || this.disabled) {
if (this.disabled) {
$linkWrapper.addClass('disabled');
} else {
$linkWrapper.addClass('active');
}
$link = (options.text == 'Prev') ? $('') : (options.text == 'Next') ? $('') : $('');
} else {
$link = (options.text == 'Prev') ? $(' ‹ ' + (options.text) + '') : (options.text == 'Next') ? $('' + (options.text) + ' › ') : $('' + (options.text) + '');
var that = this;
$link.click(function (event) {
return that._selectPage.call(that, pageIndex, event);
});
}
if (options.classes) {
$link.addClass(options.classes);
}
$linkWrapper.append($link);
if ($ul.length) {
$(this.dom.pager2).append($linkWrapper);
} else {
$(this.dom.pager2).append($linkWrapper); //... review
}
};
/**
* Select number in a page.
* @param {Number} pageIndex The index of the page to go.
* @param {event} click event.
* @chainable
*/
GridPanel.prototype._selectPage = function (pageIndex, event) {
this.currentPage = pageIndex;
if (this.selectOnClick) {
this.goToPage(pageIndex, true);
}
}
/**
* Change the grid page.
* @param {Number} index The index of the page to go.
* @param {Boolean} [force] If its true you go to the specified page even if there's no data in it.
* @chainable
*/
GridPanel.prototype.goToPage = function (index, force) {
var modal;
if (this._dynamicLoad) {
var i, numDisplayedItems = 0, that = this;
if (!((index < 0) && !force)) {
this.currentPage = index;
if (!this.html) {
return this;
}
}
if (!this.html) {
return this;
}
this.showEmptyCell(numDisplayedItems);
if (this.html) {
$(this.dom.toolbar).find(".pmui-gridpanel-searchload").addClass("load");
modal = document.createElement("div");
modal.id = "dynamic-modal-load";
$(modal).css({
height: $(this.dom.tableContainer).outerHeight(),
width: $(this.dom.tableContainer).outerWidth(),
background: "rgba(204, 197, 197, 0.4)",
position: "absolute",
zIndex: "200"
});
$(this.dom.tableContainer).prepend(modal);
}
$.ajax({
url: this.url + "?filter=" + (this.filterCriteria ? this.filterCriteria : "") + "&start=" + (this.pageSize * index) + "&limit=" + this.pageSize + "&type=" + (this.typeList ? this.typeList : ""),
type: "GET",
async: true,
contentType: "application/json",
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + that.keys.accessToken);
},
success: function (data) {
var i,
newItem,
numDisplayedItems = 0,
dataReceived;
if (typeof that.customDataRest === "function") {
dataReceived = that.customDataRest(data["data"]);
} else {
dataReceived = data["data"];
}
that._dynamicLoad.totalPages = Math.ceil(data["total"] / that.pageSize);
$(that.dom.tbody).empty();
if (dataReceived.length) {
for (i = 0; i < dataReceived.length; i += 1) {
newItem = that.factory.make({
data: dataReceived[i]
});
newItem.parent = that;
newItem.style.addClasses(['pmui-' + (i % 2 ? 'odd' : 'even')]);
if (newItem.html || that.columns.asArray()) {
newItem.setCells();
}
that.dom.tbody.appendChild(newItem.updateCellsWidth().getHTML());
$(newItem.html).find(".pmui-gridpanelcell-content").each(function () {
if (this.children.length === 0 && this.textContent.length != 0) {
this.title = this.textContent;
}
});
numDisplayedItems += 1;
}
}
that.showEmptyCell(numDisplayedItems);
that.setBehavior(that.behavior);
if (that.html) {
$(that.dom.toolbar).find(".pmui-gridpanel-searchload").removeClass("load");
$(that.dom.tableContainer).find("#dynamic-modal-load").remove();
}
return that.updatePager().updateStatusBar();
that.dom.footer.appendChild(this.dom.pager);
},
error: function (xhr, textStatus, errorThrown) {
}
});
this.setBehavior(this.behavior);
} else {
this.halfDisplayed = this.displayedPages / 2;
this.currentPage = index;
if (force && this.filterCriteria && this.filterable) {
this.pages = Math.ceil(this.filteredItems.getSize() / this.pageSize) ? Math.ceil(this.filteredItems.getSize() / this.pageSize) : 1;
} else {
this.pages = Math.ceil(this.getItems().length / this.pageSize) ? Math.ceil(this.getItems().length / this.pageSize) : 1;
}
var interval = this._getInterval(this);
//Updates the pager
if (this.dom.pager2 && this.getItems().length != 0) {
jQuery(this.dom.pager2).empty();
//Generate Prev link
if (this.prevText) {
this._appendItem.call(this, !this.invertPageOrder ? this.currentPage - 1 : this.currentPage + 1, {
text: this.prevText,
classes: 'prev'
});
}
//Generate Next link
if (this.nextText && this.nextAtFront) {
this._appendItem.call(this, !this.invertPageOrder ? this.currentPage + 1 : this.currentPage - 1, {
text: this.nextText,
classes: 'next'
});
}
//Generate start edges
if (!this.invertPageOrder) {
if (interval.start > 0 && this.edges > 0) {
if (this.useStartEdge) {
var end = Math.min(this.edges, interval.start);
for (i = 0; i < end; i += 1) {
this._appendItem.call(this, i);
}
}
if (this.edges < interval.start && (interval.start - this.edges != 1)) {
$(this.dom.pager2).append('');
} else if (interval.start - this.edges == 1) {
this._appendItem.call(this, this.edges);
}
}
} else {
if (interval.end < this.pages && this.edges > 0) {
if (this.useStartEdge) {
var begin = Math.max(this.pages - this.edges, interval.end);
for (i = this.pages - 1; i >= begin; i -= 1) {
this._appendItem.call(this, i);
}
}
if (this.pages - this.edges > interval.end && (this.pages - this.edges - interval.end != 1)) {
$(this.dom.pager2).append('');
} else if (this.pages - this.edges - interval.end == 1) {
this._appendItem.call(this, interval.end);
}
}
}
//Generate interval links
if (!this.invertPageOrder) {
for (i = interval.start; i < interval.end; i += 1) {
this._appendItem.call(this, i);
}
} else {
for (i = interval.end - 1; i >= interval.start; i -= 1) {
this._appendItem.call(this, i);
}
}
//Generate end edges
if (!this.invertPageOrder) {
if (interval.end < this.pages && this.edges > 0) {
if (this.pages - this.edges > interval.end && (this.pages - this.edges - interval.end != 1)) {
$(this.dom.pager2).append('');
} else if (this.pages - this.edges - interval.end == 1) {
this._appendItem.call(this, interval.end);
}
if (this.useEndEdge) {
var begin = Math.max(this.pages - this.edges, interval.end);
for (i = begin; i < this.pages; i += 1) {
this._appendItem.call(this, i);
}
}
}
} else {
if (interval.start > 0 && this.edges > 0) {
if (this.edges < interval.start && (interval.start - this.edges != 1)) {
$(this.dom.pager2).append('');
} else if (interval.start - this.edges == 1) {
this._appendItem.call(this, this.edges);
}
if (this.useEndEdge) {
var end = Math.min(this.edges, interval.start);
for (i = end - 1; i >= 0; i -= 1) {
this._appendItem.call(this, i);
}
}
}
}
//Generate Next link
if (this.nextText && !this.nextAtFront) {
this._appendItem.call(this, !this.invertPageOrder ? this.currentPage + 1 : this.currentPage - 1, {
text: this.nextText,
classes: 'next'
});
}
} else {
if (this.getItems().length == 0) {
jQuery(this.dom.pager2).empty();
}
}
//Show rows in gridPanel
var i, initRow, lastRow, listTarget, size, numDisplayedItems = 0, totalPages;
if (!((index < 0 || index >= this.getTotalPages(this.filterCriteria)) && !force)) {
if (!this.html) {
return this;
}
if (this.filterCriteria && this.filterable) {
listTarget = this.filteredItems.asArray();
} else {
listTarget = this.usableItemsList.asArray();
}
size = listTarget.length;
totalPages = this.getTotalPages(!!this.filterCriteria);
jQuery(this.dom.tbody).find('>tr').detach();
if (force) {
initRow = this.currentPage * this.pageSize;
lastRow = this.pageSize !== 0 ? (initRow + this.pageSize) : size;
} else {
initRow = (this.filterCriteria && this.filterable) ? 0 : this.pageSize !== 0 ? ((this.currentPage + 1) * this.pageSize) - this.pageSize : 0;
lastRow = this.pageSize !== 0 ? (initRow + this.pageSize) : size;
}
for (i = initRow; i < lastRow && i < size; i += 1) {
this.dom.tbody.appendChild(listTarget[i].updateCellsWidth().getHTML());
numDisplayedItems += 1;
}
}
if (!this.html) {
return this;
}
this.showEmptyCell(numDisplayedItems);
this.setBehavior(this.behavior);
return this;
}
};
/**
* Deprecated...
* Updates the pager.
* @chainable
* @private
*/
GridPanel.prototype.updatePager = function () {
var totalPages,
page,
i,
inputPage,
link,
textContent,
additionalClass,
currentPage = this.currentPage,
dataIndex,
infoLink;
if (this.dom.pager) {
jQuery(this.dom.pager).empty();
if (this._dynamicLoad) {
totalPages = this._dynamicLoad.totalPages;
var first, next, first, last, total;
first = PMUI.createHTMLElement('li');
first.className = "list-item-ajax";
link = PMUI.createHTMLElement('a');
link.className = 'pmui-gridpanel-pagelink pmui-gridpanel-previousbutton';
textContent = "«";
link.textContent = textContent;
link.href = '#';
first.appendChild(link);
jQuery(link).css({
height: "auto"
}).data({"index": 0});
this.dom.pager.appendChild(first);
if (this.currentPage !== 0) {
jQuery(link).removeClass("disable");
} else {
jQuery(link).addClass("disable");
}
prev = PMUI.createHTMLElement('li');
prev.className = "list-item-ajax";
link = PMUI.createHTMLElement('a');
link.className = 'pmui-gridpanel-pagelink';
link.innerHTML = "<";
jQuery(link).addClass("pmui-gridpanel-previousbutton");
link.href = '#';
prev.appendChild(link);
jQuery(link).css({
height: "auto"
}).data({"index": this.currentPage - 1});
this.dom.pager.appendChild(prev);
if (this.currentPage !== 0) {
jQuery(link).removeClass("disable");
} else {
jQuery(link).addClass("disable");
}
infoLink = PMUI.createHTMLElement('li');
infoLink.className = "list-item-ajax";
total = PMUI.createHTMLElement("a");
total.className = "pmui-gridpanel-total";
total.textContent = " Page ";
infoLink.appendChild(total);
this.dom.pager.appendChild(infoLink);
infoLink = PMUI.createHTMLElement('li');
infoLink.className = "list-item-ajax";
inputPage = PMUI.createHTMLElement("input");
jQuery(inputPage).addClass("pmui-gridpanel-gotoPage");
infoLink.appendChild(inputPage);
this.dom.pager.appendChild(infoLink);
jQuery(inputPage).val(this.currentPage + 1);
jQuery(infoLink).css({
"vertical-align": "top"
});
infoLink = PMUI.createHTMLElement('li');
infoLink.className = "list-item-ajax";
total = PMUI.createHTMLElement("a");
total.className = "pmui-gridpanel-total";
total.textContent = " of " + (totalPages ? totalPages : 1);
infoLink.appendChild(total);
this.dom.pager.appendChild(infoLink);
next = PMUI.createHTMLElement('li');
next.className = "list-item-ajax";
link = PMUI.createHTMLElement('a');
link.className = 'pmui-gridpanel-pagelink';
link.innerHTML = ">";
jQuery(link).addClass("pmui-gridpanel-nextbutton");
link.href = '#';
next.appendChild(link);
jQuery(link).css({
height: "auto"
}).data({"index": this.currentPage + 1});
this.dom.pager.appendChild(next);
if (currentPage + 1 < totalPages) {
jQuery(link).removeClass("disable");
} else {
jQuery(link).addClass("disable");
}
last = PMUI.createHTMLElement('li');
last.className = "list-item-ajax";
link = PMUI.createHTMLElement('a');
link.className = 'pmui-gridpanel-pagelink pmui-gridpanel-nextbutton';
textContent = "»";
link.textContent = textContent;
link.href = '#';
last.appendChild(link);
jQuery(link).css({
height: "auto"
}).data({"index": this._dynamicLoad.totalPages - 1});
this.dom.pager.appendChild(last);
if (currentPage + 1 < totalPages) {
jQuery(link).removeClass("disable");
} else {
jQuery(link).addClass("disable");
}
} else {
totalPages = this.getTotalPages(!!this.filterCriteria);
if (!totalPages) {
return this;
}
for (i = -1; i <= totalPages; i += 1) {
additionalClass = textContent = dataIndex = "";
if (i < 0) {
if (currentPage > 0) {
dataIndex = 'p';
additionalClass = 'pmui-gridpanel-previousbutton';
}
} else if (i === totalPages) {
if (currentPage < totalPages - 1) {
additionalClass = 'pmui-gridpanel-nextbutton';
dataIndex = 'n';
}
} else {
textContent = i + 1;
if (currentPage === i) {
additionalClass = 'pmui-active';
}
dataIndex = i;
}
if (dataIndex !== "") {
page = PMUI.createHTMLElement('li');
link = PMUI.createHTMLElement('a');
if (this._dynamicLoad) {
$(link).css({
height: 21
});
}
link.className = 'pmui-gridpanel-pagelink';
link.setAttribute("data-index", dataIndex);
if (textContent) {
link.textContent = textContent;
} else {
textContent = PMUI.createHTMLElement('div');
textContent.className = 'pmui-icon';
link.appendChild(textContent);
}
jQuery(link).addClass(additionalClass);
link.href = '#';
page.appendChild(link);
this.dom.pager.appendChild(page);
}
}
}
return this;
}
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setPageSize = function (pageSize) {
var previousSize = this.pageSize;
if (typeof pageSize !== 'number') {
throw new Error("setPageSize(): The method only accepts a number as parameter.");
}
if (pageSize < 0) {
throw new Error("setPageSize(): The method only accepts a number major or equal to zero (0).");
}
this.pageSize = pageSize;
if (this.html && previousSize !== pageSize) {
if (pageSize === 0) {
jQuery(this.dom.pager).hide();
this.currentPage = 0;
} else {
jQuery(this.dom.pager).show();
}
this.goToPage(this.currentPage);
}
return this;
};
/**
* Sets the size for the grid's displayedPages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setDisplayedPages = function (displayedPages) {
this.displayedPages = displayedPages;
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setPages = function (pages) {
this.pages = pages;
return this;
};
/**
* Sets Prev text in paginator.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setPrevText = function (prevText) {
this.prevText = prevText;
return this;
};
/**
* Sets Next text in paginator.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setNextText = function (nextText) {
this.nextText = nextText
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setInvertPageOrder = function (invertPageOrder) {
this.invertPageOrder = invertPageOrder
return this;
};
/**
* Sets numbers in a paginator.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setLabelMap = function (labelMap) {
this.labelMap = labelMap
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setHrefTextPrefix = function (hrefTextPrefix) {
this.hrefTextPrefix = hrefTextPrefix
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setHrefTextSuffix = function (hrefTextSuffix) {
this.hrefTextSuffix = hrefTextSuffix
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setNextAtFront = function (nextAtFront) {
this.nextAtFront = nextAtFront
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setEdgesPagination = function (edges) {
this.edges = edges
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setUseStartEdge = function (useStartEdge) {
this.useStartEdge = useStartEdge
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setEllipseText = function (ellipseText) {
this.ellipseText = ellipseText
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setSelectOnClick = function (selectOnClick) {
this.selectOnClick = selectOnClick
return this;
};
/**
* Sets the size for the grid's pages.
* @param {Number} pageSize
* @chainable
*/
GridPanel.prototype.setUseEndEdge = function (useEndEdge) {
this.useEndEdge = useEndEdge
return this;
};
/**
* Returns the total number of pages on the grid.
* @param {Boolean} [filter=false] If the calculation will be done taking only the filtered results.
* @return {Number}
*/
GridPanel.prototype.getTotalPages = function (filter) {
var targetListSize = filter ? this.filteredItems.getSize() : this.items.getSize();
return !this.items.getSize() ? 0 : (this.pageSize === 0 ? 1 : Math.ceil(targetListSize / this.pageSize));
};
/**
* It defines the events for the Grid.
* @chainable
*/
GridPanel.prototype.defineEvents = function () {
var that = this,
i,
columnsNum = this.columns.getSize();
this.removeEvents().eventsDefined = true;
if (this.dom.pager) {
this.filterControl.defineEvents();
for (i = 0; i < columnsNum; i += 1) {
this.columns.get(i).defineEvents();
}
/*for(i = 0; i < this.items.getSize(); i += 1) {
this.items.get(i).defineEvents();
}*/
this.addEvent('click').listenWithDelegation(this.dom.pager, 'a', function (e) {
var where;
e.preventDefault();
e.stopPropagation();
where = jQuery(this).data("index");
if (that._dynamicLoad) {
if ((where > -1 && where < that._dynamicLoad.totalPages) && (that.currentPage !== where)) {
that.goToPage(where === 'n' ? that.currentPage + 1 : (where === 'p' ? that.currentPage - 1 : where));
}
} else {
that.goToPage(where === 'n' ? that.currentPage + 1 : (where === 'p' ? that.currentPage - 1 : where));
}
});
this.addEvent('keypress').listenWithDelegation(this.dom.pager, 'input', function (e) {
if (e.keyCode == 13 || e.keyCode == 32) {
e.preventDefault();
e.stopPropagation();
if (/^\d*$/.test(this.value) && (this.value <= that._dynamicLoad.totalPages)) {
if (this.value > 0) {
that.goToPage(parseInt(this.value) - 1);
}
}
}
});
this.addEvent('change').listenWithDelegation(this.dom.pager, 'input', function (e) {
e.preventDefault();
e.stopPropagation();
if (this.value !== that.currentPage) {
this.value = that.currentPage + 1;
}
});
}
return this;
};
/**
* Sets the current page to be displayed in the grid.
* @param {Number} currentPage
* @chainable
*/
GridPanel.prototype.setCurrentPage = function (currentPage) {
this.currentPage = currentPage;
if (this.html) {
this.goToPage(this.currentPage);
}
return this;
};
/**
* Clears the filter.
* @chainable
*/
GridPanel.prototype.clearFilter = function () {
this.filterCriteria = null;
this.goToPage(this.currentPage);
return this;
};
/**
* Executes the sorting.
* @param {String} criteria The sorting criteria.
* @param {String} [sortType="asc"] The sorting order: "asc" or "desc".
* @chainable
*/
GridPanel.prototype.sort = function (criteria, sortType) {
var items,
theColumn,
i,
columns,
evaluate;
this.sortingData.criteria = criteria;
this.sortingData.type = sortType;
evaluate = function (a, b) {
var dataA,
dataB,
res;
if (typeof criteria === 'function') {
dataA = criteria.call(a, a.getData()).toString();
dataB = criteria.call(b, b.getData()).toString();
} else {
dataA = a.getData();
dataB = b.getData();
dataA = (dataA[criteria] && dataA[criteria].toString()) || "";
dataB = (dataB[criteria] && dataB[criteria].toString()) || "";
}
if (dataA < dataB) {
res = 1;
} else if (dataA > dataB) {
res = -1;
} else {
res = 0;
}
if (sortType === 'asc') {
res *= -1;
}
return res;
};
if (!this.html) {
return this;
}
this.updateUsableItemsList();
items = this.usableItemsList.asArray();
items.sort(evaluate);
this.usableItemsList.set(items);
columns = this.getColumns();
for (i = 0; i < columns.length; i += 1) {
columns[i].style.removeClasses(['pmui-sort-asc', 'pmui-sort-desc']);
if (columns[i].columnData === criteria) {
theColumn = columns[i];
}
}
if (theColumn) {
theColumn.style.addClasses(['pmui-sort-' + sortType]);
}
if (this.filterCriteria) {
this.filter(this.filterCriteria);
} else {
this.goToPage(this.currentPage);
}
this.goToPage(this.currentPage);
return this;
};
/**
* Returns the function to be executed when an accepted draggedgable is dragged over the droppable area.
* @return {Function}
*/
GridPanel.prototype.onDragOver = function () {
return function () {
};
};
/**
* Filters the rows using a criteria. The filter will be applied only in the columns in which its property
* "searchable" is true.
* @param {String} criteria The criteria.
* @chainable
*/
GridPanel.prototype.filter = function (criteria) {
var i,
columns,
j,
cell,
regExp,
rowsCopy,
visibleRows,
pmuiObject,
sortingCriteria = this.sortingData.criteria;
if (typeof criteria === 'string') {
if (criteria === "") {
this.clearFilter();
return this;
}
this.filterCriteria = criteria;
} else if (typeof criteria === 'number') {
this.filterCriteria = criteria.toString(10);
}
if (!this.dom.tbody) {
return this;
}
columns = this.columns.asArray();
regExp = new RegExp(this.filterCriteria.replace(/([\\\.\[\]\^\$\(\)\?\*\+\|\{\}])/g, "\\\$1"), "i");
rowsCopy = this.usableItemsList.asArray().slice(0);
visibleRows = jQuery(this.dom.tbody).find('tr');
for (i = 0; i < visibleRows.length; i += 1) {
pmuiObject = PMUI.getPMUIObject(visibleRows[i]);
if (pmuiObject) {
jQuery(pmuiObject.html).detach();
}
}
this.filteredItems.clear();
//if there is a sorting criteria set at the moment, first when use it to filter the rows
//this is necessary to keep the current sorting order.
if (sortingCriteria) {
for (j = 0; j < rowsCopy.length; j += 1) {
for (i = 0; i < columns.length; i += 1) {
if (columns[i].getDataType() !== 'button' && columns[i].isSearchable()) {
if (!rowsCopy[j].html) {
rowsCopy[j].getHTML();
rowsCopy[j].setCells();
}
cell = rowsCopy[j].getCells()[i];
if (regExp.test(cell.getContent()) && cell.visible) {
this.filteredItems.insert(rowsCopy.splice(j, 1)[0]);
j -= 1;
break;
}
}
}
}
}
if (rowsCopy.length) {
for (i = 0; i < columns.length; i += 1) {
if (columns[i].getDataType() !== 'button' && columns[i].isSearchable()) {
for (j = 0; j < rowsCopy.length; j += 1) {
if (!rowsCopy[j].html) {
rowsCopy[j].getHTML();
rowsCopy[j].setCells();
}
cell = rowsCopy[j].getCells()[i];
if (regExp.test(cell.getContent()) && cell.visible) {
this.filteredItems.insert(rowsCopy.splice(j, 1)[0]);
j -= 1;
}
}
}
}
}
this.goToPage(0, true);
return this;
};
/**
* [setSelectable description]
* @param {[type]} value [description]
*/
GridPanel.prototype.setSelectable = function (value) {
if (typeof value !== 'boolean') {
throw new Error('setSelectable(): the property is not valid, should be a boolean');
}
this.selectable = value;
if (!value && this.selectedRow) {
this.selectedRow.deselectRow();
this.selectedRow = null;
}
return this;
};
/**
* fixed the property visibleFooter, to hide or not the foot of GridPanel
* @param {Boolean} value the property should be type 'boolean'
*/
GridPanel.prototype.setVisibleFooter = function (value) {
if (typeof value !== 'boolean') {
throw new Error("setFooterHidden(): the value is no valid, should be a value type 'boolean'");
}
this.visibleFooter = value;
if (this.html) {
if (this.visibleFooter) {
this.showFooter();
} else {
this.hiddenFooter();
}
}
return this;
};
/**
* hidden the footer of the GridPanel if is visible
* @chainable
*/
GridPanel.prototype.hiddenFooter = function () {
this.dom.footer.style.display = 'none';
return this;
};
/**
* show the footer of the GridPanel if it has been hidden
* @chainable
*/
GridPanel.prototype.showFooter = function () {
this.dom.footer.style.display = 'block';
return this;
};
/**
* Set the height for the HTML element
* @param {Number|String} height it can be a number or a string.
* In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
* also sets the height of the table being rendered, according to the high set in order gridPanel
* @chainable
*/
GridPanel.prototype.setHeight = function (height) {
GridPanel.superclass.prototype.setHeight.call(this, height);
if (this.dom.tableContainer) {
if (typeof this.height !== "string") {
this.dom.tableContainer.style.height = this.height - 74 + 'px';
} else {
this.dom.tableContainer.style.height = 'auto';
}
}
return this;
};
PMUI.extendNamespace("PMUI.grid.GridPanel", GridPanel);
if (typeof exports !== 'undefined') {
module.exports === GridPanel;
}
}());
(function () {
/**
* @class PMUI.grid.GridPanelColumn
* Class that represents a column to be used in a {@link PMUI.grid.GridPanel GridPanel}.
* Usually you don't need to instantiate this class since the {@link PMUI.grid.GridPanel GridPanel} do it on its
* own, in fact, this class can't be instantiate if a grid parent
* ({@link PMUI.grid.GridPanel#cfg-grid grid config option}) is not specified.
* @extends {PMUI.core.Element}
*
* We don't include any example for this class 'cause we don't recommend you to do it, let the GridPanelColumn do
* it. Anyway, if you need add new columns to a GridPanel use its
* {@link PMUI.grid.GridPanel#method-addColumn addColumn() method}.
*
* @constructor
* Creates a new instance of the GridPanelColumn object.
* @param {Object} settings A JSON object with the config options.
*
* @cfg {String} [title='[untitled]'] The text to display in the column header.
* @cfg {String} [dataType='string'] The column data type. The accepted values are: "string", "number", "index" or
* "date".
* @cfg {Boolean} [sortable=false] If the column will have the sorting function enabled.
* @cfg {Boolean} [searchable=true] If the column must be filter when a filter criteria is set.
* @cfg {Function} [onButtonClick=null] A callback function to be called when a column's button is clicked. For
* info about the callback parameters please read the
* {@link PMUI.grid.GridPanelColumn#event-onButtonClick onButtonClick event doc}.
* @cfg {String} [buttonLabel='[button]'] The label for the button on a column for buttons.
* @cfg {Array} [cells=[]] The cells that belongs to the column.
* @cfg {PMUI.grid.GridPanel} grid The grid the column belongs to.
* @cfg {String|Function} [columnData=null]
* - In case of a String: The data key to be used for the cells in this column.
* - In case of a Function: A function that will return the content for the cells in column, the parameters
* received by the function are:
* - data {Object}: a JSON object with the data for the current row.
* The function will be called in the context of the current {@link PMUI.grid.GridPanelRow row object}.
* @cfg {Function} [onSort=null] A callback function to be called every time the sorting is executed.
* @cfg {String} [alignmentCell="center"] A string that specifies the alignmentCell for the content of cells that
* appertains to this column. The accepted values are: "center", "left", "right".
* @cfg {Object|PMUI.util.Style|null} [cellStyle=null] The style to be applied to the cells that belongs to the
* column, if it is set to null (the default value) no custom styles will be applied to the cells.
* @cfg {Object|PMUI.util.Style|Function|null} [buttonStyle=null] The style for the buttons in the column (only applicable
* if the column has its {@link #property-dataType dataType property} set to "button"). It can be:
*
* - null then the buttons won't apply any custom style.
* - An object literal in that case it can have the properties: cssClasses (an array in which each element is
* an string that is a class name) and cssProperties (another object literal that can contain css properties and
* its respective values).
* - An instance of {@link PMUI.util.Style}.
* - A function that must return any of the previous value types. The parameters received by the functions are:
* - the {@link PMUI.grid.GridPanelCell GridPanelCell} object in which the style is being applied.
* - data {Object}: a JSON object with the data for the current row.
* The function will be called in the context of the current {@link PMUI.grid.GridPanelRow row object}.
* @cfg {Boolean} [disabledButtons=false] If the buttons will be disabled.
* @cfg {String|Function|null} [buttonTooltip=null] Sets the tooltip message for the buttons (only applicable if
* the column has its {@link #property-dataType dataType property} set to "button"). It can be a string
* or null or a function. In the latter case the function must return a string, it will receive two parameters:
*
* - The cell, a {@link PMUI.grid.GridPanelCell GridPanelCell} object.
* - The data that belongs to the current row.
*
* The function will be called in the context of the current
* {@link PMUI.grid.GridPanelCell GridPanelCell cell object}.
*/
var GridPanelColumn = function (settings) {
/**
* The cells that belongs to the column.
* @type {PMUI.util.ArrayList}
*/
this.cells = new PMUI.util.ArrayList();
GridPanelColumn.superclass.call(this, jQuery.extend(settings, {elementTag: "th"}));
/**
* The text to be shown in the column header.
* @type {String}
*/
this.title = null;
/**
* The data type for the cells in this column. It is set by the {@link #cfg-dataType dataType config option} or
* the {@link #method-setDataType setDataType()} method.
* @type {String}
*/
this.dataType = null;
/**
* If the column has the sorting functionality enabled.
* @type {Boolean}
*/
this.sortable = null;
/**
* If the column has the searching functionality enabled.
* @type {Boolean}
*/
this.searchable = null;
/**
* @event onButtonClick
* Fired when a button in this column is clicked. The event is called in the column context.
* @param {PMUI.grid.GridPanelRow} row The row the button belongs to.
* @param {PMUI.grid.GridPanel} grid The grid the button belongs to.
*/
this.onButtonClick = null;
/**
* The label for the buttons in the column (only applicable when the datatType is set to "button").
* @type {String|Function}
*/
this.buttonLabel = null;
/**
* If the buttons are disabled or not.
* @type {Boolean}
*/
this.disabledButtons = null;
/**
* The grid the column belongs to.
* @type {PMUI.grid.GridPanel}
*/
this.grid = null;
/**
* The data key to be used for the cells in this column.
* @type {String}
*/
this.columnData = null;
/**
* The column sort order ("ASC" or "DESC").
* @type {String}
*/
this.sortOrder = null;
/**
* The alignmentCell to be applied to the content of the cells that appertain to this column.
* @type {String}
*/
this.alignmentCell = null;
/**
* The style object for the cells that belongs to the column.
* @type {PMUI.util.Style}
* @private
*/
this.cellStyle = null;
/**
* @property {PMUI.util.Style} buttonStyle The style object to be applied in the buttons that belong to the
* column. Only applicable when the column's type is 'button'.
*/
this.buttonStyle = null;
/**
* The HTML element in which the cell content will be appended.
* @type {HTMLElement}
* @private
*/
this.contentTitle = null;
/**
* The tooltip to be shown for the buttons in the column. Only applicable when the column's type is 'button'.
* @type {String|Function}
*/
this.buttonTooltip = null;
/**
* The DOM element in which the sorting arrow is shown.
* @type {HTMLElement}
* @private
*/
this.sortIcon = null;
/**
* Determines the alignment for the title in the column's header,
* @type {String}
*/
this.alignmentTitle = null;
GridPanelColumn.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.core.Element", GridPanelColumn);
GridPanelColumn.prototype.type = "GridPanelColumn";
/**
* Initialize the object
* @param {Object} [settings=null] A JSON object with the config options.
* @private
*/
GridPanelColumn.prototype.init = function (settings) {
var defaults = {
title: '[untitled]',
dataType: 'string',
sortable: false,
searchable: true,
onButtonClick: function () {
},
buttonLabel: '[button]',
cells: [],
grid: null,
columnData: null,
onSort: null,
alignmentCell: "center",
cellStyle: null,
buttonStyle: null,
visible: true,
disabledButtons: false,
buttonTooltip: null,
alignmentTitle: 'center',
positionMode: 'inherit'
};
jQuery.extend(true, defaults, settings);
this.buttonStyle = new PMUI.util.Style();
this.cellStyle = new PMUI.util.Style();
this.onSort = defaults.onSort;
this.setPositionMode(defaults.positionMode);
this.setTitle(defaults.title)
.setButtonTooltip(defaults.buttonTooltip)
.setCellStyle(defaults.cellStyle)
.setButtonStyle(defaults.buttonStyle)
.setAlignmentCell(defaults.alignmentCell)
.setColumnData(defaults.columnData)
.setDataType(defaults.dataType)
.setOnButtonClick(defaults.onButtonClick)
.setButtonLabel(defaults.buttonLabel)
.setGrid(defaults.grid)
.setSortable(defaults.sortable)
.setCells(defaults.cells)
.setVisible(defaults.visible)
.setAlignmentTitle(defaults.alignmentTitle);
if (defaults.searchable) {
this.enableSearch();
} else {
this.disableSearch();
}
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
};
/**
* Sets the button tooltip. Only applicable
* @param {String|Function|null} tooltip The tooltip to be used in the buttons in the column. It can be a string
* or null or a function. In the latter case the function must return a string, it will receive two parameters:
*
* - The cell, a {@link PMUI.grid.GridPanelCell GridPanelCell} object.
* - The data that belongs to the current row.
*
* The function will be called in the context of the current
* {@link PMUI.grid.GridPanelCell GridPanelCell cell object}.
*/
GridPanelColumn.prototype.setButtonTooltip = function (tooltip) {
if (!(typeof tooltip === 'function' || typeof tooltip === 'string' || tooltip === null)) {
throw new Error('setButtonTooltip(): the parameter must be a function or a string or null.');
}
this.buttonTooltip = tooltip;
return this;
};
/**
* Disables all the buttons in the column, this is applied only if the column's property
* {@link #property-dataType dataType} is set to "button".
* @chainable
*/
GridPanelColumn.prototype.disable = function () {
var i,
cells,
cellsLength;
this.disabledButtons = true;
cellsLength = (cells = this.cells.asArray()).length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].disable();
}
return this;
};
/**
* Disable one item cell
* @param index
* @returns {PMUI.grid.GridPanelColumn.options.GridPanelColumn}
*/
GridPanelColumn.prototype.disableCell = function (index) {
var cells = this.cells.asArray(),
cellsLength = cells.length;
if (index < cellsLength && index >= 0) {
cells[index].disable();
}
return this;
};
/**
* Enable one item cell
* @param index
* @returns {PMUI.grid.GridPanelColumn.options.GridPanelColumn}
*/
GridPanelColumn.prototype.enableCell = function (index) {
var cells = this.cells.asArray(),
cellsLength = cells.length;
if (index < cellsLength && index >= 0) {
cells[index].enable();
}
return this;
};
/**
* Enables all the buttons in the column, this is applied only if the column's property
* {@link #property-dataType dataType} is set to "button".
* @chainable
*/
GridPanelColumn.prototype.enable = function () {
var i,
cells,
cellsLength;
if (this.dataType === 'button') {
this.disabledButtons = false;
cellsLength = (cells = this.cells.asArray()).length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].enable();
}
}
return this;
};
/**
* [setVisible description]
* @param {[type]} visible [description]
*/
GridPanelColumn.prototype.setVisible = function (visible) {
var cells,
cellsLength,
i;
GridPanelColumn.superclass.prototype.setVisible.call(this, visible);
if (this.cells) {
cells = this.cells.asArray();
cellsLength = cells.length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setVisible(this.visible);
}
}
return this;
};
/**
* [getUsableButtonStyle description]
* @param {[type]} cell [description]
* @return {[type]} [description]
*/
GridPanelColumn.prototype.getUsableButtonStyle = function (cell) {
var theStyle,
buttonStyle = this.buttonStyle,
theRow;
if (typeof buttonStyle !== 'function') {
return {
cssClasses: buttonStyle.cssClasses,
cssProperties: buttonStyle.cssProperties
};
} else {
theRow = cell.row;
theStyle = buttonStyle.call(theRow, cell, theRow.getData());
return {
cssClasses: (theStyle && theStyle.cssClasses) || [],
cssProperties: (theStyle && theStyle.cssProperties) || {}
};
}
};
/**
* Sets the style for the buttons in the column (only applicable if the column has its
* {@link #property-dataType dataType property} set to "button").
* @param {PMUI.util.Style|Object|null|Function} cellStyle The style to be applied. It can be:
*
* - null then the buttons won't apply any custom style.
* - An object literal in that case it can have the properties: cssClasses (an array in which each element is
* an string that is a class name) and cssProperties (another object literal that can contain css properties and
* its respective values).
* - An instance of {@link PMUI.util.Style}.
* - A function that must return any of the previous value types.
* @chainable
*/
GridPanelColumn.prototype.setButtonStyle = function (buttonStyle) {
var i,
cells,
cellsNum,
theStyle,
theRow;
if (buttonStyle !== null && typeof buttonStyle !== 'object' && typeof buttonStyle !== 'function' && !(buttonStyle instanceof PMUI.util.Style)) {
throw new Error("setbuttonStyle(): The parameter only can be an object, an instance of PMUI.util.Style or null.");
}
this.buttonStyle = this.buttonStyle.clear();
if (buttonStyle instanceof PMUI.util.Style) {
this.buttonStyle = buttonStyle;
} else if (typeof buttonStyle === 'function') {
this.buttonStyle = buttonStyle;
} else if (buttonStyle) {
this.buttonStyle.addProperties((buttonStyle && buttonStyle.cssProperties) || {})
.addClasses((buttonStyle && buttonStyle.cssClasses) || []);
}
if (this.dataType === 'button') {
cells = this.cells.asArray();
cellsNum = cells.length;
for (i = 0; i < cellsNum; i += 1) {
cells[i].getContent().setStyle(this.getUsableButtonStyle(cells[i]));
}
}
return this;
};
/**
* Returns the style for the buttons that belongs to the column.
* @return {Object} An object literal with the properties: cssProperties and cssClasses
*/
GridPanelColumn.prototype.getButtonStyle = function () {
return {
cssClasses: this.buttonStyle.cssClasses,
cssProperties: this.buttonStyle.cssProperties
};
};
/**
* Sets the style for the cells in the column.
* @param {PMUI.util.Style|Object|null} cellStyle The style to be applied. If it is null then the cells won't apply
* any custom style.
*/
GridPanelColumn.prototype.setCellStyle = function (cellStyle) {
var i,
cells,
cellsNum;
if (cellStyle !== null && typeof cellStyle !== 'object' && !(cellStyle instanceof PMUI.util.Style)) {
throw new Error("setCellStyle(): The parameter only can be an object, an instance of PMUI.util.Style or null.");
}
this.cellStyle = this.cellStyle.clear();
if (cellStyle instanceof PMUI.util.Style) {
this.cellStyle = cellStyle;
} else if (cellStyle) {
this.cellStyle.addProperties(cellStyle.cssProperties || {})
.addClasses(cellStyle.cssClasses || []);
}
cells = this.cells.asArray();
cellsNum = cells.length;
for (i = 0; i < cellsNum; i += 1) {
cells[i].setStyle({
cssClasses: this.cellStyle.cssClasses,
cssProperties: this.cellStyle.cssProperties
});
}
return this;
};
/**
* Returns the style for the cells that belongs to the column.
* @return {Object} An object literal with the properties: cssProperties and cssClasses
*/
GridPanelColumn.prototype.getCellStyle = function () {
return {
cssProperties: this.cellStyle.cssProperties,
cssClasses: this.cellStyle.cssClasses
};
};
/**
* Sets the alignmentCell for the content in the cells that appertain to the column.
* @param {String} alignmentCell The accepted values are: "center", "left", "right".
* @chainable
*/
GridPanelColumn.prototype.setAlignmentCell = function (alignmentCell) {
var i,
cells;
if (alignmentCell === 'center' || alignmentCell === 'left' || alignmentCell === 'right') {
this.alignmentCell = alignmentCell;
cells = this.getCells();
for (i = 0; i < cells.length; i += 1) {
cells[i].setAlignment(this.alignmentCell);
}
} else {
throw new Error('setAlignment(): This method only accepts one of the following values: '
+ '"center", "left", "right"');
}
return this;
};
GridPanelColumn.prototype.setAlignmentTitle = function (alignmentTitle) {
if (alignmentTitle === 'center' || alignmentTitle === 'left' || alignmentTitle === 'right') {
this.alignmentTitle = alignmentTitle;
if (this.contentTitle) {
this.contentTitle.style.textAlign = alignmentTitle;
}
} else {
throw new Error('setAlignment(): This method only accepts one of the following values: '
+ '"center", "left", "right"');
}
return this;
};
/**
* Returns the alignment for the content in the cells that appertain to the column.
* @return {String}
*/
GridPanelColumn.prototype.getAlignment = function () {
return this.alignment;
};
/**
* Returns true is the filtering function is enabled, otherwise returns false.
* @return {Boolean}
*/
GridPanelColumn.prototype.isSearchable = function () {
return this.searchable;
};
/**
* Enables the filtering functionality.
* @chainable
*/
GridPanelColumn.prototype.enableSearch = function () {
this.searchable = true;
return this;
};
/**
* Disables the filtering functionality.
* @chainable
*/
GridPanelColumn.prototype.disableSearch = function () {
this.searchable = false;
return this;
};
/**
* Returns the data's field name that is used by the row to fill its cell for this column.
* @return {String}
*/
GridPanelColumn.prototype.getColumnData = function () {
return this.columnData;
};
/**
* Sets a function or a string that will represent the data's field name that is used by the row to fill its cell
* for this column.
* @param {String|Function} columnData It can be a String or a Function
* - In case of a String: The data key to be used for the cells in this column.
* - In case of a Function: A function that will return the content for the cells in column, the parameters receive
* by the function are:
* - data {Object}: a JSON object with the data for the current row.
* The function will be called in the context of the current {@link PMUI.grid.GridPanelRow row object}.
* @chainable
*/
GridPanelColumn.prototype.setColumnData = function (columnData) {
if (columnData === null || typeof columnData === 'string' || typeof columnData === 'function') {
this.columnData = columnData;
} else {
throw new Error('setColumnData(): The method only accepts a string or a function or NULL as parameter.');
}
return this;
};
/**
* Sets the title to be shown in the column header.
* @param {String} title
* @chainable
*/
GridPanelColumn.prototype.setTitle = function (title) {
this.title = title;
if (this.html) {
jQuery(this.contentTitle).empty();
this.contentTitle.appendChild(document.createTextNode(title));
this.contentTitle.appendChild(this.sortIcon);
}
return this;
};
/**
* Returns the column's data type.
* @return {String}
*/
GridPanelColumn.prototype.getDataType = function () {
return this.dataType;
};
/**
* Sets the data type for the column.
* @param {String} dataType The accepted values are:
*
* - "number" for cells with numeric values.
* - "string" for cells with string values.
* - "index" for cells with the row index.
* - "date" for cells with date values.
* - "button" for cells that contain a button.
* @chainable
*/
GridPanelColumn.prototype.setDataType = function (dataType) {
if (dataType === 'string' || dataType === 'number' || dataType === 'index' || dataType === 'date' || dataType === 'button') {
this.dataType = dataType;
} else {
throw new Error('setDataType(): It only accepts "string", "number", "index" or "date".');
}
return this;
};
/**
* Enables or disables the sorting functionality.
* @param {Boolean} sortable
* @chainable
*/
GridPanelColumn.prototype.setSortable = function (sortable) {
if (!(typeof sortable === 'boolean')) {
throw new Error("setSortable(): property 'sortable' should be a boolean");
}
//TODO enable event to listen the clicks on column for sorting
this.sortable = sortable;
if (this.html) {
if (sortable) {
this.style.addClasses(['pmui-sortable']);
} else {
this.style.removeClasses(['pmui-sortable']);
}
}
return this;
};
/**
* Returns the callback function to be executed when a column's button is clicked.
* @return {Function}
*/
GridPanelColumn.prototype.getOnButtonClick = function () {
return this.onButtonClick;
};
/**
* Sets the callback function to be executed when a column's button is clicked.
* @param {Function} onButtonClick
* @chainable
*/
GridPanelColumn.prototype.setOnButtonClick = function (onButtonClick) {
if (!(typeof onButtonClick == 'function')) {
throw new Error("setOnButtonClick(): property 'onButtonClick' should be a function");
}
this.onButtonClick = onButtonClick;
//TODO update the listeners for all the buttons in the column
return this;
};
/**
* Sets the text for the button labels in the column.
* @param {String|Function} buttonLabel A string or a function that returns the string for the button. Three
* parameters function will be sent to the function:
*
* - The row the button belongs to ({@link PMUI.grid.GridPanelRow GridPanelRow}).
* - The data from the row (object literal).
*
* It is important to mention that the function is called in the context of the column object.
* @chainable
*/
GridPanelColumn.prototype.setButtonLabel = function (buttonLabel) {
if (typeof buttonLabel !== 'string' && typeof buttonLabel !== 'function') {
throw new Error('setButtonLabel(): the parameter must be a string or a function.');
}
this.buttonLabel = buttonLabel;
//TODO update the label for all the buttons in the column
return this;
};
/**
* Set the grid the column belongs to.
* @param {PMUI.grid.Grid} grid
* @chainable
*/
GridPanelColumn.prototype.setGrid = function (grid) {
if (!(grid instanceof PMUI.grid.GridPanel)) {
throw new Error('setGrid(): the grid property should be instance of [PMUI.grid.GridPanel]');
}
this.grid = grid;
return this;
};
/**
* Adds a cell to the column.
* @param {PMUI.grid.GridPanelCell} cell
* @chainable
*/
GridPanelColumn.prototype.addCell = function (cell) {
if (!(cell instanceof PMUI.grid.GridPanelCell)) {
throw new Error("addCell(): this method only accepts instances of PMUI.grid.GridPanelCell.");
}
cell.setAlignment(this.alignmentCell);
cell.setStyle({
cssProperties: this.cellStyle.getProperties(),
cssClasses: this.cellStyle.getClasses()
});
this.cells.insert(cell);
return this;
};
/**
* Set the cells for the column.
* @param {PMUI.util.ArrayList|Array} cells An array in which every element is an instance of the
* {@link PMUI.grid.GridPanelCell GridPanelCell class}.
* @chainable
*/
GridPanelColumn.prototype.setCells = function (cells) {
var cellsAux = [],
i;
this.clearCells();
if (cells instanceof PMUI.util.ArrayList) {
cellsAux = cells.items.asArray();
} else if (jQuery.isArray(cells)) {
cellsAux = cells;
} else {
throw new Error('setCells(): the cells property should be instanceof of PMUI.util.ArrayList or an Array');
}
for (i = 0; i < cellsAux.length; i += 1) {
this.addCell(cellsAux[i]);
}
return this;
};
/**
* Return the column's cells.
* @return {Array} An array in which each element is an instance of the
* {@link PMUI.grid.GridPanelCell GridPanelCell class}.
*/
GridPanelColumn.prototype.getCells = function () {
return this.cells.asArray().slice(0);
};
/**
* Removes a cell from the column.
* @param {PMUI.grid.GridPanelCell|Number|String} cell The cell to remove, it can be:
*
* - An instance of {@link PMUI.grid.GridPanelCell GridPanelCell}, in this case this cell must belong to the
* column.
* - A Number, in this case it is interpreted as the index of the cell to remove.
* - A String, in this case it is interpreted as the id of the cell to remove.
* @chainable
*/
GridPanelColumn.prototype.removeCell = function (cell) {
var cellToRemove;
if (cell instanceof PMUI.grid.GridPanelCell) {
cellToRemove = cell;
} else if (typeof cell === 'number') {
cellToRemove = this.cells.get(cell);
} else if (typeof cell === 'string') {
cellToRemove = this.cells.find("id", cell);
}
if (cellToRemove) {
cellToRemove.column = null;
this.cells.remove(cellToRemove);
}
return this;
};
/**
* Clears all the cells of the column.
* @chainable
*/
GridPanelColumn.prototype.clearCells = function () {
this.cells.clear();
while (this.cells.getSize() > 0) {
this.removeCell(0);
}
return this;
};
/**
* Defines the event for the object.
* @chainable
*/
GridPanelColumn.prototype.defineEvents = function () {
var that = this;
this.removeEvents().eventsDefined = true;
if (this.html) {
this.addEvent('click').listen(this.html, function () {
if (that.sortable) {
that.sortOrder = that.sortOrder === 'asc' ? 'desc' : 'asc';
if (typeof that.onSort === 'function') {
that.onSort(that.sortOrder);
}
}
});
}
return this;
};
/**
* Creates the html for the object.
* @return {HTMLElement}
*/
GridPanelColumn.prototype.createHTML = function () {
var contentTitle,
sortIcon;
if (this.html) {
return this.html;
}
GridPanelColumn.superclass.prototype.createHTML.call(this);
sortIcon = PMUI.createHTMLElement('span');
sortIcon.className = 'pmui-grid-sort-icon';
contentTitle = PMUI.createHTMLElement('span');
contentTitle.className = 'pmui-gridpanelColumn-title';
this.contentTitle = contentTitle;
this.sortIcon = sortIcon;
this.html.appendChild(contentTitle);
this.setTitle(this.title)
.setSortable(this.sortable)
.setWidth(this.width)
.setAlignmentTitle(this.alignmentTitle);
return this.html;
};
GridPanelColumn.prototype.setWidth = function (width) {
var finalWidth,
i,
cellsLength,
cells;
if (typeof width === 'number') {
this.width = width;
} else if (/^\d+(\.\d+)?px$/.test(width)) {
this.width = parseInt(width, 10);
} else if (/^\d+(\.\d+)?%$/.test(width)) {
this.width = width;
} else if (/^\d+(\.\d+)?em$/.test(width)) {
this.width = width;
} else if (width === 'auto') {
this.width = width;
} else {
throw new Error('setWidth: width param is not a number');
}
if (typeof this.width === 'string') {
finalWidth = 'auto';
} else {
finalWidth = this.width + 'px';
}
if (this.html) {
this.contentTitle.style.minWidth = finalWidth;
}
cellsLength = (cells = this.cells.asArray()).length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setWidth(finalWidth);
}
return this;
};
GridPanelColumn.prototype.applyStyle = function () {
if (this.html) {
this.style.applyStyle();
this.style.addProperties({
display: this.visible ? this.display : "none",
position: this.positionMode,
left: this.x,
top: this.y,
width: (typeof this.width === 'string') ? this.width : 'auto',
height: this.height,
zIndex: this.zOrder
});
}
return this;
};
PMUI.extendNamespace("PMUI.grid.GridPanelColumn", GridPanelColumn);
if (typeof exports !== 'undefined') {
module.exports = GridPanelColumn;
}
}());
(function () {
/**
* @class PMUI.grid.GridPanelRow
* Class that defines a row to be used in a {@link PMUI.grid.GridPanel GridPanel}.
* The single class is not very useful alone, it should be used with a {@link PMUI.grid.GridPanel GridPanel}.
* @extends {PMUI.core.Element}
*
* Usage example:
*
* @example
* var row = new PMUI.grid.GridPanelRow({
* data: {
* name: "John",
* lastname: "McAllister"
* }
* }),
* grid = new PMUI.grid.GridPanel({
* pageSize: 6,
* columns:[
* {
* title:'columna1',
* dataType:'string',
* columnData: "name"
* },
* {
* title:'columna2',
* dataType:'number',
* width : 150,
* columnData: "lastName",
* sortable: true
* }
* ],
* items: [
* row
* ]
* });
*
* document.body.appendChild(grid.getHTML());
*
* @constructor
* Creates a new instance of the GridPanelRow class.
* @param {Object} settings A JSON object with the config options.
*
* @cfg {PMUI.data.DataField|Object} [data={}] A {@link PMUI.data.DataField DataField} or a JSON object with the
* data for the GridPanelRow.
* @cfg {PMUI.grid.GridPanel} [parent=null] The {@link PMUI.grid.GridPanel GridPanel} the GridPanelRow belongs to.
*/
var GridPanelRow = function (settings) {
GridPanelRow.superclass.call(this, jQuery.extend(settings, {elementTag: "tr"}));
/**
* An ArrayList that contains the row's cells ({@link PMUI.grid.GridPanelCell GridPanelCell}).
* @type {PMUI.util.ArrayList}
* @private
*/
this.cells = null;
/**
* The parent GridPanel of the GridPanelRow.
* @type {PMUI.grid.GridPanel GridPanel}
* @readonly
*/
this.parent = null;
/**
* The data that belongs to the GridPanelRow.
* @type {PMUI.data.DataField}
* @private
*/
this.data = null;
this.onClick = null;
this.selected = null;
this.onSelect = null;
GridPanelRow.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', GridPanelRow);
/**
* The class family.
* @type {String}
*/
GridPanelRow.prototype.family = 'GridPanelRow';
/**
* The object's family.
* @type {String}
*/
GridPanelRow.prototype.type = 'GridPanelRow';
/**
* Initialize the object.
* @param {Object} settings A JSON object with the config options.
* @private
*/
GridPanelRow.prototype.init = function (settings) {
var defaults = {
data: {},
parent: null,
onClick: null,
onSelect: function () {
}
};
jQuery.extend(true, defaults, settings);
this.cells = new PMUI.util.ArrayList();
this.data = new PMUI.data.DataField();
this.setData(defaults.data)
.setParent(defaults.parent)
.setOnClick(defaults.onClick)
.setOnSelectCallback(defaults.onSelect);
};
/**
* Set the data for the object.
* @param {PMUI.data.DataField|Object} dataç
* @chainable
*/
GridPanelRow.prototype.setData = function (data) {
var key;
if (data instanceof PMUI.data.DataField) {
this.data = data;
} else if (typeof data === 'object') {
this.data.clear();
for (key in data) {
this.data.addAttribute(key, data[key]);
}
} else {
throw new Error("setData(): it only accepts a JSON object o an instance of PMUI.data.DataField as parameter.");
}
if (this.html) {
this.setCells();
}
return this;
};
/**
* Set the parent grid for the object.
* @param {PMUI.grid.GridPanel} parent
* @chainable
*/
GridPanelRow.prototype.setParent = function (parent) {
if (parent) {
if (parent instanceof PMUI.grid.GridPanel) {
this.parent = parent;
} else {
throw new Error("setParent() method only accepts a object [PMUI.panel.GridPanel] as parameter.");
}
}
return this;
};
/**
* Returns the object's parent grid.
* @return {PMUI.grid.GridPanel}
*/
GridPanelRow.prototype.getParent = function () {
return this.parent;
};
/**
* Updates the width for each row's cell using the respective column's width.
* @chainable
*/
GridPanelRow.prototype.updateCellsWidth = function () {
var i,
cells,
cellsLength;
cellsLength = (cells = this.cells.asArray()).length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setWidth(cells[i].column.width);
}
return this;
};
/**
* Paints a cell in the row.
* @param {PMUI.grid.GridPanelCell} cell
* @chainable
* @private
*/
GridPanelRow.prototype.paintCell = function (cell) {
if (this.html) {
this.html.appendChild(cell.getHTML());
}
return this;
};
/**
* Paints all cells in the row.
* @chainable
* @private
*/
GridPanelRow.prototype.paintCells = function () {
var i,
cells = this.cells.asArray();
for (i = 0; i < cells.length; i += 1) {
this.paintCell(cells[i]);
}
return this;
};
/**
* Add a single cell into the row.
* @param {PMUI.grid.GridPanelColumn|null} [column=null] It can be:
*
* - null, in this case an empty cell will be added at the end of the row.
* - A {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the cell will be created using the settings
* the parameter has for its cells. Also the cell will be associated to the column.
*/
GridPanelRow.prototype.addCell = function (column) {
var data,
columnData,
cell,
cellIndex,
buttonLabel,
buttonTooltip,
content,
that = this;
if (!(column === null || column instanceof PMUI.grid.GridPanelColumn)) {
throw new Error("addCell(): The parameter must be an instance of PMUI.grid.GridPanel o null.");
}
data = this.data.getRecord();
cellIndex = this.parent.getColumns().length;
cell = new PMUI.grid.GridPanelCell({
columnIndex: cellIndex,
row: this,
column: column || null,
visible: (column && column.visible) && true,
width: (column && column.width) || "auto"
});
if (column) {
columnData = column.getColumnData();
if (column.dataType === 'button') {
buttonLabel = column.buttonLabel;
buttonTooltip = column.buttonTooltip;
content = new PMUI.ui.Button({
style: column.getUsableButtonStyle(),
text: typeof buttonLabel === 'function' ? buttonLabel(this, data) : (buttonLabel || ""),
messageTooltip: (typeof buttonTooltip === 'function' ? buttonTooltip.call(cell, cell, data)
: (buttonTooltip || "")),
handler: (function (handler) {
return function () {
handler(that, that.parent);
};
}(column.getOnButtonClick()))
});
} else if (column.dataType === 'index') {
content = this.parent.getItemIndex(this) + 1;
} else if (columnData) {
if (typeof columnData === 'function') {
content = columnData.call(this, data);
} else {
content = this.data.getAttribute(columnData);
}
}
cell.style.addClasses(["pmui-gridpanelcell-" + column.dataType]);
} else {
content = null;
}
cell.setContent(content);
this.cells.insert(cell);
column.addCell(cell);
this.paintCell(cell);
return this;
};
/**
* @method setCells
* Build the cells for the row using its parent {@link PMUI.grid.GridPanel#property-columns GridPanel's columns} to
* retrieve the data from the odject's {@link PMUI.grid.GridPanelRow#property-data data}.
* @chainable
*/
GridPanelRow.prototype.setCells = function () {
var i,
columns;
this.clearCells();
if (this.parent) {
columns = this.parent.getColumns();
for (i = 0; i < columns.length; i += 1) {
this.addCell(columns[i]);
}
}
return this;
};
/**
* Returns the data that belongs to the object.
* @return {Object} A JSON object with the data.
*/
GridPanelRow.prototype.getData = function () {
return this.data.getRecord();
};
/**
* Returns the object's cells.
* @return {Array} An array in which every item is a object's {@link PMUI.grid.GridPanelCell cell}.
*/
GridPanelRow.prototype.getCells = function () {
return this.cells.asArray().slice(0);
};
/**
* Clear the cells from the object.
* @chainable
*/
GridPanelRow.prototype.clearCells = function () {
var i;
while (this.cells.getSize()) {
this.removeCell(0);
}
return this;
};
/**
* Remove a cell from the object.
* @param {PMUI.grid.GridPanelCell|Number|String} cell It can be:
*
* - A instance of {@link PMUI.grid.GridPanelCell GridPanelCell}, in this case it must be a cell that belongs to
* the object.
* - A Number, in this case the cell with that index will be removed.
* - A String, in this case the cell with that id will be removed.
* @chainable
*/
GridPanelRow.prototype.removeCell = function (cell) {
var cellToRemove,
column;
if (cell instanceof PMUI.grid.GridPanelCell) {
cellToRemove = cell;
} else if (typeof cell === 'number') {
cellToRemove = this.cells.get(cell);
} else if (typeof cell === 'string') {
cellToRemove = this.cells.find("id", cell)
} else {
throw new Error("removeCell(): This method only accepts a number or a string or a instance of " +
"PMUI.grid.GridPanelCell as parameter");
}
if (cellToRemove) {
cellToRemove.setColumnIndex(null);
cellToRemove.row = null;
jQuery(cellToRemove.html).remove();
column = cellToRemove.getColumn();
if (column) {
column.removeCell(cellToRemove);
}
cellToRemove.column = null;
this.cells.remove(cellToRemove);
}
return this;
};
/**
* Attach the listeners for the object and every of its children.
* @chainable
*/
GridPanelRow.prototype.defineEvents = function () {
var that = this;
this.removeEvents().eventsDefined = true;
if (this.html) {
this.addEvent('click').listen(this.html, function (e) {
e.preventDefault();
if (typeof that.onClick === 'function') {
that.onClick(that, that.getData());
}
if (that.parent.selectable) {
if (!that.selected) {
that.selectRow();
}
}
if (that.parent && typeof that.parent.onRowClick === 'function') {
that.parent.onRowClick(that, that.getData());
}
});
}
return this;
};
/**
* Creates the object's HTML.
* @return {HTMLElement}
*/
GridPanelRow.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
GridPanelRow.superclass.prototype.createHTML.call(this);
this.paintCells()
.defineEvents();
return this.html;
};
/**
* [setOnClick description]
* @param {[type]} onClick [description]
*/
GridPanelRow.prototype.setOnClick = function (onClick) {
if (typeof onClick !== 'function' && onClick !== null) {
throw new Error('setOnClick(): the parameter is no valid, should be a function');
}
this.onClick = onClick;
return this;
};
/**
* [selectRow description]
* @return {[type]} [description]
*/
GridPanelRow.prototype.selectRow = function () {
this.selected = true;
this.style.addClasses(['pmui-active-row']);
if (typeof this.onSelect === 'function') {
this.onSelect();
}
return this;
};
/**
* [isSelectedRow description]
* @return {Boolean} [description]
*/
GridPanelRow.prototype.isSelectedRow = function () {
return this.selected;
};
/**
* [deselectRow description]
* @return {[type]} [description]
*/
GridPanelRow.prototype.deselectRow = function () {
if (this.onSelect) {
this.selected = false;
this.style.removeClasses(['pmui-active-row']);
}
return this;
};
/**
* [setOnSelectRowCallback description]
* @param {Function} callback [description]
*/
GridPanelRow.prototype.setOnSelectCallback = function (callback) {
if (typeof callback === 'function' || callback == null) {
this.onSelect = callback;
} else {
throw new Error("setOnSelectCallback(): The parameter is not a function.");
}
return this;
};
PMUI.extendNamespace('PMUI.grid.GridPanelRow', GridPanelRow);
if (typeof exports !== 'undefined') {
module.exports = GridPanelRow;
}
}());
(function () {
/**
* @class PMUI.grid.GridPanelCell
* Class that represent a cell to be used in instances of {@link PMUI.grid.GridPanelRow GridPanelRow}.
* It's not very useful to instantiate this class to use it alone. Most of cases you won't need to instantiate it,
* the {@link PMUI.grid.GridPanelRow GridPanelRow} creates its own cells objects automatically.
* {@link PMUI.grid.GridPanelRow GridPanelRow}.
* @extends {PMUI.core.Element}
*
* Usage example:
*
* @example
* var cell = new PMUI.grid.GridPanelCell({
* content: "This is the cell content"
* });
*
* document.body.appendChild(cell.getHTML());
*
* @constructor
* Creates a new instance of the GridPanelCell class.
* @param {Object} settings A JSON object with the config options.
*
* @cfg {String|Number|PMUI.core.Element|null} [content=null] The content to be displayed in the cell, in case to
* be null the cells has no content.
* @cfg {Number} [columnIndex=null] The column index the cell belongs to.
* @cfg {PMUI.grid.GridPanelRow} [row=null] The row the cell belongs to.
* @cfg {PMUI.grid.GridPanelColumn} [column=null] The column the cell belongs to.
* @cfg {String} [alignment="center"] A string that specifies the alignment for the cell content. The accepted
* values are: "center", "left", "right".
* @cfg {Boolean} [disabled=false] If the cell will be disabled or not.
*/
var GridPanelCell = function (settings) {
GridPanelCell.superclass.call(this, jQuery.extend(settings, {elementTag: "td"}));
/**
* The content for the cell.
* @type {PMUI.core.Element|String|Number}
*/
this.content = null;
/**
* The position index for the cell, relative to its parent row.
* @type {Number}
*/
this.columnIndex = null;
/**
* The row the cell belongs to.
* @type {PMUI.grid.GridPanelRow}
*/
this.row = null;
/**
* The column the cell appertains to.
* @type {PMUI.grid.GridPanelColumn}
*/
this.column = null;
/**
* The alignment to be applied to the cell content.
* @type {String}
*/
this.alignment = null;
this.contentSpan = null;
/**
* If the cell content is disabled or not.
* @type {Boolean}
*/
this.disabled = null;
GridPanelCell.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.core.Element", GridPanelCell);
/**
* The object's type.
* @type {String}
*/
GridPanelCell.prototype.type = "GridPanelCell";
/**
* The class family.
* @type {String}
*/
GridPanelCell.prototype.family = "grid";
/**
* Initializes the object.
* @param {Object} settings A JSON object with the config options.
* @private
*/
GridPanelCell.prototype.init = function (settings) {
var defaults = {
content: null,
columnIndex: null,
row: null,
column: null,
alignment: "center",
disabled: false
};
jQuery.extend(true, defaults, settings);
this.row = defaults.row;
this.column = defaults.column;
this.eventsDefined = false;
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
this.setAlignment(defaults.alignment)
.setContent(defaults.content)
.setColumnIndex(defaults.columnIndex);
};
/**
* Sets the alignment for the content in the cell.
* @param {String} alignment The accepted values are: "center", "left", "right".
* @chainable
*/
GridPanelCell.prototype.setAlignment = function (alignment) {
if (alignment === 'center' || alignment === 'left' || alignment === 'right') {
this.alignment = alignment;
if (this.html && this.contentSpan) {
this.contentSpan.style.textAlign = this.alignment;
this.style.addProperties({'text-align': this.alignment});
}
} else {
throw new Error('setAlignment(): This method only accepts one of the following values: '
+ '"canter", "left", "right"');
}
return this;
};
/**
* Returns the cell's content alignment.
* @return {String}
*/
GridPanelCell.prototype.getAlignment = function () {
return this.alignment;
};
/**
* Sets the content for the cell.
* @param {String|Number|PMUI.core.Element|null} content The content to be displayed in the cell, in case to
* be null the cells has no content.
* @chainable
*/
GridPanelCell.prototype.setContent = function (content) {
var type,
visibleContent;
this.content = content;
if (content instanceof PMUI.core.Element) {
visibleContent = content.getHTML();
} else if (typeof content === 'number' || typeof content === 'string') {
visibleContent = document.createTextNode(content);
} else if (content === null || content === undefined) {
visibleContent = undefined;
} else {
throw new Error("setContent(): invalid parameter, it should be a Number, String or an instance of PMUI.core.Element.");
}
if (this.html) {
jQuery(this.contentSpan).empty();
if (visibleContent) {
this.contentSpan.appendChild(visibleContent);
} else {
this.contentSpan.innerHTML = ' ';
}
if (this.disabled) {
this.disable();
} else {
this.enable();
}
}
return this;
};
/**
* Returns the column the cell belongs to.
* @return {PMUI.grid.GridPanelColumn}
*/
GridPanelCell.prototype.getColumn = function () {
return this.column;
};
/**
* Returns the row the cell belongs to.
* @return {PMUI.grid.GridPanelRow}
*/
GridPanelCell.prototype.getRow = function () {
return this.row;
};
/**
* Returns the cell's content.
* @return {String|Number|PMUI.core.Element|null}
*/
GridPanelCell.prototype.getContent = function () {
return this.content;
};
/**
* Sets the index of the column the cell belongs to.
* @param {Number} columnIndex
*/
GridPanelCell.prototype.setColumnIndex = function (columnIndex) {
this.columnIndex = columnIndex
return this;
};
/**
* Returns the index of the column the cell belongs to.
* @return {Number}
*/
GridPanelCell.prototype.getColumnIndex = function () {
return this.columnIndex;
};
/**
* Defines the events for the object.
* @chainable
*/
GridPanelCell.prototype.defineEvents = function () {
this.eventsDefined = true;
if (this.content instanceof PMUI.core.Element) {
this.content.defineEvents();
}
return this;
};
/**
* Creates the object's html.
* @return {HTMLElement}
*/
GridPanelCell.prototype.createHTML = function () {
var type,
contentSpan;
if (this.html) {
return this.html;
}
GridPanelCell.superclass.prototype.createHTML.call(this);
contentSpan = PMUI.createHTMLElement('span');
contentSpan.className = 'pmui-gridpanelcell-content';
this.contentSpan = contentSpan;
this.html.appendChild(this.contentSpan);
this.setAlignment(this.alignment)
.setContent(this.content)
.setWidth(this.width)
.defineEvents();
return this.html;
};
GridPanelCell.prototype.setWidth = function (width) {
if (typeof width === 'number') {
this.width = width;
} else if (/^\d+(\.\d+)?px$/.test(width)) {
this.width = parseInt(width, 10);
} else if (/^\d+(\.\d+)?%$/.test(width)) {
this.width = width;
} else if (/^\d+(\.\d+)?em$/.test(width)) {
this.width = width;
} else if (width === 'auto') {
this.width = width;
} else {
throw new Error('setWidth: width param is not a number');
}
if (this.html) {
if (typeof this.width === 'string') {
this.contentSpan.style.width = 'auto';
} else {
this.contentSpan.style.width = this.width + 'px';
}
}
return this;
};
GridPanelCell.prototype.applyStyle = function () {
if (this.html) {
this.style.applyStyle();
this.style.addProperties({
display: this.visible ? this.display : "none",
position: this.positionMode,
left: this.x,
top: this.y,
width: (typeof this.width === 'string') ? this.width : 'auto',
height: this.height,
zIndex: this.zOrder
});
}
return this;
};
/**
* Enables the cell's content.
* @chainable
*/
GridPanelCell.prototype.enable = function () {
this.disabled = false;
if (this.content && this.content.enable) {
this.content.enable();
}
return this;
};
/**
* Disables the cell's content.
* @chainable
*/
GridPanelCell.prototype.disable = function () {
this.disabled = true;
if (this.content && this.content.disable) {
this.content.disable();
}
return this;
};
PMUI.extendNamespace("PMUI.grid.GridPanelCell", GridPanelCell);
if (typeof exports !== 'undefined') {
module.exports = GridPanelCell;
}
}());
(function () {
var ControlGrid = function (settings) {
ControlGrid.superclass.call(this, jQuery.extend(true, settings, {
factory: {
products: {
"GridControlRow": PMUI.grid.GridControlRow
},
defaultProduct: "GridControlRow"
}
}));
this.columnsFactory = new PMUI.util.Factory();
this.dependencies = {};
ControlGrid.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridPanel', ControlGrid);
ControlGrid.prototype.init = function (settings) {
var defaults = {
allowedCells: {
'text': PMUI.grid.TextControlColumn,
'dropdownlist': PMUI.grid.DropDownListControlColumn,
'checkboxgroup': PMUI.grid.CheckBoxGroupColumn,
'number': PMUI.grid.NumberColumn
}
};
jQuery.extend(true, defaults, settings);
this.setAllowedCells(defaults.allowedCells)
.setColumns(defaults.columns);
};
ControlGrid.prototype.setAllowedCells = function (allowedCells) {
this.columnsFactory.clearProducts()
.setProducts(allowedCells);
return this;
};
ControlGrid.prototype.getColumn = function (column) {
var the_column = null;
if (typeof column === 'string') {
the_column = this.columns.find('name', column);
} else if (typeof column === 'number') {
the_column = this.columns.get(column);
}
return the_column;
};
ControlGrid.prototype.setColumns = function (columns) {
if (this.columnsFactory) {
ControlGrid.superclass.prototype.setColumns.call(this, columns);
}
return this;
};
ControlGrid.prototype.removeColumn = function (column) {
ControlGrid.superclass.prototype.removeColumn.call(this, column);
return this.updateDependencies();
};
ControlGrid.prototype.updateDependencies = function () {
var dependencies = {},
i,
j,
columns = this.columns.asArray(),
dependent,
dependents;
for (i = 0; i < columns.length; i += 1) {
dependents = columns[i].dependentCols;
for (j = 0; j < dependents.length; j += 1) {
if (this.getColumn(dependents[j])) {
if (!dependencies[dependents[j]]) {
dependencies[dependents[j]] = [];
}
dependencies[dependents[j]].push(columns[i]);
}
}
}
this.dependencies = dependencies;
return this;
};
ControlGrid.prototype.addColumn = function (column) {
var newColumn;
if (typeof column === 'object') {
column.grid = this;
newColumn = this.columnsFactory.make(column);
} else {
throw new Error('addColumn(): invalid column to add');
}
if (newColumn) {
this.columns.insert(newColumn);
if (this.dom.thead) {
this.dom.thead.appendChild(newColumn.getHTML());
}
}
this.updateDependencies();
return this;
};
/**
* Disables one or all the columns.
* @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
* disabled. It supports the following data types:
*
* - String, in this case the parameter is used as the column's id.
* - Number, in this case the parameter is used as the column's index.
* - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
* a child of the grid.
* - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
* be disabled.
* @chainable
*/
ControlGrid.prototype.disableColumn = function (column) {
var i,
targetColumn = this.getColumns(column);
for (i = 0; i < targetColumn.length; i += 1) {
targetColumn[i].disable();
}
return this;
};
/**
* [disableColumn description]
* @param {[type]} column [description]
* @return {[type]} [description]
*/
ControlGrid.prototype.enableColumn = function (column) {
var i,
targetColumn = this.getColumns(column);
for (i = 0; i < targetColumn.length; i += 1) {
targetColumn[i].enable();
}
return this;
};
ControlGrid.prototype.isValid = function () {
var i,
rows,
valid = true;
rows = this.getItems();
for (i = 0; i < rows.length; i += 1) {
valid = valid && rows[i].isValid();
if (!valid) {
return valid;
}
}
return valid;
};
ControlGrid.prototype.fireDependency = function () {
};
PMUI.extendNamespace('PMUI.grid.ControlGrid', ControlGrid);
}());
(function () {
var GridControlCell = function (settings) {
this.controls = [];
GridControlCell.superclass.call(this, settings);
/**
* @property {Boolean} [validAtChange=true]
* If the validation must be executed every time the field's value changes.
*/
this.validAtChange = null;
this.value = null;
this.onChange = null;
this.validAtChange = null;
this.disable = null;
GridControlCell.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridPanelCell', GridControlCell);
GridControlCell.prototype.init = function (settings) {
var defaults = {
onChange: null,
validAtChange: true,
disabled: false,
value: ""
};
jQuery.extend(true, defaults, settings);
this.initControls();
this.setOnChangeHandler(defaults.onChange)
.setValidAtChange(defaults.validAtChange)
.setValue(defaults.value);
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
};
/**
* [setValueToControls description]
* @param {[type]} value [description]
*/
GridControlCell.prototype.setValueToControls = function (value) {
var i;
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].setValue(value);
}
return this;
};
/**
* [setValue description]
* @param {[type]} value [description]
*/
GridControlCell.prototype.setValue = function (value) {
if (typeof value === 'number') {
value = value.toString();
}
if (typeof value === 'string') {
this.value = value;
} else {
throw new Error("The setValue() method only accepts string values!");
}
this.setValueToControls(value);
return this;
};
/**
* Turns on/off the validation when the field's value changes.
* @param {Boolean} validAtChange
* @chainable
*/
GridControlCell.prototype.setValidAtChange = function (validAtChange) {
this.validAtChange = !!validAtChange;
return this;
};
/**
* Sets the callback function to be called when the field's value changes.
* @param {Function} handler
* @chainable
*/
GridControlCell.prototype.setOnChangeHandler = function (handler) {
if (typeof handler === 'function' || handler === null) {
this.onChange = handler;
}
return this;
};
GridControlCell.prototype.initControls = function () {
return this;
};
GridControlCell.prototype.setValuesToControls = function (values) {
return this;
};
GridControlCell.prototype.setContent = function (content) {
return this.setValuesToControls(content);
};
/**
* Update the field's value property from the controls
* @protected
* @chainable
*/
GridControlCell.prototype.updateContentFromControls = function () {
var value = '',
i;
for (i = 0; i < this.controls.length; i += 1) {
value += ' ' + this.controls[i].getValue();
}
this.value = this.content = value.substr(1);
return this;
};
/**
* The onChange event handler constructor.
* @private
* @return {Function} The handler.
*/
GridControlCell.prototype.onChangeHandler = function () {
var that = this;
return function () {
var previousValue = that.content;
that.updateContentFromControls();
if (that.validAtChange) {
that.isValid();
}
if (typeof that.onChange === 'function') {
that.onChange(that.getContent(), previousValue);
}
if (that.column) {
that.column.onChangeHandler(that, that.content, previousValue);
}
};
};
GridControlCell.prototype.hideMessage = function () {
this.style.removeClasses(['pmui-error']);
this.html.removeAttribute("title");
try {
jQuery(this.html).tooltip("destroy");
} catch (e) {
}
return this;
};
GridControlCell.prototype.showMessage = function (message) {
this.style.addClasses(['pmui-error']);
this.html.setAttribute("title", "error");
jQuery(this.html).tooltip({
content: message,
tooltipClass: 'pmui-cell-tooltip-error',
position: {
my: "left",
at: "left bottom"
}
});
jQuery(this.html).tooltip('open');
return this;
};
/**
* [evalRequired description]
* @return {[type]} [description]
*/
GridControlCell.prototype.evalRequired = function () {
var valid = true, column = this.column;
if (column) {
if (column.required && this.value === "") {
valid = false;
}
}
if (valid) {
this.hideMessage();
} else {
this.showMessage(column.requiredMessage);
}
return valid;
};
/**
* [isValid description]
* @return {Boolean} [description]
*/
GridControlCell.prototype.isValid = function () {
var valid = true,
validator,
validators;
valid = valid && this.evalRequired();
if (!valid) {
return valid;
}
if (this.column) {
validators = this.column.validators;
for (validator in validators) {
if (validators.hasOwnProperty(validator)) {
validators[validator].setParent(this);
valid = validators[validator].isValid();
if (!valid) {
return valid;
}
}
}
}
return valid;
};
/**
* Attach the event listeners to the HTML element
* @private
* @chainable
*/
GridControlCell.prototype.defineEvents = function () {
var i;
if (this.eventsDefined) {
return this;
}
;
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].setOnChangeHandler(this.onChangeHandler())
.defineEvents();
}
this.eventsDefined = true;
return this;
};
/**
* @inheritdoc
*/
GridControlCell.prototype.enable = function () {
var i;
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].disable(false);
}
this.disabled = false;
return this;
};
/**
* @inheritdoc
*/
GridControlCell.prototype.disable = function () {
var i;
for (i = 0; i < this.controls.length; i += 1) {
this.controls[i].disable(true);
}
this.disabled = true;
return this;
};
/**
* [createHTML description]
* @return {[type]} [description]
*/
GridControlCell.prototype.createHTML = function () {
var i;
if (this.html) {
return this.html;
}
GridControlCell.superclass.prototype.createHTML.call(this);
for (i = 0; i < this.controls.length; i += 1) {
this.contentSpan.appendChild(this.controls[i].getHTML());
}
this.defineEvents();
return this.html;
};
PMUI.extendNamespace('PMUI.grid.GridControlCell', GridControlCell);
}());
(function () {
var NumberCell = function (settings) {
NumberCell.superclass.call(this, settings);
NumberCell.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlCell', NumberCell);
NumberCell.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 524288,
mask: ["###,###.###"],
onKeyUp: null,
content: ''
};
jQuery.extend(true, defaults, settings);
this.setPlaceholder(defaults.placeholder)
.setMaxLength(defaults.maxLength)
.setOnKeyUpHandler(defaults.onKeyUp)
.setContent(defaults.content)
.setMask(defaults.mask);
};
NumberCell.prototype.setOnKeyUpHandler = function (handler) {
this.controls[0].setOnKeyUpHandler(handler);
return this;
};
NumberCell.prototype.setPlaceholder = function (placeholder) {
this.controls[0].setPlaceholder(placeholder);
return this;
};
NumberCell.prototype.setMask = function (mask) {
this.controls[0].setMask(mask);
return this;
};
NumberCell.prototype.setMaxLength = function (maxLength) {
this.controls[0].setMaxLength(maxLength);
return this;
};
NumberCell.prototype.setReadOnly = function (value) {
this.controls[0].setReadOnly(value);
return this;
};
NumberCell.prototype.initControls = function () {
this.controls[0] = new PMUI.control.NumberControl();
return this;
};
NumberCell.prototype.setContent = function (content) {
return this.setValuesToControls(content);
};
NumberCell.prototype.setValuesToControls = function (values) {
if (this.controls.length) {
this.controls[0].setValue(values);
this.content = this.controls[0].getValue();
}
return this;
};
NumberCell.prototype.getPlaceholder = function () {
return this.controls[0].getPlaceholder();
};
/**
* [isReadOnly description]
* @return {Boolean} [description]
*/
NumberCell.prototype.isReadOnly = function () {
return this.controls[0].isReadOnly();
};
/**
* Switches on/off the value trimming for the field's value when it loses focus.
* @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
* @chainable
*/
PMUI.extendNamespace('PMUI.grid.NumberCell', NumberCell);
if (typeof exports !== 'undefined') {
module.exports = NumberCell;
}
}());
(function () {
var GridTextControlCell = function (settings) {
GridTextControlCell.superclass.call(this, settings);
GridTextControlCell.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlCell', GridTextControlCell);
GridTextControlCell.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 0,
trimOnBlur: true,
readOnly: false
};
jQuery.extend(true, defaults, settings);
this.setPlaceholder(defaults.placeholder)
.setMaxLength(defaults.maxLength)
.setTrimOnBlur(defaults.trimOnBlur)
.setReadOnly(defaults.readOnly);
};
GridTextControlCell.prototype.initControls = function () {
this.controls[0] = new PMUI.control.TextControl();
return this;
};
GridTextControlCell.prototype.setValuesToControls = function (values) {
return this;
};
GridTextControlCell.prototype.setContent = function (content) {
return this.setValuesToControls(content);
};
/**
* Sets the placeholder for the control. Note that this feature is only supported
* by browsers which support the "placeholder" for input elements.
* @param {String} placeholder
* @chainable
*/
GridTextControlCell.prototype.setReadOnly = function (readonly) {
this.controls[0].setReadOnly(readonly);
return this;
};
/**
* [setPlaceholder description]
* @param {[type]} placeholder [description]
*/
GridTextControlCell.prototype.setPlaceholder = function (placeholder) {
this.controls[0].setPlaceholder(placeholder);
return this;
};
/**
* [getPlaceholder description]
* @return {[type]} [description]
*/
GridTextControlCell.prototype.getPlaceholder = function () {
return this.controls[0].getPlaceholder();
};
/**
* [setMaxLength description]
* @param {[type]} maxLength [description]
*/
GridTextControlCell.prototype.setMaxLength = function (maxLength) {
this.controls[0].setMaxLength(maxLength);
return this;
};
/**
* [getMaxLength description]
* @return {[type]} [description]
*/
GridTextControlCell.prototype.getMaxLength = function () {
return this.controls[0].getMaxLength();
};
/**
* [isReadOnly description]
* @return {Boolean} [description]
*/
GridTextControlCell.prototype.isReadOnly = function () {
return this.controls[0].isReadOnly();
};
/**
* Switches on/off the value trimming for the field's value when it loses focus.
* @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
* @chainable
*/
GridTextControlCell.prototype.setTrimOnBlur = function (trimOnBlur) {
this.trimOnBlur = !!trimOnBlur;
return this;
};
PMUI.extendNamespace('PMUI.grid.GridTextControlCell', GridTextControlCell);
if (typeof exports !== 'undefined') {
module.exports = GridTextControlCell;
}
}());
(function () {
var DropDownListControlCell = function (settings) {
DropDownListControlCell.superclass.call(this, settings);
DropDownListControlCell.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlCell', DropDownListControlCell);
DropDownListControlCell.prototype.init = function (settings) {
var defaults = {
options: [],
value: null
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options);
if (defaults.value !== null) {
this.setValue(defaults.value);
}
};
DropDownListControlCell.prototype.initControls = function () {
this.controls[0] = new PMUI.control.DropDownListControl();
return this;
};
DropDownListControlCell.prototype.clearOptions = function () {
this.controls[0].clearOptions();
this.content = this.controls[0].getValue();
return this;
};
DropDownListControlCell.prototype.enableDisableOption = function (disabled, option, group) {
this.controls[0].enableDisableOption(disabled, option, group);
return this;
};
DropDownListControlCell.prototype.disableOption = function (option, group) {
return this.controls[0].enableDisableOption(true, option, group);
};
DropDownListControlCell.prototype.enableOption = function (option, group) {
return this.controls[0].enableDisableOption(false, option, group);
};
DropDownListControlCell.prototype.removeOption = function (option, group) {
this.controls[0].removeOption(option, group);
return this;
};
DropDownListControlCell.prototype.addOptionGroup = function (optionGroup) {
this.controls[0].addOptionGroup(optiongroup);
return this;
};
DropDownListControlCell.prototype.addOption = function (option, group) {
this.controls[0].addOption(option, group);
return this;
};
DropDownListControlCell.prototype.getSelectedLabel = function () {
return this.controls[0].getSelectedLabel();
};
DropDownListControlCell.prototype.valueExistsInOptions = function (value) {
return this.controls[0].valueExistsInOptions(value);
};
DropDownListControlCell.prototype.getFirstAvailableOption = function () {
return this.controls[0].getFirstAvailableOption();
}
DropDownListControlCell.prototype.setOptions = function (options) {
this.controls[0].setOptions(options);
return this;
};
DropDownListControlCell.prototype.getOptions = function (includeGroups) {
return this.controls[0].getOptions(includeGroups);
};
DropDownListControlCell.prototype.setValuesToControls = function (values) {
if (this.controls.length) {
this.controls[0].setValue(values);
this.content = this.controls[0].getValue();
}
return this;
};
DropDownListControlCell.prototype.getContent = function () {
return this.controls[0].getValue();
};
PMUI.extendNamespace('PMUI.grid.DropDownListControlCell', DropDownListControlCell);
}());
(function () {
var CheckBoxGroupControlCell = function (settings) {
CheckBoxGroupControlCell.superclass.call(this, settings);
/**
* @property {String} controlPositioning The direction for the options to be added in the field.
* @readonly
*/
this.controlPositioning = null;
/**
* @property {Number} maxDirectionOptions The max number of options that can be in the current options
* direction.
* @readonly
*/
this.maxDirectionOptions = null;
/**
* The status if the controls to be saved when the field is switch between enabled/disabled.
* @type {Object}
* @private
*/
this.auxControlStates = {};
CheckBoxGroupControlCell.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlCell', CheckBoxGroupControlCell);
CheckBoxGroupControlCell.prototype.init = function (settings) {
var defaults = {
options: [],
controlPositioning: "vertical",
maxDirectionOptions: 1,
value: '[]'
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options);
this.setMaxDirectionOptions(defaults.maxDirectionOptions);
this.setControlPositioning(defaults.controlPositioning);
};
CheckBoxGroupControlCell.prototype.enable = function () {
var key,
i,
controlsLength = this.controls.length,
controls = this.controls;
CheckBoxGroupControlCell.superclass.prototype.enable.call(this);
this.disabled = false;
for (i = 0; i < controlsLength; i += 1) {
controls[i].disable(this.auxControlStates[controls[i].id]);
}
return this;
};
CheckBoxGroupControlCell.prototype.setMaxDirectionOptions = function (max) {
if (typeof max === 'number') {
this.maxDirectionOptions = Math.floor(max);
if (this.html) {
this.setControlPositioning(this.controlPositioning);
}
} else {
throw new Error("setMaxDirectionOptions(): it only accepts number values.");
}
return this;
};
CheckBoxGroupControlCell.prototype.setControlPositioning = function (positioning) {
var errorMessage = "The setControlPositioning() method only accepts \"horizontal\" or \"vertical\" as value.",
table,
tbody,
cell,
row,
i,
column,
rowIndex;
if (typeof positioning === 'string') {
if (!(positioning === 'horizontal' || positioning === 'vertical')) {
return this;
}
this.controlPositioning = positioning;
if (this.html && this.controls) {
for (i = 0; i < this.controls.length; i += 1) {
jQuery(this.controls[i].getHTML()).detach();
}
$(this.contentSpan).empty();
table = PMUI.createHTMLElement("table");
table.className = 'pmui-field-control-table';
tbody = PMUI.createHTMLElement("tbody");
if (positioning === 'horizontal') {
row = PMUI.createHTMLElement("tr");
for (i = 0; i < this.controls.length; i += 1) {
cell = PMUI.createHTMLElement('td');
this.controls[i].getHTML();
this.controls[i].control.tabIndex = i;
cell.appendChild(this.controls[i].getHTML());
row.appendChild(cell);
if (this.maxDirectionOptions > 0 && (i + 1) % this.maxDirectionOptions === 0) {
tbody.appendChild(row);
row = PMUI.createHTMLElement("tr");
}
}
cell.style.textAlign = "start";
tbody.appendChild(row);
} else {
column = 0;
for (i = 0; i < this.controls.length; i += 1) {
cell = PMUI.createHTMLElement('td');
this.controls[i].getHTML();
this.controls[i].control.tabIndex = i;
cell.appendChild(this.controls[i].getHTML());
rowIndex = this.maxDirectionOptions === 0 ? i : i % this.maxDirectionOptions;
row = jQuery(tbody).find('tr').eq(rowIndex).get(0);
if (!row) {
row = PMUI.createHTMLElement('tr');
tbody.appendChild(row);
}
cell.style.textAlign = "start";
row.appendChild(cell);
}
}
table.appendChild(tbody);
this.contentSpan.appendChild(table);
}
}
return this;
};
CheckBoxGroupControlCell.prototype.setOptions = function (options) {
var i = 0;
this.content = [];
this.content = JSON.stringify(this.content);
if (jQuery.isArray(options)) {
for (i = 0; i < options.length; i += 1) {
this.addOption(options[i]);
}
} else {
throw new Error("setOptions(): the supplied argument must be an array.");
}
return this;
};
CheckBoxGroupControlCell.prototype.addOption = function (option) {
var newOption;
newOption = new PMUI.control.SelectableControl(jQuery.extend(true, option, {
mode: 'checkbox',
name: this.controls.length + 1,
selected: option.selected
}));
if (newOption.isSelected()) {
this.content = JSON.parse(this.content);
this.content.push(newOption.getValue());
this.content = JSON.stringify(this.content);
}
if (this.eventsDefined) {
newOption.setOnChangeHandler(this.onChangeHandler()).getHTML();
newOption.defineEvents();
}
this.auxControlStates[newOption.id] = newOption.disabled;
this.controls.push(newOption);
this.setControlPositioning(this.controlPositioning);
return this;
};
CheckBoxGroupControlCell.prototype.removeOption = function (item) {
var itemToRemove,
i;
if (item instanceof PMUI.control.SelectableControl) {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i] === item) {
itemToRemove = i;
break;
}
}
} else if (typeof item === 'string') {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].id === item) {
itemToRemove = this.controls[i];
break;
}
}
} else {
itemToRemove = item;
}
if (typeof itemToRemove === 'number') {
itemToRemove = this.controls[itemToRemove];
delete this.auxControlStates[itemToRemove.id];
jQuery(itemToRemove.html).detach();
this.controls.splice(itemToRemove, 1);
this.setControlPositioning(this.controlPositioning);
}
return this;
};
CheckBoxGroupControlCell.prototype.clearOptions = function () {
while (this.controls.length) {
this.removeOption(0);
}
return this;
};
CheckBoxGroupControlCell.prototype.updateContentFromControls = function () {
var value = [],
i;
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].isSelected()) {
value.push(this.controls[i].getValue());
}
}
this.content = JSON.stringify(value);
return this;
};
CheckBoxGroupControlCell.prototype.evalRequired = function () {
var valid = true,
value;
value = JSON.parse(this.getContent());
if (this.required && value.length === 0) {
this.showMessage(this.requiredMessage, "error");
valid = false;
} else {
this.hideMessage();
}
return valid;
};
CheckBoxGroupControlCell.prototype.disableOption = function (value) {
var index = -1,
i;
if (this.html) {
if (typeof value === 'string') {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].label === value) {
index = i;
}
}
} else {
index = value;
}
if (index === -1) {
throw new Error('the value send is not part of group CheckBox');
} else {
this.controls[index].disable(true);
}
}
return this;
};
CheckBoxGroupControlCell.prototype.enableOption = function (value) {
var index = -1,
i;
if (this.html) {
if (typeof value === 'string') {
for (i = 0; i < this.controls.length; i += 1) {
if (this.controls[i].label === value) {
index = i;
}
}
} else {
index = value;
}
if (index === -1) {
throw new Error('the value send is not part from group CheckBox');
} else {
this.controls[index].disable(false);
}
}
return this;
};
CheckBoxGroupControlCell.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
CheckBoxGroupControlCell.superclass.prototype.createHTML.call(this)
this.setControlPositioning(this.controlPositioning);
return this.html;
};
CheckBoxGroupControlCell.prototype.setValuesToControls = function (value) {
var i,
j,
values,
controls,
controlsLength;
if (value) {
try {
values = JSON.parse(value);
} catch (e) {
values = '[]';
}
controlsLength = (controls = this.controls.slice(0)).length;
for (i = 0; i < controlsLength; i += 1) {
controls[i].deselect();
}
for (i = 0; i < values.length; i += 1) {
for (j = 0; j < controlsLength; j += 1) {
if (controls[j].getValue() === values[i]) {
controls[j].select();
controls.splice(j, 1);
controlsLength -= 1;
j -= 1;
}
}
}
this.updateContentFromControls();
}
return this;
};
PMUI.extendNamespace('PMUI.grid.CheckBoxGroupControlCell', CheckBoxGroupControlCell);
}());
(function () {
var GridControlColumn = function (settings) {
this.cells = new PMUI.util.ArrayList();
GridControlColumn.superclass.call(this, jQuery.extend(settings, {elementTag: "th"}));
/**
* The name for the column.
* @type {String}
*/
this.name = null;
/**
* The text to be shown in the column header.
* @type {String}
*/
this.title = null;
/**
* The grid the column belongs to.
* @type {PMUI.grid.GridPanel}
*/
this.grid = null;
/**
* The HTML element in which the cell content will be appended.
* @type {HTMLElement}
* @private
*/
this.contentTitle = null;
/**
* [dependentCols description]
* @type {Array}
*/
this.dependentCols = [];
/**
* [onChange description]
* @type {[type]}
*/
this.onChange = null;
/**
* [dependencyHandler description]
* @type {[type]}
*/
this.dependencyHandler = null;
/**
* [disabled description]
* @type {[type]}
*/
this.disabled = null;
/**
* [validatorFactory description]
* @type {[type]}
*/
this.validatorFactory = null;
/**
* [validators description]
* @type {Object}
*/
this.validators = {};
/**
* [required description]
* @type {[type]}
*/
this.required = null;
/**
* [requiredMessage description]
* @type {[type]}
*/
this.requiredMessage = null;
GridControlColumn.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', GridControlColumn);
GridControlColumn.prototype.CELL = null;
GridControlColumn.prototype.init = function (settings) {
var defaults = {
name: this.id,
title: '[untitled]',
grid: null,
cells: [],
dependentCols: [],
onChange: null,
dependencyHandler: null,
disabled: false,
validators: [],
required: false,
requiredMessage: 'This cell is required.',
validatorFactory: new PMUI.form.ValidatorFactory()
};
jQuery.extend(true, defaults, settings);
this.setName(defaults.name)
.setTitle(defaults.title)
.setGrid(defaults.grid)
.setRequired(defaults.required)
.setDependentCols(defaults.dependentCols)
.setOnChangeHandler(defaults.onChange)
.setDependencyHandler(defaults.dependencyHandler)
.setValidatorFactory(defaults.validatorFactory)
.setValidators(defaults.validators)
.setRequiredMessage(defaults.requiredMessage);
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
};
GridControlColumn.prototype.setRequiredMessage = function (requiredMessage) {
if (typeof requiredMessage !== 'string') {
throw new Error('setRequiredMessage(): The parameter must be a strng.');
}
this.requiredMessage = requiredMessage;
return this;
};
GridControlColumn.prototype.setRequired = function (required) {
this.required = !!required;
return this;
};
GridControlColumn.prototype.setValidatorFactory = function (factory) {
if (factory instanceof PMUI.util.Factory) {
this.validatorFactory = factory;
} else {
this.validatorFactory = new PMUI.form.ValidatorFactory(factory);
}
return this;
};
GridControlColumn.prototype.addValidator = function (validator) {
var newValidator;
if (this.validatorFactory) {
if (this.validatorFactory.isValidClass(validator) || this.validatorFactory.isValidName(validator.pmType)) {
newValidator = this.validatorFactory.make(validator);
} else {
throw new Error('Invalid validator to add.');
}
}
if (newValidator && newValidator instanceof PMUI.form.Validator) {
this.validators[newValidator.type] = newValidator;
}
return this;
};
GridControlColumn.prototype.clearValidators = function () {
var key;
for (key in this.validators) {
if (this.validators.hasOwnProperty(key)) {
this.validators[key] = null;
delete this.validators[key];
}
}
return this;
};
GridControlColumn.prototype.setValidators = function (validators) {
var i = 0;
if (jQuery.isArray(validators)) {
this.clearValidators();
for (i = 0; i < validators.length; i += 1) {
this.addValidator(validators[i]);
}
}
return this;
};
GridControlColumn.prototype.getCells = function () {
return this.cells.asArray().slice(0);
};
GridControlColumn.prototype.setName = function (name) {
if (typeof name !== 'string') {
throw new Error("setName(): The parameter must be a string.");
}
this.name = name;
return this;
};
GridControlColumn.prototype.setDependencyHandler = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error("setDependencyHandler(): The parameter must be a function or null.");
}
this.dependencyHandler = handler;
return this;
};
GridControlColumn.prototype.onChangeHandler = function (cell, newValue, oldValue) {
var i,
dependentCols = this.dependentCols,
grid = this.grid,
dependentCell,
col,
row;
cell.row.updateData(cell);
if (typeof this.onChange === 'function') {
this.onChange(cell, newValue, oldValue);
}
if (grid) {
for (i = 0; i < dependentCols.length; i += 1) {
col = grid.getColumn(dependentCols[i]);
if (!col) {
this.removeDependentCol(dependentCols[i]);
continue;
}
dependentCell = cell.row.getCell(col);
col.fireDependencyHandler(dependentCell);
}
}
return this;
};
GridControlColumn.prototype.fireDependencyHandler = function (cell) {
var grid = this.grid,
dependsOf,
obj = {},
i,
targetRow,
targetCells,
j;
if (grid) {
targetCells = cell ? [cell] : this.cells.asArray();
for (j = 0; j < targetCells.length; j += 1) {
cell = targetCells[j];
targetRow = cell.row;
dependsOf = grid.dependencies[this.name];
for (i = 0; i < dependsOf.length; i += 1) {
obj[dependsOf[i].name] = targetRow.getCell(dependsOf[i]);
}
if (typeof this.dependencyHandler === 'function') {
this.dependencyHandler.call(cell, cell, obj);
}
}
}
return this;
};
GridControlColumn.prototype.enable = function () {
var i,
cells = this.getCells();
for (i = 0; i < cells.length; i += 1) {
cells[i].enable();
}
this.disabled = false;
return this;
};
GridControlColumn.prototype.disable = function () {
var i,
cells = this.getCells();
for (i = 0; i < cells.length; i += 1) {
cells[i].disable();
}
this.disabled = true;
return this;
};
GridControlColumn.prototype.setOnChangeHandler = function (handler) {
if (!(typeof handler === 'function' || handler === null)) {
throw new Error("setOnChangeHandler(): the parameter must be a function or null.");
}
this.onChange = handler;
return this;
};
GridControlColumn.prototype.setDependentCols = function (dependentCols) {
if (!jQuery.isArray(dependentCols)) {
throw new Error("setDependentCols(): The parameter must be an array.");
}
this.dependentCols = dependentCols;
if (this.grid) {
this.grid.updateDependencies();
}
return this;
};
GridControlColumn.prototype.removeDependentCol = function (columnName) {
var i;
for (i = 0; this.dependentCols.length; i += 1) {
if (this.dependentCols[i] === columnName) {
this.dependentCols.splice(i, 1);
i--;
}
}
return this;
};
GridControlColumn.prototype.removeCell = function (cell) {
var cellToRemove;
if (cell instanceof PMUI.grid.GridControlCell) {
cellToRemove = cell;
} else if (typeof cell === 'number') {
cellToRemove = this.cells.get(cell);
} else if (typeof cell === 'string') {
cellToRemove = this.cells.find("id", cell);
}
if (cellToRemove) {
cellToRemove.column = null;
this.cells.remove(cellToRemove);
}
return this;
};
GridControlColumn.prototype.clearCells = function () {
this.cells.clear();
while (this.cells.getSize() > 0) {
this.removeCell(0);
}
return this;
};
GridControlColumn.prototype.addCell = function (cell) {
if (!(cell instanceof PMUI.grid.GridControlCell)) {
throw new Error("addCell(): The parameter must be an instance of PMUI.grid.GridControlCell.");
}
this.cells.insert(cell);
return this;
};
GridControlColumn.prototype.setTitle = function (title) {
this.title = title;
if (this.html) {
jQuery(this.contentTitle).empty();
this.contentTitle.appendChild(document.createTextNode(title));
}
return this;
};
GridControlColumn.prototype.setGrid = function (grid) {
if (!(grid instanceof PMUI.grid.ControlGrid)) {
throw new Error('setGrid(): the grid property should be instance of [PMUI.grid.GridControl]');
}
this.grid = grid;
this.grid.updateDependencies();
return this;
};
GridControlColumn.prototype.getCellDefaultCfg = function () {
return {
disabled: this.disabled
};
};
GridControlColumn.prototype.createCell = function () {
var cell;
if (!this.CELL instanceof PMUI.grid.GridControlCell) {
throw new Error("The cell associated to the " + this.type + " is not valid!");
}
cell = new this.CELL(this.getCellDefaultCfg());
this.addCell(cell);
return cell;
};
GridControlColumn.prototype.createHTML = function () {
var contentTitle,
sortIcon;
if (this.html) {
return this.html;
}
GridControlColumn.superclass.prototype.createHTML.call(this);
contentTitle = PMUI.createHTMLElement('span');
contentTitle.className = 'pmui-gridpanelColumn-title';
this.contentTitle = contentTitle;
this.html.appendChild(contentTitle);
this.setTitle(this.title);
return this.html;
};
PMUI.extendNamespace('PMUI.grid.GridControlColumn', GridControlColumn);
}());
(function () {
var NumberColumn = function (settings) {
NumberColumn.superclass.call(this, settings);
this.placeholder = null;
this.maxLength = null;
this.mask = null;
NumberColumn.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlColumn', NumberColumn);
NumberColumn.prototype.CELL = PMUI.grid.NumberCell;
NumberColumn.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 524288,
mask: "##############.###############",
content: '',
readOnly: false
};
jQuery.extend(true, defaults, settings);
this.setMaxLength(defaults.maxLength)
.setPlaceHolder(defaults.placeholder)
.setReadOnly(defaults.readOnly)
.setMask(defaults.mask);
};
NumberColumn.prototype.setMaxLength = function (input) {
this.maxLength = input
return this;
};
NumberColumn.prototype.setPlaceHolder = function (input) {
this.placeholder = input;
return this;
};
NumberColumn.prototype.setReadOnly = function (input) {
this.readOnly = input;
return this;
};
NumberColumn.prototype.setMask = function (mask) {
this.mask = mask;
return this;
};
NumberColumn.prototype.getCellDefaultCfg = function () {
return jQuery.extend(true, NumberColumn.superclass.prototype.getCellDefaultCfg.call(this), {
maxLength: this.maxLength,
mask: this.mask,
placeholder: this.placeholder,
readOnly: this.readOnly
});
};
PMUI.extendNamespace('PMUI.grid.NumberColumn', NumberColumn);
}());
(function () {
var TextControlColumn = function (settings) {
TextControlColumn.superclass.call(this, settings);
this.maxLength = null;
this.placeholder = null;
this.trimOnBlur = null;
this.readOnly = null;
TextControlColumn.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlColumn', TextControlColumn);
TextControlColumn.prototype.CELL = PMUI.grid.GridTextControlCell;
TextControlColumn.prototype.init = function (settings) {
var defaults = {
maxLength: null,
placeholder: null,
trimOnBlur: false,
readOnly: false
};
jQuery.extend(true, defaults, settings);
this.setMaxLength(defaults.maxLength)
.setPlaceHolder(defaults.placeholder)
.setTrimOnBlur(defaults.trimOnBlur)
.setReadOnly(defaults.readOnly);
};
TextControlColumn.prototype.setMaxLength = function (input) {
var i,
cells,
cellsLength;
if (typeof input !== 'number') {
throw new Error("setMaxLength(): the parameter must be a number.");
}
this.maxLength = input;
cells = this.cells.asArray();
cellsLength = cells.length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setMaxLength(this.maxLength);
}
return this;
};
TextControlColumn.prototype.setPlaceHolder = function (input) {
var i,
cells,
cellsLength;
if (typeof input !== 'string') {
throw new Error("setPlaceHolder(): the parameter must be a string.");
}
this.placeholder = input;
cells = this.cells.asArray();
cellsLength = cells.length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setPlaceHolder(this.placeholder);
}
return this;
};
TextControlColumn.prototype.setTrimOnBlur = function (input) {
var i,
cells,
cellsLength;
this.trimOnBlur = !!input;
cells = this.cells.asArray();
cellsLength = cells.length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setTrimOnBlur(this.trimOnBlur);
}
return this;
};
TextControlColumn.prototype.setReadOnly = function (input) {
var i,
cells,
cellsLength;
this.readOnly = !!input;
cells = this.cells.asArray();
cellsLength = cells.length;
for (i = 0; i < cellsLength; i += 1) {
cells[i].setReadOnly(this.readOnly);
}
return this;
};
TextControlColumn.prototype.getCellDefaultCfg = function () {
return jQuery.extend(true, TextControlColumn.superclass.prototype.getCellDefaultCfg.call(this), {
maxLength: this.maxLength,
trimOnBlur: this.trimOnBlur,
placeholder: this.placeholder,
readOnly: this.readOnly
});
};
PMUI.extendNamespace('PMUI.grid.TextControlColumn', TextControlColumn);
}());
(function () {
var DropDownListControlColumn = function (settings) {
DropDownListControlColumn.superclass.call(this, settings);
this.options = [];
DropDownListControlColumn.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlColumn', DropDownListControlColumn);
DropDownListControlColumn.prototype.CELL = PMUI.grid.DropDownListControlCell;
DropDownListControlColumn.prototype.init = function (settings) {
var defaults = {
options: []
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options);
};
DropDownListControlColumn.prototype.setOptions = function (options) {
var i,
cells;
if (!jQuery.isArray(options)) {
throw new Error("setOptions(): the parameter must be an array.");
}
this.options = options;
cells = this.cells.asArray();
for (i = 0; i < cells.length; i += 1) {
cells[i].setOptions(this.options);
}
return this;
};
DropDownListControlColumn.prototype.getCellDefaultCfg = function () {
return jQuery.extend(true, DropDownListControlColumn.superclass.prototype.getCellDefaultCfg.call(this), {
options: this.options
});
};
PMUI.extendNamespace('PMUI.grid.DropDownListControlColumn', DropDownListControlColumn);
}());
(function () {
var CheckBoxGroupColumn = function (settings) {
CheckBoxGroupColumn.superclass.call(this, settings);
this.controlPositioning = null;
this.maxDirectionOptions = null;
this.options = [];
CheckBoxGroupColumn.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.grid.GridControlColumn', CheckBoxGroupColumn);
CheckBoxGroupColumn.prototype.CELL = PMUI.grid.CheckBoxGroupControlCell;
CheckBoxGroupColumn.prototype.init = function (settings) {
var defaults = {
options: [],
controlPositioning: "vertical",
maxDirectionOptions: 1
};
jQuery.extend(true, defaults, settings);
this.setOptions(defaults.options);
this.setMaxDirectionOptions(defaults.maxDirectionOptions);
this.setControlPositioning(defaults.controlPositioning);
};
CheckBoxGroupColumn.prototype.setOptions = function (options) {
var i,
cells;
if (!jQuery.isArray(options)) {
throw new Error("setOptions(): the parameter must be an array.");
}
this.options = options;
cells = this.cells.asArray();
for (i = 0; i < cells.length; i += 1) {
cells[i].setOptions(this.options);
}
return this;
};
CheckBoxGroupColumn.prototype.setControlPositioning = function (positioning) {
this.controlPositioning = positioning;
return this;
};
CheckBoxGroupColumn.prototype.setMaxDirectionOptions = function (maxDirectionOptions) {
this.maxDirectionOptions = maxDirectionOptions;
return this;
};
CheckBoxGroupColumn.prototype.getCellDefaultCfg = function () {
return jQuery.extend(true, CheckBoxGroupColumn.superclass.prototype.getCellDefaultCfg.call(this), {
controlPositioning: this.controlPositioning,
maxDirectionOptions: this.maxDirectionOptions,
options: this.options
});
};
PMUI.extendNamespace('PMUI.grid.CheckBoxGroupColumn', CheckBoxGroupColumn);
}());
(function () {
var GridControlRow = function (settings) {
GridControlRow.superclass.call(this, settings);
this.removeButton = null;
};
PMUI.inheritFrom('PMUI.grid.GridPanelRow', GridControlRow);
GridControlRow.prototype.getCell = function (column) {
var cell = null;
if (column instanceof PMUI.grid.GridControlColumn) {
cell = this.cells.find("column", column);
} else if (typeof column === 'number') {
cell = this.cells.get(column);
}
return cell;
};
GridControlRow.prototype.setCells = function () {
var i,
columns,
cellsFactory,
cell;
this.clearCells();
if (this.parent) {
columns = this.parent.columns.asArray();
cellsFactory = this.parent.cellsFactory;
for (i = 0; i < columns.length; i += 1) {
cell = columns[i].createCell();
cell.column = columns[i];
cell.row = this;
this.cells.insert(cell);
if (this.html) {
this.html.appendChild(cell.getHTML());
}
this.data.addAttribute(cell.column.name, cell.getContent());
}
}
return this;
};
GridControlRow.prototype.updateData = function (cell) {
if (this.cells.contains(cell)) {
this.data.addAttribute(cell.column.name, cell.getContent());
}
return this;
};
GridControlRow.prototype.defineEvents = function () {
var that = this;
if (!this.removeButton) {
return this;
}
jQuery(this.html).on('mouseenter', function () {
var cell = that.getCell(that.cells.getSize() - 1);
cell.html.appendChild(that.removeButton);
}).on('mouseleave', function () {
jQuery(that.removeButton).detach();
});
jQuery(this.removeButton).on('click', '.pmui-gridpanelrow-deleteButton-link', function () {
if (that.parent) {
that.parent.removeItem(that);
}
});
return this;
};
GridControlRow.prototype.createHTML = function () {
var deleteButton,
deleteAnchor;
if (this.html) {
return this.html;
}
GridControlRow.superclass.prototype.createHTML.call(this);
deleteButton = PMUI.createHTMLElement('div');
deleteAnchor = PMUI.createHTMLElement('a');
deleteButton.className = 'pmui-gridpanelrow-deleteButton';
deleteAnchor.className = 'pmui-gridpanelrow-deleteButton-link';
deleteAnchor.href = '#';
deleteButton.appendChild(deleteAnchor);
this.removeButton = deleteButton;
this.defineEvents();
return this.html;
};
GridControlRow.prototype.isValid = function () {
var i,
cells = this.cells.asArray(),
valid = true;
for (i = 0; i < cells.length; i += 1) {
valid = valid && cells[i].isValid();
if (!valid) {
return valid;
}
}
return valid;
};
PMUI.extendNamespace('PMUI.grid.GridControlRow', GridControlRow);
}());
(function () {
var ContainableItem = function (settings) {
ContainableItem.superclass.call(this, settings);
/**
* The parent container for the object.
* @type {PMUI.core.Container}
* @internal
*/
this.parent = null;
/**
* The data abstraction for the object.
* @type {PMUI.data.DataField}
*/
this.data = null;
ContainableItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', ContainableItem);
ContainableItem.prototype.init = function (settings) {
var defaults = {
parent: null,
data: new PMUI.data.DataField()
};
$.extend(true, defaults, settings);
this.setParent(defaults.parent)
.setData(defaults.data);
};
ContainableItem.prototype.setData = function (data) {
if (data instanceof PMUI.data.DataField) {
this.data = data;
}
return this;
};
ContainableItem.prototype.getData = function () {
return this.data;
};
ContainableItem.prototype.setParent = function (parent) {
if (parent !== undefined) {
if (parent instanceof PMUI.core.Container || parent === null) {
this.parent = parent;
} else {
throw new Error("setParent(): The parameter must be instance of PMUI.core.Container or null.");
}
}
return this;
};
PMUI.extendNamespace('PMUI.item.ContainableItem', ContainableItem);
if (typeof exports !== 'undefined') {
module.exports == ContainableItem;
}
}());
(function () {
/**
* @class PMUI.item.TabItem
* Creates an TabItem class, this is a basic element
* @extends {PMUI.core.Element}
*
* Usage example:
*
* @example
* var ti,p;
* p = new PMUI.form.FormPanel({
* width: 604,
* height: 130,
* fieldset: true,
* legend: "my fieldset panel",
* items: [
* {
* pmType: "text",
* label: "Name",
* id: "123",
* value: "",
* placeholder: "insert your name",
* name: "name"
* },{
* pmType: "text",
* label: "Last name",
* value: "",
* placeholder: "your lastname here asshole!",
* name: "lastname"
* }, {
* pmType: "panel",
* layout: 'hbox',
* items: [
* {
* pmType: "text",
* label: "E-mail",
* value: "",
* name: "email"
* },{
* pmType: "text",
* label: "Phone",
* value: "555",
* name: "phone"
* }
* ]
* }
* ],
* layout: "vbox"
* });
* ti = new PMUI.item.TabItem({
* title : 'titulo1',
* panel : p,
* icon : 'pmui-gridpanel-engranaje'
* });
* jQuery('body').append(ti.getHTML());
* ti.defineEvents();
*
* create a new instance of the TabItem, according to the example we set the following properties
* @cfg {String} [title='[untitled]'], Title The title for the Window
* @cfg {PMUI.core.Panel|Object} panel, this property is to set TabItems that are sent from the JSON
* @cfg {String} [icon = ""], this property serves to define one TabItem icon with css with putting a className for this icon
* parameter to family {panel}
* @cfg {Function} [onSelect= function() {}], this property is a function handler to from execute the
* function when is callback define events
*/
var TabItem = function (settings) {
TabItem.superclass.call(this, settings);
/**
* Defines the Title of the TabItem
* @type {String}
*/
this.title = null;
/**
* Element defines the panel that concentrates tab
* @type {PMUI.core.Panel}
*/
this.panel = null;
/**
* Defines the property is a function handler to from execute the
* function when is callback define events
* @type {function}
*/
this.onSelect = null;
/**
* The property define the TabItem is selected for triggering the
* @type {boolean}
*/
this.selected = null;
/**
* The property define the TabItem is selected for triggering
* @type {boolean}
*/
this.onClose = null;
/**
* property is defined when a TabItem is selected or event triggers a reference
* @type {HTMLElement}
*/
this.link = null;
/**
* Defines the fatory for childrend [PMUI.core.Panel]
* @type {PMUI.util.factory}
*/
this.factory = null;
/**
* property defined Tag for icon
* @type {HTMLElement}
*/
this.classNameIcon = null;
/**
* property defined Tag for spanTitle
* @type {HTMLElement}
*/
this.spanTitle = null;
/**
* TabItem title can be hidden or viewed by the user if you only want an icon mostart
* @type {Boolean}
*/
this.titleVisible = null;
/**
* [onClick description]
* @type {[type]}
* @private
*/
this.onClick = null;
this.icon = null;
TabItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Element', TabItem);
TabItem.prototype.family = 'TabItem';
TabItem.prototype.type = 'TabItem';
TabItem.prototype.init = function (settings) {
var defaults = {
title: '[untitled]',
panel: null,
onSelect: function () {
},
elementTag: 'li',
onClick: null
};
jQuery.extend(true, defaults, settings);
this.factory = new PMUI.util.Factory({
products: {
'treepanel': PMUI.panel.TreePanel,
'panel': PMUI.core.Panel,
'grid': PMUI.grid.GridPanel
},
defaultProduct: 'panel'
});
this.setElementTag(defaults.elementTag)
.setTitle(defaults.title)
.setPanel(defaults.panel)
.setOnSelectCallback(defaults.onSelect)
.setOnClick(defaults.onClick);
};
TabItem.prototype.setOnClick = function (onClick) {
if (typeof onClick !== 'function' && onClick !== null) {
throw new Error('setOnClick(): the parameter is no valid, should be a function');
}
this.onClick = onClick;
return this;
}
/**
* Add an item to the panel.
* @param {PMUI.item.TabItem} item
* It can be a valid JSON object or an object that inherits from {@link PMUI.item.TabItem}.
* @chainable
*/
TabItem.prototype.setOnSelectCallback = function (callback) {
if (typeof callback === 'function') {
this.onSelect = callback;
} else {
throw new Error("setOnSelectCallback(): The parameter is not a function.");
}
return this;
};
/**
* @event
* Define the event listeners for the TabPanel.
* @chainable
*/
TabItem.prototype.defineEvents = function () {
var that = this;
this.removeEvents().eventsDefined = true;
if (this.link) {
this.addEvent('click').listen(this.link, function (e) {
if (typeof that.onSelect === 'function') {
e.preventDefault();
e.stopPropagation();
if (!that.selected) {
that.select();
}
}
if (typeof that.onClick === 'function') {
that.onClick(that);
}
});
that.panel.defineEvents();
}
return this;
};
/**
* Sets the tabItem title
* Set the proportion of the html element
* @param {string} title
* @chainable
*/
TabItem.prototype.setTitle = function (title) {
this.title = title;
if (this.html) {
this.spanTitle.textContent = this.title;
}
return this;
};
/**
* Returns title the tabItem.
* @return {string}
*/
TabItem.prototype.getTitle = function () {
return this.title;
};
/**
* Sets the tabPanel [PMUI.core.panel] and full children except Window
* @chainable
*/
TabItem.prototype.setPanel = function (panel) {
var newPanel, errorMessage = "setPanel(): the object supplied as a parameter is not a valid type.";
if (!panel) {
throw new Error("setPanel(): you must specified a valid panel.");
}
if (panel instanceof PMUI.ui.Window) {
throw new Error(errorMessage);
}
if (this.factory) {
if (this.factory.isValidClass(panel) || this.factory.isValidName(panel.pmType) || (panel instanceof PMUI.grid.GridPanel)) {
newPanel = this.factory.make(panel);
} else {
throw new Error(errorMessage);
}
}
if (newPanel) {
this.panel = newPanel;
}
return this;
};
/**
* Returns panel the tabItem.
* @return {PMUI.core.Panel}
*/
TabItem.prototype.getPanel = function () {
return this.panel;
};
/**
* if the tab item is selected, the selected property changes
* @return {boolean}
*/
TabItem.prototype.isSelected = function () {
return this.selected;
};
/**
* if the tab item is selected, the selected property changes to true and calls
* your function onSelect
* @chainable
*/
TabItem.prototype.select = function () {
this.selected = true;
this.style.addClasses(['pmui-active']);
if (this.html) {
this.onSelect();
}
return this;
};
/**
* if the tab item is selected, the selected property changes to false
* @chainable
*/
TabItem.prototype.deselect = function () {
this.selected = false;
this.style.removeClasses(['pmui-active']);
return this;
};
/**
* @method hideTitle
* hide the title of TabItem
* @chainable
*/
TabItem.prototype.hideTitle = function () {
this.titleVisible = false;
if (this.html) {
this.spanTitle.style.display = "none";
}
return this;
};
/**
* @method hideTitle
* shows the title of the TabItem
* @chainable
*/
TabItem.prototype.showTitle = function () {
this.titleVisible = true;
if (this.html) {
this.spanTitle.style.display = "inline-block";
}
return this;
};
TabItem.prototype.createHTML = function () {
var tab,
ref,
spanTitle,
title,
icon;
if (this.html) {
return this.html;
}
TabItem.superclass.prototype.createHTML.call(this);
ref = PMUI.createHTMLElement('a');
spanTitle = PMUI.createHTMLElement('span');
icon = PMUI.createHTMLElement('i');
icon.className = 'pmui-tab-icon';
ref.className = 'pmui-tab-ref';
ref.href = '#';
spanTitle.className = 'pmui-tab_title';
spanTitle.textContent = this.title;
ref.appendChild(icon);
ref.appendChild(spanTitle);
this.icon = icon;
this.html.appendChild(ref);
this.spanTitle = spanTitle;
this.link = ref;
this.setWidth(this.width);
if (this.eventsDefined) {
this.defineEvents();
}
return this.html;
};
TabItem.prototype.setWidth = function (width) {
TabItem.superclass.prototype.setWidth.call(this, width);
if (this.html) {
if (width == "auto") {
this.link.setAttribute("title", "");
this.spanTitle.style.width = "auto";
} else {
this.link.setAttribute("title", this.title);
if (this.width >= 65) {
this.spanTitle.style.width = this.width - 46 + 'px';
} else {
this.spanTitle.style.width = '0px';
}
}
}
return this;
};
PMUI.extendNamespace('PMUI.item.TabItem', TabItem);
if (typeof exports !== "undefined") {
module.exports = TabItem;
}
}());
(function () {
/**
* @class PMUI.item.AccordionItem
* @extends PMUI.core.Item
*
* Class to handle Accordion items, this is the basic element for make an {@link PMUI.panel.AccordionPanel Accordion}
*
* @constructor
* For creates a new component the JSON config can have the following sentences
* @param {Object} settings The configuration options contain:
*
* {
* title: "The title",
* body: panel // This item can be any element that inherit from {@link PMUI.core.Panel Panel}
* iconClass: "the-class",
* style: {
* cssProperties: {
* "background-color": "#f2eaea"
* }
* }
* } ...
* @cfg {String} [title=""] Shows the title for the container
* @cfg {Object} [body=null] Defines the element that will be inside the element
* @cfg {String} [iconClass='pmui-accordion-item-icon'] Defines the class name for the header of the item
*
*/
AccordionItem = function (settings) {
AccordionItem.superclass.call(this, settings);
/**
* @property {String} [iconClass='pmui-accordion-item-icon']
* Defines the class name for the header of the item
*/
this.iconClass = null;
/**
* @property {String} [headerClass='pmui-accordion-item-header']
* Defines the class name for the header
* @private
*/
this.headerClass = null,
/**
* @property {String} [bodyClass='pmui-accordion-item-body']
* Defines the class name for the body
* @private
*/
this.bodyClass = null,
/**
* @property {String} [footerClass='pmui-accordion-item-footer']
* Defines the class name for the footer
* @private
*/
this.footerClass = null;
/**
* @property {String} [containerClass='pmui-accordion-item-container']
* Defines the class name for the global container
* @private
*/
this.containerClass = null;
/**
* @property {String} [iconExpanded='pmui-accordion-item-closed']
* Defines the class name that represents the icon expanded for the AccordionItem
* @private
*/
this.iconExpanded = null;
/**
* @property {String} [iconClosed='pmui-accordion-item-expanded']
* Defines the class name that represents the icon closed for the AccordionItem
* @private
*/
this.iconClosed = null;
/**
* @property {Boolean} [collapsed=true]
* Defines whether the object is collapsed or not
* @private
*/
this.collapsed = null;
/**
* @property {Object} [container=null]
* container Defines the container object for the class xxxxxxx
* @private
*/
this.container = null;
/**
* @property {Object} [header=null]
* Defines the header object for the class
* @private
*/
this.header = null;
/**
* @property {Object} [body={}]
* Defines the body object for the item
* @private
*/
this.body = {};
/**
* @property {Object} [footer=null]
* Defines the footer object for the class
* @private
*/
this.footer = null;
/**
* @property {Boolean} [selected=false]
* Defines whether the item is selected by default when the component is ready to render
* on the browser
*/
this.selected = null;
/**
* @property {Boolean} [propName]
* Represents a flag for control the rendered of the item.
* If the property is True the item was rendered the otherwise if it
* False the item still was not rendered
* @private
*/
this.hiddenBody = null;
/**
* @property {Object} [iconNode=null]
* Defines the iconNode object for the class
* @private
*/
this.iconNode = null;
AccordionItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Item', AccordionItem);
AccordionItem.prototype.type = 'Accordion';
AccordionItem.prototype.family = 'Item';
AccordionItem.prototype.init = function (settings) {
var defaults = {
title: '',
body: '',
collapsed: true,
iconClass: 'pmui-accordion-item-icon',
headerClass: 'pmui-accordion-item-header',
bodyClass: 'pmui-accordion-item-body',
footerClass: 'pmui-accordion-item-footer',
containerClass: 'pmui-accordion-item-container',
iconClosed: "pmui-accordion-item-closed",
iconExpanded: "pmui-accordion-item-expanded",
selected: false,
factory: {
products: {
'panel': PMUI.core.Item
}
}
};
jQuery.extend(true, defaults, settings);
this.setFactory(defaults.products)
.setTitle(defaults.title)
.setBody(defaults.body)
.setIconClass(defaults.iconClass)
.setHeaderClass(defaults.headerClass)
.setBodyClass(defaults.bodyClass)
.setFooterClass(defaults.footerClass)
.setContainerClass(defaults.containerClass)
.setIconExpanded(defaults.iconExpanded)
.setIconClosed(defaults.iconClosed)
.setCollapsed(defaults.collapsed)
.setSelected(defaults.selected);
};
/**
* Sets the string that represent the title. It could be an simple string
* or an string that contains html tags. If the string contains html code
* it will be rendered
* @param {String} title
*/
AccordionItem.prototype.setTitle = function (title) {
if (typeof title === "string") {
this.title = title;
if (this.header) {
this.header.items[2].title.html.innerHTML = title;
}
}
return this;
};
/**
* Sets the class name for the AccordionItem's title
* @param {String} class name
*/
AccordionItem.prototype.setIconClass = function (className) {
this.iconClass = (typeof className === 'string') ? className : '';
if (this.header) {
this.header.items[1].iconTitle.html.className = 'pmui-accordion-item-iconbase ' + className;
}
return this;
};
/**
* Sets the class name for the AccordionItem's header
* @param {String} class name
* @private
*/
AccordionItem.prototype.setHeaderClass = function (className) {
this.headerClass = (typeof className === 'string') ? className : '';
if (this.header) {
this.header.html.className = className;
}
return this;
};
/**
* Sets the class name for the AccordionItem's body
* @param {String} class name
* @private
*/
AccordionItem.prototype.setBodyClass = function (className) {
this.bodyClass = (typeof className === 'string') ? className : '';
if (this.html) {
this.container[1].className = className;
}
return this;
};
/**
* Sets the class name for the AccordionItem's footer
* @param {String} class name
* @private
*/
AccordionItem.prototype.setFooterClass = function (className) {
this.footerClass = (typeof className === 'string') ? className : '';
if (this.html) {
this.container[2].className = className;
}
return this;
};
/**
* Sets the class name for the AccordionItem's container
* @param {String} class name
* @private
*/
AccordionItem.prototype.setContainerClass = function (className) {
this.containerClass = (typeof className === 'string') ? className : '';
if (this.html) {
this.html.className = className;
}
return this;
};
/**
* Sets the class name that represents the icon in its expanded status
* @param {String} class name
* @private
*/
AccordionItem.prototype.setIconExpanded = function (className) {
this.iconExpanded = (typeof className === 'string') ? className : '';
if (this.header) {
this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + className;
}
return this;
};
/**
* Sets the class name that represents the icon in its closed status
* @param {String} class name
* @private
*/
AccordionItem.prototype.setIconClosed = function (className) {
this.iconClosed = (typeof className === 'string') ? className : '';
if (this.header) {
this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + className;
}
return this;
};
/**
* Sets the status for the item, whether it collapsed or not
* @param {Boolean} option
* @private
*/
AccordionItem.prototype.setCollapsed = function (option) {
this.collapsed = option;
return this;
};
/**
* Sets the status collapsed. If it true the container will be opened
* and if it false will be closed
* @param {Boolean} status
*/
AccordionItem.prototype.setSelected = function (status) {
if (typeof status === 'boolean') {
this.selected = status;
this.setCollapsed(!status);
}
return this;
};
/**
* Sets the body for the item as a new child for the container.
* The parameter can be:
*
* - {@link PMUI.core.Panel panel}: In this case the item can be any class that inherit from
* {@link PMUI.core.Panel Panel}. For example {@link PMUI.form.Form Form}, {@link PMUI.panel.TreePanel TreePanel}, etc.
* - String: In this case the string must have contain html code o can be a simple string like to exaple below:
*
* "Colosa"
*
* @param {PMUI.core.Panel|String} body
*/
AccordionItem.prototype.setBody = function (body) {
var currentPanel = this.body.html;
if (body instanceof PMUI.core.Container) {
body.getHTML();
this.body.items = [
{
panel: {
html: body.html
}
}
];
} else if (typeof body === 'string') {
this.body.items = [
{
panel: {
html: body
}
}
];
}
try {
this.body.html.removeChild(this.body.items[0].panel.html);
} catch (e) {
}
this.addItem(body);
return this;
};
/**
* Adds an child item to the object.
* @param {PMUI.core.Element|Object} item It can be one of the following data types:
* - {PMUI.core.Element} the object to add.
* - {Object} a JSON object with the settings for the Container to be added.
* @param {Number} [index] An index in which the item will be added.
* @chainable
*/
AccordionItem.prototype.addItem = function (item, index) {
var itemToBeAdded;
if (this.factory) {
itemToBeAdded = this.factory.make(item);
}
if (itemToBeAdded) {
itemToBeAdded.parent = this;
this.clearItems();
this.items.insert(itemToBeAdded);
if (this.body.html) {
this.body.html.appendChild(itemToBeAdded.getHTML());
if (this.eventsDefined) {
itemToBeAdded.defineEvents();
}
}
}
return this;
};
/**
* Handler the collapse funtionality
* @return {PMUI.item.AccordionItem}
*/
AccordionItem.prototype.toggleCollapse = function () {
if (this.collapsed) {
this.expand();
} else {
this.collapse();
}
return this;
};
/**
* Collapses the item and sets the collapsed property in false
*/
AccordionItem.prototype.collapse = function () {
var items;
items = this.container;
jQuery(items[1]).slideUp(300);
jQuery(items[2]).slideUp(300);
this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + this.iconClosed;
this.collapsed = true;
return this;
};
/**
* Expands the item selected. If the multipleSelection is enabled, It is posible select
* one or more items the otherwise is only possible select one item.
* @return {PMUI.item.AccordionItem}
*/
AccordionItem.prototype.expand = function () {
var i,
items,
otherElements;
items = this.container;
jQuery(items[1]).slideDown(300);
jQuery(items[2]).slideDown(300);
this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + this.iconExpanded;
this.collapsed = false;
//For other items
if (this.parent.multipleSelection === false) {
otherElements = this.parent.getItems();
for (i = 0; i < otherElements.length; i += 1) {
if (this.id != otherElements[i].id) {
otherElements[i].collapsed = false;
otherElements[i].toggleCollapse();
}
}
}
return this;
};
AccordionItem.prototype.createHTML = function () {
var container,
header,
iconNode,
iconTitle,
title,
body,
footer,
containerIcon;
if (this.html) {
return this.html;
}
container = AccordionItem.superclass.prototype.createHTML.call(this);
container.setAttribute('class', this.containerClass);
/*=============================================
Header accordion
=============================================*/
header = PMUI.createHTMLElement("div");
header.setAttribute('class', this.headerClass);
iconNode = PMUI.createHTMLElement('span');
containerIcon = PMUI.createHTMLElement('span');
containerIcon.className = "pmui-accordion-item-iconContainer";
if (this.collapsed) {
iconNode.setAttribute("class", 'pmui-accordion-item-iconbase ' + this.iconClosed);
} else if (!this.collapsed) {
iconNode.setAttribute("class", 'pmui-accordion-item-iconbase ' + this.iconExpanded);
}
iconTitle = PMUI.createHTMLElement('span');
iconTitle.setAttribute('class', 'pmui-accordion-item-iconbase ' + this.iconClass);
title = PMUI.createHTMLElement('span');
title.className = 'pmui-accordion-item-title';
title.innerHTML = this.title;
containerIcon.appendChild(iconNode);
header.appendChild(containerIcon);
this.iconNode = containerIcon;
header.appendChild(iconTitle);
header.appendChild(title);
this.header = {
html: header,
items: [
{
iconNode: {
html: iconNode
}
},
{
iconTitle: {
html: iconTitle
}
},
{
title: {
html: title
}
}
]
};
/*=============================================
body accordion
=============================================*/
body = PMUI.createHTMLElement("div");
body.setAttribute('class', this.bodyClass);
if (typeof this.parent.heightItem === "string") {
body.style.height = this.parent.heightItem;
} else {
body.style.height = this.parent.heightItem + 'px';
}
if (this.collapsed) {
body.style.display = 'none';
}
this.body.html = body;
jQuery(body).html(this.body.items[0].panel.html);
/*=============================================
footer accordion
=============================================*/
footer = PMUI.createHTMLElement("div");
footer.setAttribute('class', this.footerClass);
this.container = new Array();
this.container.push(header, body, footer);
/*=============================================
Container accordion
=============================================*/
container.appendChild(header);
container.appendChild(body);
container.appendChild(footer);
this.html = container;
return this.html;
};
/**
* Defines events related to the current panel and the calls to DefineEvents from
* every {@link PMUI.core.Item item}
* return {this}
*/
AccordionItem.prototype.defineEvents = function () {
var j,
fnSelect,
children,
that = this;
this.removeEvents().eventsDefined = true;
if (that.container[0]) {
that.addEvent('click').listen(this.iconNode, function (e) {
e.preventDefault();
that.toggleCollapse();
fnSelect = that.parent.listeners.select;
fnSelect(that, e);
e.stopPropagation();
});
}
if (that.items.getSize() > 0) {
children = that.getItems();
for (j = 0; j < children.length; j += 1) {
children[j].defineEvents();
}
}
return this;
};
PMUI.extendNamespace('PMUI.item.AccordionItem', AccordionItem);
if (typeof exports !== "undefined") {
module.exports = AccordionItem;
}
}());
(function () {
/**
* @class PMUI.item.TreeNode
* Each node in a TreePanel. It is able to apply data binding, that means that it can use its
* {@link PMUI.item.TreeNode#property-data data property} for set another object's property.
* @extends PMUI.core.Item
*
* The following example shows a TreeNode object whose child nodes are created using the
* {@link PMUI.item.TreeNode#cfg-items items config option}:
*
* @example
* var t;
* $(function() {
* t = new PMUI.item.TreeNode({
* label: 'America',
* childrenDefaultSettings: {
* label: '[untitled node]'
* },
* items: [
* {
* label: "North America",
* items: [
* {
* label: 'Canada',
* data: {
* label: 'Canada',
* value: 'cnd'
* }
* },
* {
* label: 'Mexico'
* }
* ]
* },
* {
* label: "Central America and Caribbean",
* items: [
* {
* label: 'Cuba'
* },
* {
* label: 'Guatemala'
* }
* ]
* },
* {
* label: "South America",
* items: [
* {
* label: 'Argentina'
* },
* {
* label: 'Bolivia'
* }
* ]
* }
* ]
* });
* document.body.appendChild(t.getHTML());
* t.defineEvents();
* });
*
* The following example shows a TreeNode object whose child nodes are created using the
* {@link PMUI.item.TreeNode#cfg-dataItems dataItems config option}:
*
* @example
* var t2;
* $(function() {
* t2 = new PMUI.item.TreeNode({
* data: {
* name: 'America'
* },
* labelDataBind: 'name',
* autoBind: true,
* childrenDefaultSettings: {
* labelDataBind: 'name',
* autoBind: true
* },
* dataItems: [
* {
* name: "Argentina"
* },
* {
* name: "Bolivia"
* },
* {
* name: "Brasil"
* }
* ]
* });
* document.body.appendChild(t2.getHTML());
* t2.defineEvents();
* });
*
* @cfg {Boolean} [sterile=false] If the node will be able to have children nodes.
* @cfg {String} [itemsDataBind=null] The key from the object's data to be used as the array for create the child
* nodes of the current node when the data binding be applied. If the data binding is applied then the
* {@link PMUI.item.TreeNode#cfg-items items config option} will be ignored.
* @cfg {String} [labelDataBind=null] The key from the object's data to be used as the label for the current node
* when the data binding be applied. If the data binding is applied then the
* {@link PMUI.item.TreeNode#cfg-label label config option} will be ignored.
* @cfg {String} [nodeClassDataBind=null] The key from the object's data to be used as the class for the node's
* html when the data binding be applied. If the data binding is applied then the
* {@link PMUI.item.TreeNode#cfg-nodeClass nodeClass config option} will be ignored.
* @cfg {Object} [data={}] The data for the node.
* @cfg {Boolean} [collapsed=true] If the node will be collapsed or not from the beginning.
* @cfg {String} [label='[item]'] The label text for the node.
* @cfg {String} [nodeClass=''] The css class for the node's html.
* @cfg {Function} [onCollapse=null] The callback function to be executed everytime the current node is collpased.
* Please read the {@link PMUI.item.TreeNode#event-onCollapse onCollapse event documentation}.
* @cfg {Function} [onExpand=null] The callback function yto be executed everytime the current node is expanded.
* Please read the {@link PMUI.item.TreeNode#event-onExpanded onExpanded event documentation}.
* @cfg {Object} [childrenDefaultSettings={}] The default config options to be applied to the new child nodes for
* the current node. This only will be applied when the appended item is an object literal.
* @cfg {Boolean} [autobind] If the data binding will be applied automatically on instantiation and everytime the
* data is set. This will cause that some others config options be ignored (like
* {@link PMUI.item.TreeNode#cfg-label label}, {@link PMUI.item.TreeNode#cfg-nodeClass nodeClass} and/or
* {@link PMUI.item.TreeNode#cfg-items items}).
* @cfg {Boolean} [recursiveChildrenDefaultSettings=false] If the current
* {@link PMUI.item.TreeNode#cfg-childrenDefaultSettings childrenDefaultSettings config option} will be inherited
* to the same config option of its child nodes.
* @cfg {Function|null} [onBeforeAppend=null] A callback function to be called everytime the
* {@link #event-onBeforeAppend onBeforeAppend} event is fired. For info about the parameters used for this
* callback please read the documentation for this event.
* @cfg {Function|null} [onAppend=null] A callback function to be called everytime the
* {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
* read the documentation for this event.
*/
var TreeNode = function (settings) {
TreeNode.superclass.call(this, settings);
/**
* If the node can have child nodes.
* @type {Boolean}
*/
this.sterile = null;
/**
* If the node is collapsed.
* @type {Boolean}
* @readonly
*/
this.collapsed = null;
/**
* The current label being used by the node.
* @type {String}
* @readonly
*/
this.label = null;
/**
* A literal object that will contain all the dom elements that compose the node's html structure.
* @type {Object}
* @readonly
*/
this.dom = {};
/**
* The css class for the node's html.
* @type {String}
* @readonly
*/
this.nodeClass = null;
/**
* @event onClick
* Fired when the current node is clicked
*/
this.onClick = null;
/**
* @event onCollapse
* Fired when the current node is collapsed.
*/
this.onCollapse = null;
/**
* @event onExpand
* Fired when the current node is expanded.
*/
this.onExpand = null;
/**
* The key of the object's data that will be used as the array to build the child nodes for the current one
* when the data binding is applied.
* @type {String}
* @readonly
*/
this.itemsDataBind = null;
/**
* The key of the object's data that will be used as the label for the current node when the data binding is
* applied.
* @type {String}
* @readonly
*/
this.labelDataBind = null;
/**
* The key of the object's data that will be used as the css class for the current node's html when the data
* binding is applied.
* @type {String}
* @readonly
*/
this.nodeClassDataBind = null;
/**
* If the data binding will be applied on initialization and everytime the data is set.
* @type {Boolean}
*/
this.autoBind = null;
/**
* The default config options to be applied to the child nodes to be add to the current node, but only when an
* object literal is used.
* @type {Object}
*/
this.childrenDefaultSettings = {};
/**
* The data for the current node. This data can be used to modify another node's properties through data
* binding.
* @type {PMUI.data.DataField}
* @private
*/
this.data = null;
/**
* If the settings defined in the {@link #property-childrenDefaultSettings childrenDefaultSettings} property will
* be inherited to all the child nodes of the current one.
* @type {Boolean}
*/
this.recursiveChildrenDefaultSettings = null;
/**
* @event onBeforeAppend
* Fired before a child node is appended to the current node or to any of its children nodes.
* @param {PMUI.item.TreeNode} node The node in which the event is being fired.
* @param {PMUI.item.TreeNode} newNode The new node to be appended.
*/
this.onBeforeAppend = null;
/**
* @event onAppend
* Fired everytime a child node is appended to the current node or to any of its children nodes.
* @param {PMUI.item.TreeNode} node The node in which the event is being fired.
* @param {PMUI.item.TreeNode} newNode The appended node.
*/
this.onAppend = null;
TreeNode.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Item', TreeNode);
/**
* @property {String} type=TreeNode The type of the class.
* @readonly
*/
TreeNode.prototype.type = 'TreeNode';
/**
* @property {String} family=Tree The class family.
* @readonly
*/
TreeNode.prototype.family = 'Tree';
/**
* Initializes the object.
* @param {Object} settings The object literal with the config options.
* @private
*/
TreeNode.prototype.init = function (settings) {
var defaults = {
itemsDataBind: null,
labelDataBind: null,
nodeClassDataBind: null,
data: {},
sterile: false,
collapsed: true,
label: '[item]',
nodeClass: "",
onCollapse: null,
onExpand: null,
onClick: null,
childrenDefaultSettings: {},
autoBind: false,
recursiveChildrenDefaultSettings: false,
onAppend: null,
onBeforeAppend: null
};
jQuery.extend(true, defaults, settings);
this.data = new PMUI.data.DataField();
this.setNodeClass(defaults.nodeClass)
if (defaults.autoBind) {
this.enableAutoBind();
} else {
this.disableAutoBind();
}
this.setRecursiveChildrenDefaultSettings(defaults.recursiveChildrenDefaultSettings)
.setChildrenDefaultSettings(defaults.childrenDefaultSettings)
.setSterility(defaults.sterile)
.setLabel(defaults.label)
.setParent(defaults.parent)
.setOnCollapseHandler(defaults.onCollapse)
.setOnExpandHandler(defaults.onExpand)
.setOnClickHandler(defaults.onClick)
.setItemsDataBind(defaults.itemsDataBind)
.setLabelDataBind(defaults.labelDataBind)
.setNodeClassDataBind(defaults.nodeClassDataBind)
.setOnAppendHandler(defaults.onAppend)
.setOnBeforeAppendHandler(defaults.onBeforeAppend);
if (defaults.dataItems && jQuery.isArray(defaults.dataItems)) {
this.setDataItems(defaults.dataItems);
} else {
this.setItems(defaults.items);
}
this.setData(defaults.data);
if (defaults.collapsed) {
this.collapse();
} else {
this.expand();
}
this.elementTag = 'li';
};
/**
* Sets the callback function to be called everytime the {@link #event-onBeforeAppend onBeforeAppend} event is
* fired. For info about the parameters used for this callback please read the documentation for this event.
* @param {Function|null} handler
*/
TreeNode.prototype.setOnBeforeAppendHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnBeforeAppendHandler(): The parameter must be a function or null.");
}
this.onBeforeAppend = handler;
return this;
};
/**
* Sets the callback function to be called everytime the
* {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
* read the documentation for this event.
* @param {Function|null} handler
*/
TreeNode.prototype.setOnAppendHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnAppendHandler(): The parameter must be a function or null.");
}
this.onAppend = handler;
return this;
};
/**
* Returns the level of the node relative to its root node.
* @return {Number}
*/
TreeNode.prototype.getDepth = function () {
if (!this.parent || !(this.parent instanceof PMUI.item.TreeNode)) {
return 0;
} else {
return this.parent.getDepth() + 1;
}
};
/**
* Returns the root node of the current one.
* @return {PMUI.item.TreeNode}
*/
TreeNode.prototype.getRoot = function () {
if (this.getDepth() === 0) {
return this;
} else {
return this.parent.getRoot();
}
};
/**
* Turns on the auto binding functionality.
* @chainable
*/
TreeNode.prototype.enableAutoBind = function () {
this.autoBind = true;
return this;
};
/**
* Turns off the auto binding functionality.
* @chainable
*/
TreeNode.prototype.disableAutoBind = function () {
this.autoBind = false;
return this;
};
/**
* Returns true if the current {@link PMUI.item.TreeNode#property-childrenDfaultSettings childrenDfaultSettings}
* will be inherited to the next child nodes that will be added.
* @return {Boolean} [description]
*/
TreeNode.prototype.isRecursiveChildrenDefaultSettings = function () {
return this.recursiveChildrenDefaultSettings;
};
/**
* Establish if the current object's childrenDefaultSettings property values will be set it to the same property
* of its future child nodes.
* @param {Boolean} recursive
*/
TreeNode.prototype.setRecursiveChildrenDefaultSettings = function (recursive) {
this.recursiveChildrenDefaultSettings = !!recursive;
return this;
};
/**
* Returns the default config options for the node's child nodes.
* @return {Object}
*/
TreeNode.prototype.getChildrenDefaultSettings = function () {
return this.childrenDefaultSettings;
};
/**
* Sets the default config options for the child nodes to be added. Note that if you set this, the effect will
* take effect only in new nodes added from the moment of the new set. The default settings will have effect only
* when you add an item using anything but an instance of {@link PMUI.core.Element} (or ant subclass of it).
* @param {Object} settings A object literal with some or all of the config options for a TreeNode instance.
*/
TreeNode.prototype.setChildrenDefaultSettings = function (settings) {
if (typeof settings !== 'object') {
throw new Error('setChildrenDefaultSettings(): The parameter must be an object.');
}
this.childrenDefaultSettings = settings;
return this;
};
/**
* Sets the key of the current node's data to be used as the css class for the node's html.
* @param {String} nodeClassDataBind
*/
TreeNode.prototype.setNodeClassDataBind = function (nodeClassDataBind) {
if (nodeClassDataBind !== null && typeof nodeClassDataBind !== 'string') {
throw new Error("setNodeClassDataBind(): the parameter must be a string or null.");
}
this.nodeClassDataBind = nodeClassDataBind;
return this;
};
/**
* Sets the key of the current node's data to be used as the array of items to be add to the current node.
* Note that every item in the array will be added as a data item
* (see the {@link PMUI.core.Container#method-addDataItem addDataItem()} method).
* @param {String} itemsDataBind
* @chainable
*/
TreeNode.prototype.setItemsDataBind = function (itemsDataBind) {
if (itemsDataBind !== null && typeof itemsDataBind !== 'string') {
throw new Error("setItemsDataBind(): the parameter must be a string or null.");
}
this.itemsDataBind = itemsDataBind;
return this;
};
/**
* Sets the key of the current node's data to be used as the label for the current node.
* @param {String} labelDataBind
* @chainable
*/
TreeNode.prototype.setLabelDataBind = function (labelDataBind) {
if (labelDataBind !== null && typeof labelDataBind !== 'string') {
throw new Error("setLabelDataBind(): the parameter must be a string or null.");
}
this.labelDataBind = labelDataBind;
return this;
};
/**
* Bind the node's data to its properties using:
*
* - The {@link PMUI.item.TreeNode#property-labelDataBind labelDataBind property} to set the
* {@link PMUI.item.TreeNode#property-label label property}.
* - The {@link PMUI.item.TreeNode#property-itemsDataBind labelDataBind property} for set the node's child items.
* - The {@link PMUI.item.TreeNode#property-nodeClassDataBind nodeClassDataBind property} for set the css class for
* the node's html.
* @chainable
*/
TreeNode.prototype.bindData = function () {
var data = this.data.getRecord();
this.setLabel(data[this.labelDataBind] || this.label);
if (this.itemsDataBind && jQuery.isArray(data[this.itemsDataBind])) {
this.setDataItems(data[this.itemsDataBind]);
}
this.setNodeClass(data[this.nodeClassDataBind] || this.nodeClass);
return this;
};
/**
* Sets the data for the node.
* @param {Object|PMUI.data.DataField} data
*/
TreeNode.prototype.setData = function (data) {
var key;
if (data instanceof PMUI.data.DataField) {
this.data = data;
} else if (typeof data === 'object') {
this.data.clear();
for (key in data) {
this.data.addAttribute(key, data[key]);
}
} else {
throw new Error("setData(): it only accepts a JSON object o an instance of PMUI.data.DataField as parameter.");
}
if (this.autoBind) {
this.bindData();
}
return this;
};
/**
* Return the node's data.
* @return {Object} A object literal containing the node's data.
*/
TreeNode.prototype.getData = function () {
return this.data.getRecord();
};
/**
* Sets the callback function for the {@link PMUI.item.TreeNode#event-onClickHandler onClickHandler event}.
* @param {Function} handler
*/
TreeNode.prototype.setOnClickHandler = function (handler) {
if (handler === null || typeof handler === 'function') {
this.onClick = handler;
}
return this;
};
/**
* Sets the callback function for the {@link PMUI.item.TreeNode#event-onCollapse onCollapse event}.
* @param {Function} handler
*/
TreeNode.prototype.setOnCollapseHandler = function (handler) {
if (handler === null || typeof handler === 'function') {
this.onCollapse = handler;
}
return this;
};
/**
* Sets the callback function for the {@link PMUI.item.TreeNode#event-onExpand onExpand event}.
* @param {Function} handler
*/
TreeNode.prototype.setOnExpandHandler = function (handler) {
if (handler === null || typeof handler === 'function') {
this.onExpand = handler;
}
return this;
};
/**
* Sets the factory for the curren node.
* @private
* @chainable
*/
TreeNode.prototype.setFactory = function () {
this.factory = new PMUI.util.Factory({
products: {
'treeNode': PMUI.item.TreeNode
},
defaultProduct: 'treeNode'
});
return this;
};
/**
* Sets the parent node for the current one.
* @param {null|PMUI.item.TreeNode|PMUI.panel.TreePanel} parent.
* @chainable
*/
TreeNode.prototype.setParent = function (parent) {
if (parent === null || parent instanceof PMUI.item.TreeNode || parent instanceof PMUI.panel.TreePanel) {
TreeNode.superclass.prototype.setParent.call(this, parent);
}
return this;
};
/**
* Returns true if the node is sterile (it can have more child nodes).
* @return {Boolean}
*/
TreeNode.prototype.isSterile = function () {
return this.sterile;
};
/**
* Sets a custom css class for the node's html.
* @param {String} nodeClass
*/
TreeNode.prototype.setNodeClass = function (nodeClass) {
if (typeof nodeClass !== 'string') {
throw new Error('setNodeClass(): the parameter must be a string.');
}
nodeClass = jQuery.trim(nodeClass);
if (this.html) {
jQuery(this.dom.nodeElement).removeClass(this.nodeClass).addClass('pmui-treepanel-node').addClass(nodeClass);
}
this.nodeClass = nodeClass;
return this;
};
/**
* Returns the custom css class of the node's html.
* @return {String}
*/
TreeNode.prototype.getNodeClass = function () {
return this.nodeClass;
};
/**
* Sets if the node is sterile or not (if can have more child nodes).
* @param {Boolean} sterile
* @chainable
*/
TreeNode.prototype.setSterility = function (sterile) {
this.sterile = !!sterile;
return this;
};
/**
* Sets the label for the node.
* @param {String} label
*/
TreeNode.prototype.setLabel = function (label) {
if (typeof label !== 'string') {
throw new Error('setLabel(): This method only accepts parameter of type String.');
}
this.label = label;
if (this.html) {
this.dom.label.textContent = label;
}
return this;
};
/**
* Applies the collapsing to its child nodes list.
* @param {Boolean} applyToChildren If the child nodes will apply the collapsing too.
* @chainable
*/
TreeNode.prototype.collapse = function (applyToChildren) {
var i, childNodes;
this.collapsed = true;
if (applyToChildren) {
childNodes = this.items.asArray();
for (i = 0; i < childNodes.length; i += 1) {
childNodes[i].collapse(true);
}
}
if (this.html) {
jQuery(this.containmentArea).hide();
jQuery(this.dom.nodeElement).addClass('pmui-treepanel-node-collapsed');
if (typeof this.onCollapse === 'function') {
this.onCollapse();
}
}
return this;
};
/**
* Applies the expanding to its child nodes list.
* @param {Boolean} applyToChildren If the child nodes will apply the expanding too.
* @chainable
*/
TreeNode.prototype.expand = function (applyToChildren) {
var i,
childNodes;
this.collapsed = false;
if (applyToChildren) {
childNodes = this.items.asArray();
for (i = 0; i < childNodes.length; i += 1) {
childNodes[i].expand(true);
}
}
if (this.html) {
jQuery(this.containmentArea).show();
jQuery(this.dom.nodeElement).removeClass('pmui-treepanel-node-collapsed');
if (typeof this.onExpand === 'function') {
this.onExpand();
}
}
return this;
};
/**
* Returns true if the node's child list is collapsed, otherwise it returns false.
* @return {Boolean}
*/
TreeNode.prototype.isCollapsed = function () {
return this.collapsed;
};
/**
* Returns true if the node has no child nodes, otherwise it returns false.
* @return {Boolean}
*/
TreeNode.prototype.isLeaf = function () {
return !this.items.getSize();
};
/**
* Returns true id the node is root, otherwise it returns false.
* @return {Boolean}
*/
TreeNode.prototype.isRoot = function () {
return this.parent instanceof PMUI.panel.TreePanel || !this.parent;
};
/**
* Updates the node's css class that sepcifies if the node is leaf or not.
* @private
* @chainable
*/
TreeNode.prototype.updateNodeTypeClass = function () {
if (this.items.getSize() === 0) {
jQuery(this.dom.nodeElement).removeClass('pmui-treepanel-node-father').addClass('pmui-treepanel-node-leaf');
} else {
jQuery(this.dom.nodeElement).removeClass('pmui-treepanel-node-leaf').addClass('pmui-treepanel-node-father');
}
return this;
};
/**
* The private handler fot the {@link #event-onBeforeAppend onBeforeAppend} event.
* @private
* @chainable
*/
TreeNode.prototype.onBeforeAppendHandler = function (treeNode) {
if (typeof this.onBeforeAppend === 'function') {
this.onBeforeAppend(this, treeNode);
}
this.parent && this.parent.onBeforeAppendHandler(treeNode);
return this;
};
/**
* The private handler for the {@link #event-onAppend} event.
* @private
* @chainable
*/
TreeNode.prototype.onAppendHandler = function (treeNode) {
if (typeof this.onAppend === 'function') {
this.onAppend(this, treeNode);
}
this.parent && this.parent.onAppendHandler(treeNode);
return this;
};
/**
* Adds a new child node.
* @param {Object|PMUI.item.TreeNode} item The item to add, it can be:
* - A object literal with the config options to create the {@link PMUI.item.TreeNode TreeNode} to be added. If
* you have set the {@link PMUI.item.TreeNode#property-childDefaultSettings childDefaultSettings property} the
* contents of the object literal will be merge with (and into) the contens of this property. The resulting object
* literal will be used to create the {@link PMUI.item.TreeNode TreeNode} to be added.
* - An instance of {@link PMUI.item.TreeNode TreeNode}.
* @param {Number} index The index position for the node insertion.
*/
TreeNode.prototype.addItem = function (item, index) {
var settings = {},
toAdd;
if (this.isSterile()) {
throw new Error('addItem(): The tree node is sterile, it can\'t have children.');
}
if (typeof item !== 'object' && !(item instanceof PMUI.item.TreeNode)) {
throw new Error('addItem(): The parameter must be an instance of PMUI.item.TreeNode or an object.');
}
if (item instanceof PMUI.item.TreeNode) {
item.setParent(this);
toAdd = item;
} else if (typeof item === 'object') {
if (this.recursiveChildrenDefaultSettings) {
item.childrenDefaultSettings = this.childrenDefaultSettings;
item.recursiveChildrenDefaultSettings = true;
}
jQuery.extend(true, settings, this.childrenDefaultSettings, item);
settings.parent = this;
toAdd = this.factory.make(settings);
}
if (toAdd) {
if (typeof this.onBeforeAppend === 'function') {
this.onBeforeAppend(this, toAdd);
}
this.onBeforeAppendHandler(toAdd);
TreeNode.superclass.prototype.addItem.call(this, toAdd, index);
this.onAppendHandler(toAdd);
}
if (this.html) {
this.updateNodeTypeClass();
}
return this;
};
/**
* @inheritdoc
*/
TreeNode.prototype.setItems = function (items) {
if (this.onAppend !== undefined) {
TreeNode.superclass.prototype.setItems.call(this, items);
}
return this;
};
/**
* Removes a direct child node.
* @param {Number|PMUI.item.TreeNode|String} item It can be a string (id of the child to remove),
* a number (index of the child to remove) or a TreeNode object.
* @chainable
*/
TreeNode.prototype.removeItem = function (item) {
TreeNode.superclass.prototype.removeItem.call(this, item);
this.updateNodeTypeClass();
return this;
};
/**
* Toggles the collapsing/expanding actions.
* @chainable
*/
TreeNode.prototype.toggleCollapsing = function () {
if (this.isCollapsed()) {
this.expand();
} else {
this.collapse();
}
return this;
};
/**
* Clears the current filter criteria.
* @chainable
*/
TreeNode.prototype.clearFilter = function () {
this.filter("");
return this;
};
/**
* Filter the current node and all its children.
* @param {String} filterCriteria The criteria for filtering, the filtering will be perform in the node's data.
* @return {Boolean} True if any of its nodes meets the filtering criteria.
*/
TreeNode.prototype.filter = function (filterCriteria) {
var res = false,
childNodes = this.items.asArray(),
i,
regExp,
partialRes;
for (i = 0; i < childNodes.length; i += 1) {
childNodes[i].setVisible(partialRes = childNodes[i].filter(filterCriteria));
res = res || partialRes;
}
if (!res) {
regExp = new RegExp(filterCriteria, "i");
if (regExp.test(this.label)) {
return true;
}
}
return res;
};
/**
* Define then events for the object.
* @chainable
*/
TreeNode.prototype.defineEvents = function () {
var that;
TreeNode.superclass.prototype.defineEvents.call(this);
if (this.dom.title) {
that = this;
this.addEvent('click').listen(this.dom.title, function (e) {
var root = that.getRoot(),
panel = root.parent;
e.preventDefault();
if (typeof that.onClick === 'function') {
that.onClick();
}
if (panel instanceof PMUI.panel.TreePanel && typeof panel.onNodeClick === 'function') {
panel.onNodeClick(panel, that);
}
that.toggleCollapsing();
});
}
return this;
};
/**
* Creates the node's html.
* @return {HTMLELement}
*/
TreeNode.prototype.createHTML = function () {
var nodeElement,
title,
label,
icon,
childrenList,
elbow;
if (this.html) {
return this.html;
}
html = PMUI.createHTMLElement(this.elementTag || "div");
html.id = this.id;
PMUI.linkToPMUIObject(html, this);
this.html = html;
this.actionHTML = html;
this.applyStyle();
nodeElement = this.html;
nodeElement.className = 'pmui-treepanel-node';
nodeElement.id = this.id;
title = PMUI.createHTMLElement('a');
title.className = 'pmui-treepanel-node-title';
title.href = '#';
elbow = PMUI.createHTMLElement('b');
elbow.className = 'pmui-treepanel-node-elbow';
elbow.innerHTML = ' ';
label = PMUI.createHTMLElement('span');
label.className = 'pmui-treepanel-node-label';
icon = PMUI.createHTMLElement('i');
icon.className = 'pmui-treepanel-node-icon';
icon.innerHTML = ' ';
childrenList = PMUI.createHTMLElement('ul');
childrenList.className = 'pmui-treepanel-list';
this.actionHTML = title;
title.appendChild(elbow);
title.appendChild(icon);
title.appendChild(label);
nodeElement.appendChild(title);
nodeElement.appendChild(childrenList);
this.dom.nodeElement = nodeElement;
this.dom.title = title;
this.dom.label = label;
this.dom.icon = icon;
this.containmentArea = childrenList;
this.html = nodeElement;
this.updateNodeTypeClass();
this.setParent(this.parent)
.setSterility(this.sterile)
.setLabel(this.label)
.setNodeClass(this.nodeClass);
if (this.items.getSize() > 0) {
this.paintItems(this.items.asArray().slice(0));
}
if (this.collapsed) {
this.collapse();
} else {
this.expand();
}
this.style.applyStyle();
return this.html;
};
PMUI.extendNamespace('PMUI.item.TreeNode', TreeNode);
}());
(function () {
/**
* @class PMUI.item.ListItem
*/
var ListItem = function (settings) {
ListItem.superclass.call(this, settings);
this.text = null;
this.iconClass = null;
this.toolbar = null;
this.dom = {};
this.actions = [];
this.visibleIcon = null;
this.data = null;
this.textDataBind = null;
this.onClick = null;
ListItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Item', ListItem);
ListItem.prototype.type = "ListItem";
ListItem.prototype.init = function (settings) {
var defaults = {
elementTag: 'li',
text: '[list-item]',
actions: [],
iconClass: null,
visibleIcon: false,
data: {},
textDataBind: null,
onClick: null
};
this.toolbar = new PMUI.toolbar.Toolbar({
style: {
cssClasses: ['pmui-listitem-actions']
}
});
this.data = new PMUI.data.DataField();
jQuery.extend(true, defaults, settings);
this.setTextDataBind(defaults.textDataBind)
.setElementTag(defaults.elementTag)
.setText(defaults.text)
.setIconClass(defaults.iconClass)
.setActions(defaults.actions)
.setData(defaults.data)
.setOnClickHandler(defaults.onClick);
if (defaults.visibleIcon) {
this.showIcon();
} else {
this.hideIcon();
}
};
ListItem.prototype.setOnClickHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setOnClickHandler(): The parameter must be or null.");
}
this.onClick = handler;
return this;
};
ListItem.prototype.onClickHandler = function () {
var that = this;
return function () {
if (typeof that.onClick === 'function') {
that.onClick(that);
}
};
};
ListItem.prototype.setTextDataBind = function (textDataBind) {
if (textDataBind !== null && typeof textDataBind !== 'string') {
throw new Error("setTextDataBind(): the parameter must be a string or null.");
}
this.textDataBind = textDataBind;
return this.bindData();
};
ListItem.prototype.bindData = function () {
var data = this.data.getRecord();
this.setText(data[this.textDataBind] || this.text || "");
return this;
};
ListItem.prototype.setData = function (data) {
var key;
if (data instanceof PMUI.data.DataField) {
this.data = data;
} else if (typeof data === 'object') {
this.data.clear();
for (key in data) {
this.data.addAttribute(key, data[key]);
}
} else {
throw new Error("setData(): it only accepts a JSON object o an instance of PMUI.data.DataField as parameter.");
}
this.bindData();
return this;
};
ListItem.prototype.clearActions = function () {
this.toolbar.clearItems();
return this;
};
ListItem.prototype.addAction = function (action) {
var that = this,
aux,
handler;
aux = action.handler || null;
if (!(aux === null || typeof aux === 'function')) {
throw new Error("addAction(): The parameter's \"'handler\" property must be a function or null or not be specified.");
}
handler = aux ? function () {
if (aux) {
aux.call(that, that, this);
}
} : null;
action.action = handler;
if (action instanceof PMUI.toolbar.ToolbarAction) {
action.setAction(handler);
} else if (typeof action === 'object') {
action.action = handler;
}
this.toolbar.addItem(action);
return this;
};
ListItem.prototype.setActions = function (actions) {
var i;
if (!jQuery.isArray(actions)) {
throw new Error("setActions(): The parameter must be an array.");
}
this.clearActions();
for (i = 0; i < actions.length; i += 1) {
this.addAction(actions[i]);
}
return this;
};
ListItem.prototype.setIconClass = function (iconClass) {
if (!(iconClass === null || typeof iconClass === 'string')) {
throw new Error("setIconClass(): the parameter must be a string or null.");
}
this.iconClass = iconClass;
if (this.dom.iconContainer) {
this.dom.iconContainer.className = 'pmui-listitem-icon ' + (iconClass || "");
}
return this;
};
ListItem.prototype.hideIcon = function () {
this.visibleIcon = false;
if (this.dom.iconContainer) {
this.dom.iconContainer.style.display = 'none';
}
return this;
};
ListItem.prototype.showIcon = function () {
this.visibleIcon = true;
if (this.dom.iconContainer) {
this.dom.iconContainer.style.display = '';
}
return this;
}
ListItem.prototype.setText = function (text) {
if (typeof text !== 'string') {
throw new Error("setText(): the parameter must be a string.");
}
this.text = text;
if (this.dom.textContainer) {
this.dom.textContainer.textContent = text;
}
return this;
};
ListItem.prototype.getData = function () {
return this.data.getRecord();
};
ListItem.prototype.defineEvents = function () {
return this;
};
ListItem.prototype.createHTML = function () {
var textContainer,
iconContainer,
that = this;
if (this.html) {
return this.html;
}
ListItem.superclass.prototype.createHTML.call(this);
textContainer = PMUI.createHTMLElement('div');
textContainer.className = 'pmui-listitem-text';
iconContainer = PMUI.createHTMLElement('i');
iconContainer.className = 'pmui-listitem-icon';
this.dom.textContainer = textContainer;
this.dom.iconContainer = iconContainer;
this.html.appendChild(iconContainer);
this.html.appendChild(textContainer);
this.html.appendChild(this.toolbar.getHTML());
this.setText(this.text);
if (this.visibleIcon) {
this.showIcon();
} else {
this.hideIcon();
}
this.addEvent('click').listen(this.html, function (e) {
var parent = that.getParent();
e.stopPropagation();
if (typeof that.onClick === 'function') {
that.onClick(that);
}
if (parent) {
parent.onItemClickHandler(that);
}
});
return this.html;
};
PMUI.extendNamespace('PMUI.item.ListItem', ListItem);
}());
(function () {
/**
* @class PMUI.proxy.Proxy
* Defines the proxy functionality to persist data
* @abstract
*
* @constructor
* Creates a new instance of this class
* @param {Object} options
*
* @cfg {Object} data Object that will be sent through the proxy
*/
var Proxy = function (options) {
/**
* Data object used to send/receive through proxy
* @type {Object}
*/
this.data = null;
Proxy.prototype.init.call(this, options);
};
/**
* Defines the object's type
* @type {String}
*/
Proxy.prototype.type = "Proxy";
/**
* Defines the object's family
* @type {String}
*/
Proxy.prototype.family = "Proxy";
/**
* @private
* Initilizes the object with the default values
* @param {Object} options
*/
Proxy.prototype.init = function (options) {
var defaults = {
data: null
};
jQuery.extend(true, defaults, options);
this.setData(defaults.data);
};
/**
* Sets the data
* @param {Object} data Object to be sent
*/
Proxy.prototype.setData = function (data) {
this.data = data;
return this;
};
/**
* Returns the data related to this class
* @return {Object}
*/
Proxy.prototype.getData = function () {
return this.data;
};
/**
* @abstract
* Sends the data to the target
*/
Proxy.prototype.send = function () {
};
/**
* @abstract
* Receives the data from the target
*/
Proxy.prototype.receive = function () {
};
PMUI.extendNamespace("PMUI.proxy.Proxy", Proxy);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = Proxy;
}
}());
(function () {
/**
* @class PMUI.proxy.RestProxy
* @extend PMUI.proxy.Proxy
*
* How used the class
* The {@link PMUI.proxy.RestClient Restclient} class encapsulate all the funcionality about the communicate
* with the server.
*
* @example
* restclient = new PMUI.proxy.RestProxy({
* url: "url.php",
* method: "GET",
* data: {},
* dataType: "json",
* success: function () {
* ...
* },
* failure: function () {
* ...
* },
* complete: function() {
* ...
* }
* });
*
*
* @constructor
* Creates a new component
* @param {Object} [settings] settings The configuration options are specified on the config options:
*
* @cfg {String} [url=""] The url property defines the target for the RestClient.
* @cfg {String} method
* The available methods to consume are the follow:
*
* 'create', 'read', 'update', 'delete'
*
* These have a directly relation to:
*
* 'create': 'POST',
* 'read' : 'GET',
* 'update': 'PUT',
* 'delete': 'DELETE'
*
* Based in the {@link RestClient RestClient} library
* @cfg {Object} data Object that will be sent through the proxy.
* @cfg {String} [dataType='json'] The available options are the following sentences:
*
* json: 'application/json',
* plain: 'text/plain',
* form: 'application/x-www-form-urlencoded',
* html: 'text/html'
*
* @cfg {String} [authorizationType= "none"] The options availables for the property:
*
* "none"
* "basic"
* "oauth2"
*
* @cfg {Boolean} [authorizationOAuth=false] For enable the communicate between client-server
* through Oauth protocol the property must to be true
*
*/
var RestProxy = function (options) {
RestProxy.superclass.call(this, options);
this.url = null;
this.method = null;
this.rc = null;
this.data = null;
RestProxy.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.proxy.Proxy', RestProxy);
/**
* @property {String}
* @readonly
*/
RestProxy.prototype.type = "RestProxy";
RestProxy.prototype.init = function (options) {
var defaults = {
url: null,
method: 'GET',
data: {},
dataType: 'json',
authorizationType: 'none',
authorizationOAuth: false,
success: function () {
},
failure: function () {
},
complete: function () {
}
};
jQuery.extend(true, defaults, options);
this.setRestClient()
.setUrl(defaults.url)
.setAuthorizationOAuth(defaults.authorizationOAuth)
.setMethod(defaults.method)
.setData(defaults.data)
.setDataType(defaults.dataType)
.setSuccessAction(defaults.success)
.setFailureAction(defaults.failure)
.setCompleteAction(defaults.complete);
};
/**
* Creates an instance of the {@link RestClient ResClient} and
* stores the object inside of {@link RestProxy#rc rc property}
*/
RestProxy.prototype.setRestClient = function () {
if (this.rc instanceof RestClient === false) {
this.rc = new RestClient();
}
return this;
};
/**
* Sets the url that will be used for send or get the data
* from server, It's a basic element for {@link RestClient RestClient}
* @param {String} url A string that represents the connection between
* Client and server
*
*
*/
RestProxy.prototype.setUrl = function (url) {
this.url = url;
return this;
};
/**
* Sets the OAuth protocol to 'false' or 'true' for enable or disable the property.
*
* //Example
* obj = new PMUI.proxy.RestProxy();
* obj.setAuthorizationOAuth(true);
*
* @param {Boolean} option Defines the boolean option
*/
RestProxy.prototype.setAuthorizationOAuth = function (option) {
if (typeof option === 'boolean') {
this.rc.setSendBearerAuthorization(option);
}
return this;
};
/**
* This method is useful for update the current action.
* The available methods to consume are the follow:
* 'create', 'read', 'update', 'delete'
* These have a directly relation to:
* 'create': 'POST',
* 'read' : 'GET',
* 'update': 'PUT',
* 'delete': 'DELETE'
* Based in the {@link RestClient RestClient} library
* @param {String} A string that reporesents
*/
RestProxy.prototype.setMethod = function (method) {
this.method = method;
return this;
};
RestProxy.prototype.setSuccessAction = function (action) {
RestProxy.prototype.success = action;
return this;
};
RestProxy.prototype.setFailureAction = function (action) {
RestProxy.prototype.failure = action;
return this;
};
RestProxy.prototype.setCompleteAction = function (action) {
RestProxy.prototype.complete = action;
return this;
};
/**
*
* @param {Object} data Data for send to server
* @return {PMUI.proxy.RestProxy} this The current component
*
*/
RestProxy.prototype.setData = function (data) {
this.data = data;
return this;
};
/**
* Sets the Data type used for request data.
* the types availables for the methods are the following:
*
* json: 'application/json',
* plain: 'text/plain',
* form: 'application/x-www-form-urlencoded',
* html: 'text/html'
*
* //For example
* restclient = new PMUI,proxy.RestProxy();
* restclient.setDataType("json");
* ...
*
* @param {String} dataType Type
*/
RestProxy.prototype.setDataType = function (dataType) {
this.rc.setDataType(dataType);
return this;
};
RestProxy.prototype.setCredentials = function (usr, pass) {
this.rc.setBasicCredentials(usr, pass);
return this;
};
RestProxy.prototype.setContentType = function () {
this.rc.setContentType();
return this;
};
/**
* Sets the authorization type and credentials for the RestClient.
*
* The options availables about authorization types are:
*
* 'none'
* 'basic'
* 'oauth2'
*
* The credentials must have two keys: The username and password
*
* //For example
* restclient = new PMUI.proxy.RestProxy();
* restclient.setAuthorizationType('oauth2', credentials);
*
* Is possible sets the property while instances the class like to sentence below:
*
* restclient = new PMUI.proxy.RestProxy({
* ...
* authorizationOAuth: true
* ...
* });
*
*
* @param {Object} type Authorization type
* @param {Object} credentials These are the keys that represent the username and password
*/
RestProxy.prototype.setAuthorizationType = function (type, credentials) {
this.rc.setAuthorizationType(type);
switch (type) {
case 'none':
break;
case 'basic':
this.rc.setBasicCredentials(credentials.client, credentials.secret);
break;
case 'oauth2':
this.rc.setAccessToken(credentials);
break;
}
return this;
};
/**
* Send data to server using POST method from RestClient
* @param {Object} settings Configuration parameters
*
* //For example
* this.post({
* url: "url.php",
* data: {
* grant_type: "authorization_code",
* code: keys.authorizedCode
* },
* success: function (xhr, response) {
* console.log(response);
* },
* failure: function (xhr, response) {
* console.log(response);
* },
* complete: function (xhr, response) {
* console.error(response);
* }
* });
*
*/
RestProxy.prototype.post = function (settings) {
var that = this;
if (settings !== undefined) {
that.init(settings);
}
if (this.rc) {
that.rc.postCall({
url: that.url,
id: that.uid,
data: that.data,
success: function (xhr, response) {
that.success.call(that, xhr, response);
},
failure: function (xhr, response) {
that.failure.call(that, xhr, response);
},
complete: function (xhr, response) {
that.complete.call(that, xhr, response);
}
});
that.rc.setSendBearerAuthorization(false);
} else {
throw new Error("the RestClient was not defined, please verify the property 'rc' for continue.");
}
};
/**
* Updates data from the server using UPDATE method from RestClient
* @param {Object} settings
* If it necessary is possible overwrite all the data instantiated on the RestClient
*
* //For example
* this.update({
* url: "url.php",
* authorizationOAuth: true,
* success: function (xhr, response) {
* console.log(response);
* },
* failure: function (xhr, response) {
* console.error(response);
* },
* complete: function (xhr, response) {
* console.error(response);
* }
* });
*
*/
RestProxy.prototype.update = function (settings) {
var that = this;
if (settings !== undefined) {
that.init(settings);
}
if (this.rc) {
this.rc.putCall({
url: this.url,
id: this.uid,
data: this.data,
success: function (xhr, response) {
that.success.call(this, xhr, response);
},
failure: function (xhr, response) {
that.failure.call(this, xhr, response);
},
complete: function (xhr, response) {
that.complete.call(that, xhr, response);
}
});
} else {
throw new Error("the RestClient was not defined, please verify the property 'rc' for continue.");
}
};
/**
* Gets data from the server using GET method from RestClient
* @param {Object} settings
* If it necessary is possible overwrite all the data instantiated on the RestClient
*
* //For example
* this.get({
* url: "url.php",
* authorizationOAuth: true,
* success: function (xhr, response) {
* console.log(response);
* },
* failure: function (xhr, response) {
* console.error(response);
* },
* complete: function (xhr, response) {
* console.error(response);
* }
* });
*
*/
RestProxy.prototype.get = function (settings) {
var that = this;
if (settings !== undefined) {
that.init(settings);
}
if (this.rc) {
that.rc.getCall({
url: that.url,
id: that.uid,
data: that.data,
success: function (xhr, response) {
that.success.call(that, xhr, response);
},
failure: function (xhr, response) {
that.failure.call(that, xhr, response);
},
complete: function (xhr, response) {
that.complete.call(that, xhr, response);
}
});
that.rc.setSendBearerAuthorization(false);
} else {
throw new Error("the RestClient was not defined, please verify the property 'rc' for continue.");
}
};
/**
* Removes data using DELETE method from RestClient
* @param {Object} settings
* If it necessary is possible overwrite all the data instantiated on the RestClient
*
* //For example
* this.remove({
* url: "url.php",
* authorizationOAuth: true,
* success: function (xhr, response) {
* console.log(response);
* },
* failure: function (xhr, response) {
* console.error(response);
* },
* complete: function (xhr, response) {
* console.error(response);
* }
* });
*
*/
RestProxy.prototype.remove = function (settings) {
var that = this;
if (settings !== undefined) {
that.init(settings);
}
if (this.rc) {
this.rc.deleteCall({
url: this.url,
id: this.uid,
data: this.data,
success: function (xhr, response) {
that.success.call(this, xhr, response);
},
failure: function (xhr, response) {
that.failure.call(this, xhr, response);
},
complete: function (xhr, response) {
that.complete.call(that, xhr, response);
}
});
} else {
throw new Error("the RestClient was not defined, please verify the property for continue.");
}
};
/**
* This is the callback for {@link RestClient RestClient} and it will be actived
* and executed when the {@link RestClient RestClient} petition was success.
* @param {Object} xhr
* @param {Object} response
*/
RestProxy.prototype.success = function (xhr, response) {
};
/**
* If something happend with the connection or maybe the server is
* down, this callback will be executed
* @param {Object} xhr
* @param {Object} response
*/
RestProxy.prototype.failure = function (xhr, response) {
};
/**
* This is the callback for {@link RestClient RestClient} and it will be actived
* and executed when the {@link RestClient RestClient} petition finishes.
* @param {Object} xhr
* @param {Object} response
*/
RestProxy.prototype.complete = function (xhr, response) {
};
PMUI.extendNamespace('PMUI.proxy.RestProxy', RestProxy);
if (typeof exports !== 'undefined') {
module.exports = RestProxy;
}
}());
(function () {
/**
* @class PMUI.event.Event
* Handles the events generated in the PMUI library
* @abstract
*
* @constructor
* Creates a new instance of class
* @param {Object} options
*
* @cfg {Object} element Defines the HTMLElement
* @cfg {Function} handler Defines the callback Function to be executed
* @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
* @cfg {String} name Event name
*/
var Event = function (options) {
/**
* Stores the HTMLElement associated
* @type {Object}
*/
this.element = null;
/**
* Stores the callback function to be executed
* @type {Function}
*/
this.handler = null;
/**
* Stores the selector for delegated event listeners.
* @type {String}
*/
this.selector = null;
/**
* Event name
* @type {String}
*/
this.eventName = null;
Event.prototype.init.call(this, options);
};
/**
* Defines the object's type
* @type {String}
*/
Event.prototype.type = "Event";
/**
* Defines the object's family
* @type {String}
*/
Event.prototype.family = "Event";
/**
* @private
* Initializes the object with default options
* @param {Object} options
*/
Event.prototype.init = function (options) {
var defaults = {
handler: function (scope) {
}
};
jQuery.extend(true, defaults, options);
if (defaults.action && defaults.action instanceof PMUI.event.Action) {
this.setHandler(defaults.action.handler);
} else {
this.setHandler(defaults.handler);
}
this.setElement(defaults.element)
.setEventName(defaults.name);
return this;
};
/**
* Sets the HTML Element
* @param {Object} element [description]
*/
Event.prototype.setElement = function (element) {
this.element = element;
return this;
};
/**
* Sets the callback function
* @param {Function} fn
*/
Event.prototype.setHandler = function (fn) {
if (typeof fn === 'function') {
this.handler = fn;
}
return this;
};
/**
* Sets the selector to be used for delegated event handlers.
* @param {String} selector
* @chainable
*/
Event.prototype.setSelector = function (selector) {
if (typeof selector === 'string') {
this.selector = selector;
}
return this;
};
/**
* Sets the event name
* @param {String} name
*/
Event.prototype.setEventName = function (name) {
this.eventName = name;
return this;
};
/**
* Add a handler to listen for a event with delegation.
* @param {HTMLElement} parentElement The parent element whose child elements the event will be delegate to.
* @param {String} selector A valid jQuery selector to filter the descendants of the selected
* elements that trigger the event.
* @param {Function} handler A function to execute when the event is triggered.
* @chainable
*/
Event.prototype.listenWithDelegation = function (parentElement, selector, handler) {
var handlerFunction;
if (handler instanceof PMUI.event.Action) {
handlerFunction = handler.handler;
} else {
handlerFunction = handler;
}
$(parentElement).on(this.eventName, selector, handler);
this.setHandler(handler).setElement(parentElement).setSelector(selector);
return this;
};
/**
* @abstract
* Defines the way to listen the event (jquery)
* @param {HTMLElement} element [description]
* @param {Function} handler [description]
*/
Event.prototype.listen = function (element, handler) {
var handlerFunction;
if (handler instanceof PMUI.event.Action) {
handlerFunction = handler.handler;
} else {
handlerFunction = handler;
}
jQuery(element).on(this.eventName, handlerFunction);
this.setHandler(handlerFunction).setElement(element);
return this;
};
/**
* Removes the event listener.
* @chainable
*/
Event.prototype.unlisten = function () {
if (this.selector) {
jQuery(this.element).off(this.eventName, this.selector, this.handler);
} else {
jQuery(this.element).off(this.eventName, this.handler);
}
return this;
};
PMUI.extendNamespace('PMUI.event.Event', Event);
if (typeof exports !== 'undefined') {
module.exports = Event;
}
}());
(function () {
/**
* @class PMUI.event.EventFactory
* @extend PMUI.util.Factory
* Extends the factory class to produce Events instances
*
* @constructor
* Creates a new instance od the class
*/
var EventFactory = function () {
EventFactory.superclass.call(this);
EventFactory.prototype.init.call(this);
};
PMUI.inheritFrom('PMUI.util.Factory', EventFactory);
/**
* Defines the object's type
* @type {String}
*/
EventFactory.prototype.type = 'EventFactory';
/**
* @private
* Define the event types supported
* @type {Object}
*/
EventFactory.prototype.eventTypes = {
'click': 'mouse',
'mousedown': 'mouse',
'mouseup': 'mouse',
'mousemove': 'mouse',
'mouseover': 'mouse',
'mouseout': "mouse",
'mouseenter': "mouse",
'mouseleave': "mouse",
'dblclick': "mouse",
'drag': 'mouse',
'drop': 'mouse',
'resize': 'mouse',
'rightclick': 'mouse',
'contextmenu': 'mouse',
'blur': 'form',
'change': 'form',
'focus': 'form',
'select': 'form',
'submit': 'form',
'keyup': 'keyboard',
'keydown': 'keyboard',
'keypress': 'keyboard'
};
/**
* @private
* Initializes the object with default values
*/
EventFactory.prototype.init = function () {
var defaults = {
products: {
"mouse": PMUI.event.MouseEvent,
"form": PMUI.event.FormEvent,
"keyboard": PMUI.event.KeyboardEvent,
"event": PMUI.event.Event
},
defaultProduct: "event"
};
this.setProducts(defaults.products)
.setDefaultProduct(defaults.defaultProduct);
};
/**
* Overwrite the make function to accept strings
* @param {Object/String} type
* @return {PMUI.event.Event}
*/
EventFactory.prototype.make = function (type) {
var eventInstance,
eventType;
if (this.isValidClass(type)) {
eventInstance = type;
} else {
eventType = this.eventTypes[type] || 'event';
eventInstance = this.build(eventType, {name: type});
}
return eventInstance;
};
PMUI.extendNamespace('PMUI.event.EventFactory', EventFactory);
if (typeof exports !== 'undefined') {
module.exports = EventFactory;
}
}());
(function () {
/**
* @class PMUI.event.MouseEvent
* @extend PMUI.event.Event
* Handles the events generated by the mouse
*
* @constructor
* Create a new instance
* @param {Object} options Constructor options
*
* @cfg {Object} element Defines the HTMLElement
* @cfg {Function} handler Defines the callback Function to be executed
* @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
* @cfg {String} name Event name
*/
var MouseEvent = function (options) {
MouseEvent.superclass.call(this, options);
};
PMUI.inheritFrom('PMUI.event.Event', MouseEvent);
/**
* Defines the object's type
* @type {String}
*/
MouseEvent.prototype.type = "MouseEvent";
PMUI.extendNamespace('PMUI.event.MouseEvent', MouseEvent);
if (typeof exports !== 'undefined') {
module.exports = MouseEvent;
}
}());
(function () {
/**
* @class PMUI.event.KeyboardEvent
* @extend PMUI.event.Event
* Handles the events generated by the mouse
*
* @constructor
* Create a new instance
* @param {Object} options Constructor options
*
* @cfg {Object} element Defines the HTMLElement
* @cfg {Function} handler Defines the callback Function to be executed
* @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
* @cfg {String} name Event name
*/
var KeyboardEvent = function (options) {
KeyboardEvent.superclass.call(this, options);
};
PMUI.inheritFrom('PMUI.event.Event', KeyboardEvent);
/**
* Defines the object's type
* @type {String}
*/
KeyboardEvent.prototype.type = "KeyboardEvent";
PMUI.extendNamespace('PMUI.event.KeyboardEvent', KeyboardEvent);
if (typeof exports !== 'undefined') {
module.exports = KeyboardEvent;
}
}());
(function () {
/**
* @class PMUI.event.FormEvent
* @extend PMUI.event.Event
* Handles the events generated by the mouse
*
* @constructor
* Create a new instance
* @param {Object} options Constructor options
*
* @cfg {Object} element Defines the HTMLElement
* @cfg {Function} handler Defines the callback Function to be executed
* @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
* @cfg {String} name Event name
*/
var FormEvent = function (options) {
FormEvent.superclass.call(this, options);
};
PMUI.inheritFrom('PMUI.event.Event', FormEvent);
/**
* Defines the object's type
* @type {String}
*/
FormEvent.prototype.type = "FormEvent";
PMUI.extendNamespace('PMUI.event.FormEvent', FormEvent);
if (typeof exports !== 'undefined') {
module.exports = FormEvent;
}
}());
(function () {
/**
* @class PMUI.draw.Core
* @extends PMUI.core.Element
*
* This class contains the common behavior of the main families of classes
* in the library
* This class should never be instantiated since its just an abstraction of
* properties that classes in the library share
*
* //e.g.
* //We will set the properties defined in this class, to a custom shape
* var customShape = new PMUI.draw.CustomShape({
* id : "someid",
* canvas : someCanvas //assuming there is a canvas instance
* style: { //style options regarding the objects
* cssProperties: {}, //These are the style properties we want the
* //object to have
* cssClasses: ["someclass"] //css classes that will be applied
* to the object
* },
* //now we set the width and height
* width : 30,
* height : 50,
* //and the coordinates we want the shape to be positioned
* x : 10,
* y : 5,
* //z-index of the element
* zOrder : 1,
* //set to true if we want to make it visible
* visible : true,
*
* });
*
* @constructor
* Creates an instance of the class
* @param {Object} options options for initializing the object
* @cfg {String} id id that will be assigned to the element
* @cfg {Object} [style={
* cssProperties: {},
* cssClasses: []
* }] style properties and classes that we want to assign to the element
* @cfg {PMUI.draw.Canvas} [canvas=null] canvas associated to the element
* @cfg {number} [width=0] width of the element
* @cfg {number} [height=0] height of the element
* @cfg {number} [x=0] x coordinate of the element
* @cfg {number} [y=0] y coordinate of the element
* @cfg {number} [zOrder=1] z-Index applied to the element
* @cfg {boolean} [visible=true] Determines whether an element will be visible
*/
var Core = function (options) {
Core.superclass.call(this, options);
/**
* Defines the canvas object
* @type {Object}
*/
this.canvas = null;
/**
* previous width of the Core
* @property {number}
*/
this.oldWidth = 0;
/**
* previous height of the Core
* @property {number}
*/
this.oldHeight = 0;
/**
* previous x coordinate of the Core
* @property {number}
*/
this.oldX = 0;
/**
* previous y coordinate of the Core
* @property {number}
*/
this.oldY = 0;
/**
* The x coordinate relative to the canvas
* @property {number}
* @deprecated Use {@link PMUI.draw.Core#getAbsoluteY} instead.
*/
this.absoluteX = 0;
/**
* The y coordinate relative to the canvas
* @property {number}
* @deprecated Use {@link PMUI.draw.Core#getAbsoluteX} instead.
*/
this.absoluteY = 0;
/**
* Previous x coordinate relative to the canvas
* @property {number}
*/
this.oldAbsoluteX = 0;
/**
* Previous y coordinate relative to the canvas
* @property {number}
*/
this.oldAbsoluteY = 0;
/**
* Zoom in x
* @property {number}
*/
this.zoomX = 0;
/**
* Zoom in y
* @property {number}
*/
this.zoomY = 0;
/**
* Width after the zoom
* @property {number}
*/
this.zoomWidth = 0;
/**
* Height after the zoom
* @property {number}
*/
this.zoomHeight = 0;
/**
* Saved options is a copy of the default initializer extended
* with the parameter 'options'
* @property {Object}
*/
this.savedOptions = {};
/**
* Defines the drag behavior object
* @type {PMUI.behavior.DragBehavior}
*/
this.drag = null;
Core.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.core.Element', Core);
/**
* Denotes the type of the object
* @property {String}
*/
Core.prototype.type = "Core";
/**
* @abstract Method for applying the styles and preform tasks related to the
* view of the object
*/
Core.prototype.paint = function () {
};
/**
* Initializes the element with the options given
* @param {Object} options options for initializing the object
*/
Core.prototype.init = function (options) {
var defaults = {
zOrder: 1,
visible: true,
drag: "nodrag",
positionMode: "absolute"
};
$.extend(true, defaults, options);
this.setZOrder(defaults.zOrder)
.setVisible(defaults.visible)
// .setDragBehavior(defaults.drag)
.setCanvas(defaults.canvas);
};
Core.prototype.applyStyle = function () {
if (this.html) {
this.style.applyStyle();
this.style.addProperties({
display: this.visible ? this.display : "none",
position: "absolute", //TODO use this.positionMode instead,
left: this.zoomX,
top: this.zoomY,
width: this.zoomWidth,
height: this.zoomHeight,
zIndex: this.zOrder
});
}
return this;
};
Core.prototype.createHTML = function () {
var properties;
if (!this.html) {
Core.superclass.prototype.createHTML.call(this);
this.defineEvents();
}
return this.html;
};
/**
* Sets the position of the Core to a given pair of coordinates
* @param {Number} newX new x coordinate for the Core
* @param {Number} newY new y coordinate for the Core
* @chainable
*/
Core.prototype.setPosition = function (newX, newY) {
this.setX(newX);
this.setY(newY);
return this;
};
/**
* Sets the dimension of the Core to a given width and height
* @param {Number} newWidth new width of the Core
* @param {Number} newHeight new height of the Core
* @chainable
*/
Core.prototype.setDimension = function (newWidth, newHeight) {
this.setWidth(newWidth);
this.setHeight(newHeight);
return this;
};
/**
* Sets the x coordinate of the Core, returns true if successful
* @param {Number} newX
* @chainable
*/
Core.prototype.setX = function (newX) {
if (typeof newX === "number") {
newX = Math.round(newX);
this.x = newX;
if (this.canvas) {
this.zoomX = this.x * this.canvas.zoomFactor;
} else {
this.zoomX = this.x;
}
this.setAbsoluteX();
if (this.html) {
this.style.addProperties({left: this.zoomX});
}
} else {
throw new Error("setX : parameter newX is not a number");
}
return this;
};
/**
* Sets the y coordinate of the Core, returns true if successful
* @param {Number} newY
* @chainable
*/
Core.prototype.setY = function (newY) {
if (typeof newY === "number") {
newY = Math.round(newY);
this.y = newY;
if (this.canvas) {
this.zoomY = this.y * this.canvas.zoomFactor;
} else {
this.zoomY = this.y;
}
this.setAbsoluteY();
if (this.html) {
this.style.addProperties({top: this.zoomY});
}
}
return this;
};
/**
* Sets the x coordinate of the Core relative to the canvas
* @chainable
* @deprecated absoluteX will be an in-moment property retrieved by the {PMUI.draw.Core#getAbsoluteX} method.
*/
Core.prototype.setAbsoluteX = function () {
if (!this.parent) {
this.absoluteX = this.zoomX;
} else {
this.absoluteX = this.zoomX + this.parent.absoluteX;
}
return this;
};
/**
* Sets the value to an old X reference
* @param {Number} newX
* @chainable
*/
Core.prototype.setOldX = function (newX) {
if (typeof newX === "number") {
this.oldX = newX;
}
return this;
};
/**
* Sets the value to an old y reference
* @param {Number} newY
* @chainable
*/
Core.prototype.setOldY = function (newY) {
if (typeof newY === "number") {
this.oldY = newY;
}
return this;
};
/**
* Sets the y coordinate of the Core relative to the canvas
* @chainable
* @deprecated absoluteY will be an in-time generated property retrieved by the {PMUI.draw.Core#getAbsoluteY} getter.
*/
Core.prototype.setAbsoluteY = function () {
if (!this.parent) {
this.absoluteY = this.zoomY;
} else {
this.absoluteY = this.zoomY + this.parent.absoluteY;
}
return this;
};
/**
* Sets the width of the Core, returns true if successful
* @param {Number} newWidth
* @chainable
*/
Core.prototype.setWidth = function (newWidth) {
var intPart;
if (typeof newWidth === "number" && newWidth >= 0) {
this.width = newWidth;
if (this.canvas) {
this.zoomWidth = this.width * this.canvas.zoomFactor;
intPart = Math.floor(this.zoomWidth);
this.zoomWidth = (this.zoomWidth % 2 === 0) ? intPart + 1 : intPart;
} else {
this.zoomWidth = this.width;
}
if (this.html) {
this.style.addProperties({width: this.zoomWidth});
}
}
return this;
};
/**
* Sets the height of the Core, returns true if successful
* @param {Number} newHeight
* @chainable
*/
Core.prototype.setHeight = function (newHeight) {
var intPart;
if (typeof newHeight === "number" && newHeight >= 0) {
this.height = newHeight;
if (this.canvas) {
this.zoomHeight = this.height * this.canvas.zoomFactor;
intPart = Math.floor(this.zoomHeight);
this.zoomHeight = (this.zoomHeight % 2 === 0) ? intPart + 1 : intPart;
} else {
this.zoomHeight = this.height;
}
if (this.html) {
this.style.addProperties({height: this.zoomHeight});
}
}
return this;
};
/**
* Sets the zOrder of this element
* @param {Number} newZOrder
* @chainable
*/
Core.prototype.setZOrder = function (newZOrder) {
if (typeof newZOrder === "number" && newZOrder > 0) {
this.zOrder = newZOrder;
if (this.html) {
this.style.addProperties({zIndex: this.zOrder});
}
}
return this;
};
/**
* Sets the canvas for the current object
* @param {PMUI.draw.Canvas} newCanvas
* @returns {PMUI.draw.Core}
*/
Core.prototype.setCanvas = function (newCanvas) {
if (newCanvas && newCanvas.family === "Canvas") {
this.canvas = newCanvas;
}
return this;
};
/**
* Sets this element to be visible or not if it has html, just sets the display
* property to inline or none
* @param {boolean} newVisible
* @chainable
*/
Core.prototype.setVisible = function (newVisible) {
if (typeof newVisible === "boolean") {
this.visible = newVisible;
if (this.html) {
if (newVisible) {
this.style.addProperties({display: "inline"});
} else {
this.style.addProperties({display: "none"});
}
}
}
return this;
};
/**
* Sets the id of this element and updates the html if there is such
* @param {String} newID
* @chainable
*/
Core.prototype.setID = function (newID) {
this.id = newID;
if (this.html) {
this.html.id = this.id;
}
return this;
};
/**
* Returns the canvas related to this object
* @returns {PMUI.draw.Canvas}
*/
Core.prototype.getCanvas = function () {
return this.canvas;
};
/**
* Returns the x coordinate relative to the canvas of this object
* @return {Number}
*/
Core.prototype.getAbsoluteX = function () {
var x, oldDisplay;
if (this instanceof PMUI.draw.Canvas) {
return 0;
} else if (PMUI.isInDOM(this.html)) {
oldDisplay = this.html.style.display;
this.html.style.display = 'inline-block';
x = $(this.html).offset().left;
this.html.style.display = oldDisplay;
return x - $(this.canvas.html).offset().left;
} else {
return this.zoomX + this.parent.getAbsoluteX();
}
};
/**
* Returns the y coordinate relative to the canvas of this object
* @return {Number}
*/
Core.prototype.getAbsoluteY = function () {
var y, oldDisplay;
if (this instanceof PMUI.draw.Canvas) {
return 0;
} else if (PMUI.isInDOM(this.html)) {
oldDisplay = this.html.style.display;
this.html.style.display = 'inline-block';
y = $(this.html).offset().top;
this.html.style.display = oldDisplay;
return y - $(this.canvas.html).offset().top;
} else {
return this.zoomY + this.parent.getAbsoluteY();
}
};
/**
* Returns the style of this Core
* @return {PMUI.util.Style}
*/
Core.prototype.getStyle = function () {
return this.style;
};
/**
* Gets the x coordinate relative to the zoom scale
* @return {Number}
*/
Core.prototype.getZoomX = function () {
return this.zoomX;
};
/**
* Gets the y coordinate relative to the zoom scale
* @return {Number}
*/
Core.prototype.getZoomY = function () {
return this.zoomY;
};
/**
* Gets the width relative to the zoom scale
* @return {Number}
*/
Core.prototype.getZoomWidth = function () {
return this.zoomWidth;
};
/**
* Gets the height relative to the zoom scale
* @return {Number}
*/
Core.prototype.getZoomHeight = function () {
return this.zoomHeight;
};
/**
* Retrieves the previous value for coordinate x
* @return {Number}
*/
Core.prototype.getOldX = function () {
return this.oldX;
};
/**
* Retrieves the previous value for coordinate y
* @return {Number}
*/
Core.prototype.getOldY = function () {
return this.oldY;
};
/**
* Retrieves the previous value for width
* @return {Number}
*/
Core.prototype.getOldWidth = function () {
return this.oldWidth;
};
/**
* Retrieves the previous value for height
* @return {Number}
*/
Core.prototype.getOldHeight = function () {
return this.oldHeight;
};
/**
* Stringifies the basic data of this shape and the drag behavior of this shape
* @return {Object}
*/
Core.prototype.stringify = function () {
return {
id: this.getID(),
x: this.getX(),
y: this.getY(),
width: this.getWidth(),
height: this.getHeight(),
type: this.type,
style: this.getStyle().stringify(),
drag: this.savedOptions.drag
};
};
/**
* Sets a valid drag behavior
* @param {Object} obj
*/
Core.prototype.setDragBehavior = function (obj) {
var factory = new PMUI.behavior.BehaviorFactory({
products: {
"customshapedrag": PMUI.behavior.CustomShapeDragBehavior,
"regulardrag": PMUI.behavior.RegularDragBehavior,
"connectiondrag": PMUI.behavior.ConnectionDragBehavior,
"connection": PMUI.behavior.ConnectionDragBehavior,
"nodrag": PMUI.behavior.NoDragBehavior
},
defaultProduct: "nodrag"
});
this.drag = factory.make(obj);
if (this.html && this.drag) {
this.drag.attachDragBehavior(this);
}
return this;
};
/**
* Functions that calculates the relative position over the canvas
* @param {Object} e jQuery Event
* @return {Object}
*/
Core.prototype.relativePoint = function (e) {
var auxX, auxY;
auxX = e.pageX - this.absoluteX;
auxY = e.pageY - this.absoluteY;
if (this.canvas) {
auxX += this.canvas.getLeftScroll();
auxY += this.canvas.getTopScroll();
auxX = Math.floor(auxX / this.canvas.zoomFactor);
auxY = Math.floor(auxY / this.canvas.zoomFactor);
}
return {
x: auxX,
y: auxY
};
};
/**
* JSON parser for creating PMDrawObjects
* @param {Object} json
*/
Core.prototype.parseJSON = function (json) {
this.initObject(json);
};
/**
* Number that represents the top direction
* @property {number}
*/
Core.prototype.TOP = 0;
/**
* Number that represents the right direction
* @property {number}
*/
Core.prototype.RIGHT = 1;
/**
* Number that represents the bottom direction
* @property {Number}
*/
Core.prototype.BOTTOM = 2;
/**
* Number that represents the left direction
* @property {Number}
*/
Core.prototype.LEFT = 3;
/**
* Number that represents horizontal direction
* @property {Number}
*/
Core.prototype.HORIZONTAL = 0;
/**
* Number that represents vertical direction
* @property {Number}
*/
Core.prototype.VERTICAL = 1;
/**
* Number of zoom scales available
* @property {Number}
*/
Core.prototype.ZOOMSCALES = 5;
PMUI.extendNamespace('PMUI.draw.Core', Core);
if (typeof exports !== 'undefined') {
module.exports = Core;
}
}());
(function () {
/**
* @class PMUI.draw.BehavioralElement
* Class that encapsulates the behavior of all elements that have container and
* drop behaviors attached to them.
* since this class inherits from {@link PMUI.draw.Core}, then the common behaviors
* and properties for all elements in the designer are also part of this class
* The purpose of this class is to encapsulate behaviors related to drop and
* containment of elements, so it shouldn't be instantiated, we should
* instantiate the elements that inherit from this class instead.
*
* //i.e
* //we will set the behaviors that are related only to this class
* var shape = new PMUI.draw.CustomShape({
* //we can set different types of containers here and the factory
* //will do all the work
* container : "regular",
* drop : {
* //type specifies the drop behavior we want, again we just need
* // to pass a string
* type : "container",
* //selectors are the css selectors that this element will
* //accept to be dropped
* selectors : [".firstselector",".secondselector"],
* //overwrite is an option to override previous and default
* //selectors
* overwrite : false
* }
* });
*
*
* @extends PMUI.draw.Core
*
* @constructor Creates a new instance of this class
* @param {Object} options
* @cfg {String} [container="nocontainer"] the type of container behavior
* we want for an object, it can be regular,or nocontainer, or any other class
* that extends the {@link PMUI.behavior.ContainerBehavior} class, but also note that we would
* need to override the factory for container behaviors located in this class.
* @cfg {Object} [drop={
* drop : "nodrop",
* selectors : [],
* overwrite : false
* }] Object that contains the options for the drop behavior we want an object
* to have, we can, assign type which can be container, connection,
* connectioncontainer, or no drop. As with the container behavior we can extend
* the behaviors and factory for this functionality.
* We also have selectors that specify the selectors the drop behavior will
* accept and the overwrite feature
*/
var BehavioralElement = function (options) {
BehavioralElement.superclass.call(this, options);
/**
* Determines the Behavior object that this object has
* @property {PMUI.behavior.ContainerBehavior}
*/
this.containerBehavior = null;
/**
* Determines the Drop Behavior of the element
* @type {PMUI.behavior.DropBehavior}
*/
this.dropBehavior = null
/**
* List of the children
* @property {*}
*/
this.children = null;
BehavioralElement.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', BehavioralElement);
/**
* Type of the all instances of this class
* @property {String}
*/
BehavioralElement.prototype.type = "BehavioralElement";
/**
* Instance initializer which uses options to extend the default config options.
* The default options are container: nocontainer, and drop: nodrop
* @param {Object} options
*/
BehavioralElement.prototype.init = function (options) {
var defaults = {
drop: "nodrop",
container: "nocontainer"
};
$.extend(true, defaults, options);
var defaults = {
drop: {
type: "nodrop",
selectors: [],
overwrite: false
},
container: "nocontainer"
};
$.extend(true, defaults, options);
this.setDropBehavior(defaults.drop.type, defaults.drop.selectors,
defaults.drop.overwrite);
this.setContainerBehavior(defaults.container);
this.children = new PMUI.util.ArrayList();
};
BehavioralElement.prototype.setDropBehavior = function (behavior, selectors,
overwrite) {
this.dropBehavior = this.dropBehaviorFactory(behavior, selectors);
this.dropBehavior.setSelectors(selectors, overwrite);
if (this.html && this.dropBehavior) {
this.dropBehavior.attachDropBehavior(this);
// update the saved object
// added by mauricio to save the drop behavior of this shape
$.extend(true, this.savedOptions.drop, {
type: behavior,
overwrite: overwrite
});
if (selectors && selectors.hasOwnProperty('length')) {
this.dropBehavior.updateSelectors(this, selectors, overwrite);
// update the saved object
// added by mauricio to save the drop behavior of this shape
$.extend(true, this.savedOptions.drop, {
selectors: selectors
});
}
}
return this;
};
/**
* Factory of drop behaviors. It uses lazy instantiation to create instances of
* the different drop behaviors
* @param {String} type Type of drop behavior we want to assign to an object,
* it can be nodrop, container, connection or connectioncontainer
* @param {Array} selectors Array containing the css selectors that the drop
* behavior will accept
* @return {DropBehavior}
*/
BehavioralElement.prototype.dropBehaviorFactory = function (type, selectors) {
if (type === "nodrop") {
if (!this.noDropBehavior) {
this.noDropBehavior = new PMUI.behavior.NoDropBehavior(selectors);
}
return this.noDropBehavior;
}
if (type === "container") {
if (!this.containerDropBehavior) {
this.containerDropBehavior = new PMUI.behavior.ContainerDropBehavior(selectors);
}
return this.containerDropBehavior;
}
if (type === "connection") {
if (!this.connectionDropBehavior) {
this.connectionDropBehavior = new PMUI.behavior.ConnectionDropBehavior(selectors);
}
return this.connectionDropBehavior;
}
if (type === "connectioncontainer") {
if (!this.connectionContainerDropBehavior) {
this.connectionContainerDropBehavior =
new PMUI.behavior.ConnectionContainerDropBehavior(selectors);
}
return this.connectionContainerDropBehavior;
}
};
/**
* Sets the container behavior of an object, using the same options as
* the factory
* @param {String} behavior the container behavior we want the factory to
* assign, it can be regular, or nocontainer
* @chainable
*/
BehavioralElement.prototype.setContainerBehavior = function (behavior) {
// update the saved object
// added by mauricio to save the container behavior of this
$.extend(true, this.savedOptions, {container: behavior});
this.containerBehavior = this.containerBehaviorFactory(behavior);
return this;
};
/**
* Factory of container behaviors. It uses lazy instantiation to create
* instances of the different container behaviors
* @param {String} type An string that specifies the container behavior we want
* an instance to have, it can be regular or nocontainer
* @return {ContainerBehavior}
*/
BehavioralElement.prototype.containerBehaviorFactory = function (type) {
if (type === "regular") {
if (!this.regularContainerBehavior) {
this.regularContainerBehavior = new PMUI.behavior.RegularContainerBehavior();
}
return this.regularContainerBehavior;
}
if (!this.noContainerBehavior) {
this.noContainerBehavior = new PMUI.behavior.NoContainerBehavior();
}
return this.noContainerBehavior;
};
/**
* Updates the children positions of a container given the x and y difference
* @param {Number} diffX x difference
* @param {Number} diffY y difference
* @chainable
* // TODO make this method recursive
*/
BehavioralElement.prototype.updateChildrenPosition = function (diffX, diffY) {
var children = this.getChildren(),
child,
i,
updatedChildren = [],
previousValues = [],
newValues = [];
for (i = 0; i < children.getSize(); i += 1) {
child = children.get(i);
if ((diffX !== 0 || diffY !== 0) && !this.canvas.currentSelection.contains(child)) {
updatedChildren.push(child);
previousValues.push({
x: child.x,
y: child.y
});
newValues.push({
x: child.x + diffX,
y: child.y + diffY
});
}
child.setPosition(child.x + diffX, child.y + diffY);
}
if (updatedChildren.length > 0) {
this.canvas.triggerPositionChangeEvent(updatedChildren, previousValues,
newValues);
}
return this;
};
/**
* Returns whether the instance is a container or not
* @return {Boolean}
*/
BehavioralElement.prototype.isContainer = function () {
return this.containerBehavior &&
this.containerBehavior.type !== "NoContainerBehavior";
};
/**
* Encapsulates the functionality of adding an element this element according
* to its container behavior
* @param {PMUI.draw.Shape} shape Shape we want to add to the element
* @param {Number} x x coordinate where the shape will be positionated relative
* to this element
* @param {Number} y y coordinate where the shape will be positionated relative
* to this element
* @param {Boolean} topLeftCorner determines if the drop position should be
* calculated from the top left corner of the shape or, from its center
* @chainable
*/
BehavioralElement.prototype.addElement = function (shape, x, y,
topLeftCorner) {
this.containerBehavior.addToContainer(this, shape, x, y, topLeftCorner);
return this;
};
/**
* Encapsulates the functionality of removing an element this element according
* to its container behavior
* @param {PMUI.draw.Shape} shape shape to be removed from this element
* @chainable
*/
BehavioralElement.prototype.removeElement = function (shape) {
this.containerBehavior.removeFromContainer(shape);
return this;
};
/**
* Swaps a shape from this container to a different one
* @param {PMUI.draw.Shape} shape shape to be swapped
* @param {PMUI.draw.BehavioralElement} otherContainer the other container the shape will
* be swapped to
* @param {Number} x x coordinate where the shape will be positionated relative
* to this element
* @param {Number} y y coordinate where the shape will be positionated relative
* to this element
* @param {Boolean} topLeftCorner determines if the drop position should be
* calculated from the top left corner of the shape or, from its center
* @chainable
*/
BehavioralElement.prototype.swapElementContainer = function (shape,
otherContainer, x,
y, topLeftCorner) {
var newX = !x ? shape.getX() : x,
newY = !y ? shape.getY() : y;
shape.changedContainer = true;
this.removeElement(shape);
otherContainer.addElement(shape, newX, newY, topLeftCorner);
return this;
};
/**
* Returns the list of children belonging to this shape
* @returns {PMUI.util.ArrayList}
*/
BehavioralElement.prototype.getChildren = function () {
return this.children;
};
/**
* Updates the dimensions and position of this shape (note: 'this' is a shape)
* @param {Number} margin the margin for this element to consider towards the
* shapes near its borders
* @chainable
*/
BehavioralElement.prototype.updateDimensions = function (margin) {
// update its size (if an child grew out of the shape)
// only if it's not the canvas
if (this.family !== 'Canvas') {
this.updateSize(margin);
this.refreshConnections();
// updates JQueryUI's options (minWidth and minHeight)
PMUI.behavior.ResizeBehavior.prototype.updateResizeMinimums(this);
BehavioralElement.prototype.updateDimensions.call(this.parent, margin);
}
return this;
};
/**
* Sets the selectors of the current drop behavior
* @param {Array} selectors new css selectors for the drop behavior
* @param {Boolean} overwrite determines whether the default selectors will
* be erased
* @chainable
*/
BehavioralElement.prototype.setDropAcceptedSelectors = function (selectors,
overwrite) {
if (selectors && selectors.hasOwnProperty('length')) {
this.drop.updateSelectors(this, selectors, overwrite);
}
return this;
};
/**
* Attach the drop behavior to the element, if there is such
* @chainable
*/
BehavioralElement.prototype.updateBehaviors = function () {
if (this.dropBehavior) {
this.dropBehavior.attachDropBehavior(this);
this.dropBehavior.updateSelectors(this);
}
return this;
};
/**
* Stringifies the container and drop behavior of this object
* @return {Object}
*/
BehavioralElement.prototype.stringify = function () {
var inheritedJSON = BehavioralElement.superclass.prototype.stringify.call(this),
thisJSON = {
container: this.savedOptions.container,
drop: this.savedOptions.drop
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
PMUI.extendNamespace('PMUI.draw.BehavioralElement', BehavioralElement);
if (typeof exports !== 'undefined') {
module.exports = BehavioralElement;
}
}());
(function () {
/**
* @class PMUI.behavior.BehaviorFactory
* @extend PMUI.util.Factory
* Extends the factory class to produce Behavior instances
*
* @constructor
* Creates a new instance od the class
* @param {Object} options
*/
var BehaviorFactory = function (options) {
BehaviorFactory.superclass.call(this, options);
};
PMUI.inheritFrom('PMUI.util.Factory', BehaviorFactory);
/**
* Defines the object's type
* @type {String}
*/
BehaviorFactory.prototype.type = 'BehaviorFactory';
/**
* Overwrite the make function to accept strings
* @param {Object/String} type
* @return {Object}
*/
BehaviorFactory.prototype.make = function (obj) {
var behaviorInstance,
behaviorType = obj.pmType;
if (this.isValidClass(obj)) {
behaviorInstance = obj;
} else if (this.isValidName(behaviorType)) {
behaviorInstance = this.build(behaviorType, obj);
} else if (this.isValidName(obj)) {
behaviorInstance = this.build(obj, {});
} else {
behaviorInstance = this.build(this.defaultProduct, obj);
}
return behaviorInstance;
};
PMUI.extendNamespace('PMUI.behavior.BehaviorFactory', BehaviorFactory);
if (typeof exports !== 'undefined') {
module.exports = BehaviorFactory;
}
}());
(function () {
/**
* @abstract
* @class PMUI.behavior.DragBehavior
* Abstract class that encapsulates the drag behavior of an object
*
* @constructor Creates a new instance of the class
*
*/
var DragBehavior = function () {
};
/**
* Type of the object
* @property {String}
*/
DragBehavior.prototype.type = "DragBehavior";
/**
* Family of the object
* @property {String}
*/
DragBehavior.prototype.family = "DragBehavior";
/**
* Attach the drag listener and its corresponding ui properties to the shape
* @param {PMUI.draw.Shape} shape
*/
DragBehavior.prototype.attachDragBehavior = function (shape) {
var dragOptions,
$shape = $(shape.getHTML());
dragOptions = {
revert: false,
helper: "none",
cursorAt: false,
revertDuration: 0,
grid: [1, 1],
start: this.onDragStart(shape),
drag: this.onDrag(shape),
stop: this.onDragEnd(shape)
};
$shape.draggable(dragOptions);
};
/**
* @event dragStart
* @abstract drag start handler, function that runs when the drag start event occurs,
* it should return a function so that any implementation should go inside the
* return
* @param {PMUI.draw.Shape} shape current shape being dragged
* @template
* @protected
*/
DragBehavior.prototype.onDragStart = function (shape) {
return function (e, ui) {
};
};
/**
* @event drag
* Drag handler, function that runs when dragging is occurring,
* it should return a function so that any implementation should go inside the
* return
* @param {PMUI.draw.Shape} shape shape being dragged
* @template
* @protected
*/
DragBehavior.prototype.onDrag = function (shape) {
return function (e, ui) {
};
};
/**
* @event dragEnd
* Drag end handler, function that runs when the drag end event occurs,
* it should return a function so that any implementation should go inside the
* return
* @param {PMUI.draw.Shape} shape
* @template
* @protected
*/
DragBehavior.prototype.onDragEnd = function (shape) {
return function (e, ui) {
};
};
/**
* @abstract Executes the hook Function for the drag start event
* @template
* @protected
*/
DragBehavior.prototype.dragStartHook = function (hookFunction) {
};
/**
* @abstract Executes the hook function for the drag event
* @template
* @protected
*/
DragBehavior.prototype.dragHook = function (hookFunction) {
};
/**
* @abstract Executes the hook function for the drag end event
* @template
* @protected
*/
DragBehavior.prototype.dragEndHook = function () {
};
PMUI.extendNamespace('PMUI.behavior.DragBehavior', DragBehavior);
if (typeof exports !== 'undefined') {
module.exports = DragBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.CustomShapeDragBehavior
* Encapsulates the drag behavior of a custom shape (with ports and connections)
* , it also encapsulates the behavior for multiple drag
* @extends PMUI.behavior.DragBehavior
*
* @constructor Creates a new instance of the class
*
*/
var CustomShapeDragBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.DragBehavior', CustomShapeDragBehavior);
/**
* Type of the instances
* @property {String}
*/
CustomShapeDragBehavior.prototype.type = "CustomShapeDragBehavior";
/**
* Attach the drag behavior and ui properties to the corresponding shape
* @param {PMUI.draw.CustomShape} customShape
*/
CustomShapeDragBehavior.prototype.attachDragBehavior = function (customShape) {
var dragOptions,
$customShape = $(customShape.getHTML());
dragOptions = {
revert: false,
helper: "none",
cursorAt: false,
revertDuration: 0,
disable: false,
grid: [1, 1],
start: this.onDragStart(customShape),
drag: this.onDrag(customShape, true),
stop: this.onDragEnd(customShape, true)
};
$customShape.draggable({'cursor':"move"});
$customShape.draggable(dragOptions);
};
//TODO Encapsulates behaviors for multiple drag, and simple custom shape drag
//TODO Initialize all oldX and oldY values
/**
* On drag start handler, it uses the {@link PMUI.behavior.RegularDragBehavior}.onDragStart
* method to initialize the drag, but also initializes other properties
* @param {PMUI.draw.CustomShape} customShape
* @return {Function}
*/
CustomShapeDragBehavior.prototype.onDragStart = function (customShape) {
return function (e, ui) {
if(customShape.canvas.currentSelection.asArray().length == 0){
customShape.canvas.addToSelection(customShape);
}
PMUI.behavior.RegularDragBehavior.prototype.onDragStart.call(this,
customShape)(e, ui);
customShape.previousXDragPosition = customShape.getX();
customShape.previousYDragPosition = customShape.getY();
if (customShape.canvas.snapToGuide) {
//init snappers
customShape.canvas.startSnappers(e);
}
customShape.canvas.isDragging = true;
};
};
/**
* Procedure executed while dragging, it takes care of multiple drag, moving
* connections, updating positions and children of the shapes being dragged
* @param {PMUI.draw.CustomShape} customShape shape being dragged
* @param {boolean} root return whether this is the shape where the drag started
* @param {number} childDiffX x distance needed for the non-root shapes to move
* @param {number} childDiffY y distance needed for the non-root shapes to move
* @param {Object} e jQuery object containing the properties when a drag event
* occur
* @param {Object} ui JQuery UI object containing the properties when a drag
* event occur
*/
CustomShapeDragBehavior.prototype.onDragProcedure = function (customShape, root, childDiffX, childDiffY, e, ui) {
var i,
j,
sibling,
diffX,
diffY,
port,
child,
connection,
shape1,
shape2,
canvas = customShape.canvas,
k,
uiOffset,
positionsX1= [];
uiOffset = {};
uiOffset.x = ui.helper.position().left / canvas.zoomFactor;
uiOffset.y = ui.helper.position().top / canvas.zoomFactor;
uiOffset.diffX = customShape.x - uiOffset.x;
uiOffset.diffY = customShape.y - uiOffset.y;
for (k = 0; k < customShape.canvas.currentSelection.getSize(); k += 1) {
sibling = customShape.canvas.currentSelection.get(k);
if (sibling.id !== customShape.id) {
positionsX1.push(sibling.x + uiOffset.diffX);
}
}
// shapes
if (root) {
// Commented for problem on snappers
if (customShape.canvas.snapToGuide) {
customShape.canvas.processGuides(e, ui, customShape);
}
customShape.setPosition(ui.helper.position().left / canvas.zoomFactor,
ui.helper.position().top / canvas.zoomFactor);
diffX = customShape.x - customShape.previousXDragPosition;
diffY = customShape.y - customShape.previousYDragPosition;
customShape.previousXDragPosition = customShape.x;
customShape.previousYDragPosition = customShape.y;
for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
sibling = customShape.canvas.currentSelection.get(i);
if (sibling.id !== customShape.id) {
if (((sibling.x + diffX) >= 0) && ((sibling.y + diffY) >= 0)){
sibling.setPosition(sibling.x + diffX, sibling.y + diffY);
} else {
e.preventDefault();
}
}
}
} else {
customShape.setPosition(customShape.x, customShape.y);
}
// children
if (root) {
for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
sibling = customShape.canvas.currentSelection.get(i);
for (j = 0; j < sibling.children.getSize(); j += 1) {
child = sibling.children.get(j);
PMUI.behavior.CustomShapeDragBehavior.prototype.onDragProcedure.call(this, child,
false, diffX, diffY, e, ui);
}
}
} else {
for (i = 0; i < customShape.children.getSize(); i += 1) {
child = customShape.children.get(i);
PMUI.behavior.CustomShapeDragBehavior.prototype.onDragProcedure.call(this, child,
false, childDiffX, childDiffY, e, ui);
}
}
// connections
if (root) {
for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
sibling = customShape.canvas.currentSelection.get(i);
for (j = 0; j < sibling.ports.getSize(); j += 1) {
//for each port update its absolute position and repaint its connection
port = sibling.ports.get(j);
connection = port.connection;
port.setPosition(port.x, port.y);
if (customShape.canvas.sharedConnections.
find('id', connection.getID())) {
// move the segments of this connections
if (connection.srcPort.parent.getID() === sibling.getID()) {
// to avoid moving the connection twice
// (two times per shape), move it only if the shape
// holds the sourcePort
connection.move(diffX * canvas.zoomFactor,
diffY * canvas.zoomFactor);
}
} else {
connection
.setSegmentColor(PMUI.util.Color.GREY, false)
.setSegmentStyle("regular", false)
.disconnect()
.connect();
}
}
}
} else {
for (i = 0; i < customShape.ports.getSize(); i += 1) {
//for each port update its absolute position and repaint its connection
port = customShape.ports.get(i);
connection = port.connection;
shape1 = connection.srcPort.parent;
shape2 = connection.destPort.parent;
port.setPosition(port.x, port.y);
if (customShape.canvas.sharedConnections.
find('id', connection.getID())) {
// to avoid moving the connection twice
// (two times per shape), move it only if the shape
// holds the sourcePort
if (connection.srcPort.parent.getID() ===
customShape.getID()) {
connection.move(childDiffX * canvas.zoomFactor,
childDiffY * canvas.zoomFactor);
}
} else {
connection
.setSegmentColor(PMUI.util.Color.GREY, false)
.setSegmentStyle("regular", false)
.disconnect()
.connect();
}
}
}
};
/**
* On drag handler, calls the drag procedure while the dragging is occurring,
* and also takes care of the snappers
* @param {PMUI.draw.CustomShape} customShape shape being dragged
* @param {boolean} root return whether this is the shape where the drag started
* @param {number} childDiffX x distance needed for the non-root shapes to move
* @param {number} childDiffY y distance needed for the non-root shapes to move
* @return {Function}
*/
CustomShapeDragBehavior.prototype.onDrag = function (customShape, root, childDiffX, childDiffY) {
var self = this;
return function (e, ui) {
// call to dragEnd procedure
self.onDragProcedure(customShape, root, childDiffX,
childDiffY, e, ui);
};
};
/**
* Procedure executed on drag end, it takes care of multiple drag, moving
* connections, updating positions and children of the shapes being dragged
* @param {PMUI.draw.CustomShape} customShape shape being dragged
* @param {boolean} root return whether this is the shape where the drag started
* @param {Object} e jQuery object containing the properties when a drag event
* occur
* @param {Object} ui JQuery UI object containing the properties when a drag
* event occur
*/
CustomShapeDragBehavior.prototype.dragEndProcedure = function (customShape, root, e, ui) {
var i,
j,
sibling,
port,
child,
connection,
shape1,
shape2,
canvas = customShape.canvas;
// shapes
if (root) {
// the difference between this segment of code and the segment of code
// found in dragProcedure is that it's not needed to move the shapes
// anymore using differentials
customShape.setPosition(ui.helper.position().left / canvas.zoomFactor,
ui.helper.position().top / canvas.zoomFactor);
customShape.wasDragged = true;
customShape.canvas.isDragging = false;
for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
sibling = customShape.canvas.currentSelection.get(i);
sibling.setPosition(sibling.x, sibling.y);
}
} else {
customShape.setPosition(customShape.x, customShape.y);
}
// children
if (root) {
for (i = 0; i < customShape.canvas.currentSelection.getSize();
i += 1) {
sibling = customShape.canvas.currentSelection.get(i);
for (j = 0; j < sibling.children.getSize(); j += 1) {
child = sibling.children.get(j);
child.changedContainer = true;
PMUI.behavior.CustomShapeDragBehavior.prototype.dragEndProcedure.call(this,
child, false, e, ui);
}
}
} else {
for (i = 0; i < customShape.children.getSize(); i += 1) {
child = customShape.children.get(i);
PMUI.behavior.CustomShapeDragBehavior.prototype.dragEndProcedure.call(this,
child, false, e, ui);
}
}
// connections
if (root) {
for (i = 0; i < customShape.canvas.currentSelection.getSize();
i += 1) {
sibling = customShape.canvas.currentSelection.get(i);
for (j = 0; j < sibling.ports.getSize(); j += 1) {
// for each port update its absolute position and repaint
// its connection
port = sibling.ports.get(j);
connection = port.connection;
port.setPosition(port.x, port.y);
if (customShape.canvas.sharedConnections.
find('id', connection.getID())) {
// move the segments of this connections
if (connection.srcPort.parent.getID() ===
sibling.getID()) {
// to avoid moving the connection twice
// (two times per shape), move it only if the shape
// holds the sourcePort
connection.disconnect(true).connect({
algorithm: 'user',
points: connection.points,
dx: parseFloat($(connection.html).css('left')),
dy: parseFloat($(connection.html).css('top'))
});
connection.checkAndCreateIntersectionsWithAll();
}
} else {
connection
.setSegmentColor(connection.originalSegmentColor, false)
.setSegmentStyle(connection.originalSegmentStyle, false)
.disconnect()
.connect();
connection.setSegmentMoveHandlers();
connection.checkAndCreateIntersectionsWithAll();
}
}
}
} else {
for (i = 0; i < customShape.ports.getSize(); i += 1) {
//for each port update its absolute position and repaint
//its connection
port = customShape.ports.get(i);
connection = port.connection;
shape1 = connection.srcPort.parent;
shape2 = connection.destPort.parent;
port.setPosition(port.x, port.y);
if (customShape.canvas.sharedConnections.
find('id', connection.getID())) {
// to avoid moving the connection twice
// (two times per shape), move it only if the shape
// holds the sourcePort
if (connection.srcPort.parent.getID() ===
customShape.getID()) {
connection.checkAndCreateIntersectionsWithAll();
}
} else {
connection
.setSegmentColor(connection.originalSegmentColor, false)
.setSegmentStyle(connection.originalSegmentStyle, false)
.disconnect()
.connect();
connection.setSegmentMoveHandlers();
connection.checkAndCreateIntersectionsWithAll();
}
}
}
};
/**
* On drag end handler, ot calls drag end procedure, removes the snappers and,
* fires the command move if necessary
* @param {PMUI.draw.CustomShape} customShape
* @return {Function}
*/
CustomShapeDragBehavior.prototype.onDragEnd = function (customShape) {
var command,
self = this;
return function (e, ui) {
// call to dragEnd procedure
self.dragEndProcedure(customShape, true, e, ui);
customShape.dragging = false;
// hide the snappers
customShape.canvas.verticalSnapper.hide();
customShape.canvas.horizontalSnapper.hide();
if (!customShape.changedContainer) {
command = new PMUI.command.CommandMove(customShape.canvas.currentSelection);
command.execute();
customShape.canvas.commandStack.add(command);
}
customShape.changedContainer = false;
// decrease the zIndex of the oldParent of customShape
customShape.decreaseParentZIndex(customShape.oldParent);
customShape.canvas.emptyCurrentSelection();
};
};
PMUI.extendNamespace('PMUI.behavior.CustomShapeDragBehavior', CustomShapeDragBehavior);
if (typeof exports !== 'undefined') {
module.exports = CustomShapeDragBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.RegularDragBehavior
* Class that encapsulates the regular drag behavior of a shape
* @extends PMUI.behavior.DragBehavior
*
* @constructor Creates a new instance of the class
*
*/
var RegularDragBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.DragBehavior', RegularDragBehavior);
/**
* Type of the object
* @property {String}
*/
RegularDragBehavior.prototype.type = "RegularDragBehavior";
/**
* Attach the drag behavior to a given shape
* @param {PMUI.draw.Shape} shape
*/
RegularDragBehavior.prototype.attachDragBehavior = function (shape) {
var $shape = $(shape.getHTML());
RegularDragBehavior.superclass.prototype.attachDragBehavior.call(this, shape);
$shape.draggable({'cursor':"move"});
};
/**
* On drag start handler, initializes everything that is needed for a shape to
* be dragged
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
RegularDragBehavior.prototype.onDragStart = function (shape) {
return function (e, ui) {
var canvas = shape.canvas,
currentLabel = canvas.currentLabel,
selectedShape,
i;
// hide the current connection if there was one
canvas.hideCurrentConnection();
if (currentLabel) {
currentLabel.loseFocus();
$(currentLabel.textField).focusout();
}
// sort the data of the snappers (a shape might have been inserted in
// the customShape arrayList or the regularShapes)
canvas.fixSnapData();
if (!canvas.currentSelection.contains(shape)) {
canvas.emptyCurrentSelection();
/* Also decreases the z-index */
canvas.addToSelection(shape);
}
// added by mauricio
// these lines must be here and not in the top (currentSelection
// is updated in the if above)
for (i = 0; i < canvas.currentSelection.getSize(); i += 1) {
selectedShape = canvas.currentSelection.get(i);
selectedShape.setOldX(selectedShape.getX());
selectedShape.setOldY(selectedShape.getY());
selectedShape.setOldParent(selectedShape.getParent());
}
// increase shape's ancestors zIndex
shape.increaseParentZIndex(shape.getParent());
shape.canvas.isDragging = true;
return true;
};
};
/**
* On drag handler, sets the position of the shape to current position of the
* shape in the screen
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
RegularDragBehavior.prototype.onDrag = function (shape) {
return function (e, ui) {
shape.setPosition(ui.helper.position().left,
ui.helper.position().top);
// show or hide the snappers
shape.canvas.showOrHideSnappers(shape);
};
};
/**
* On drag end handler, set the final position of the shape and fires the
* command move
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
RegularDragBehavior.prototype.onDragEnd = function (shape) {
return function (e, ui) {
var command;
shape.setPosition(ui.helper.position().left / shape.canvas.zoomFactor,ui.helper.position().top / shape.canvas.zoomFactor);
// decrease the zIndex of the oldParent of this shape
shape.decreaseParentZIndex(shape.oldParent);
shape.dragging = false;
shape.canvas.isDragging = false;
// hide the snappers
shape.canvas.verticalSnapper.hide();
shape.canvas.horizontalSnapper.hide();
if (!shape.changedContainer) {
command = new PMUI.command.CommandMove(shape.canvas.currentSelection);
command.execute();
shape.canvas.commandStack.add(command);
}
shape.changedContainer = false;
};
};
PMUI.extendNamespace('PMUI.behavior.RegularDragBehavior', RegularDragBehavior);
if (typeof exports !== 'undefined') {
module.exports = RegularDragBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.ConnectionDragBehavior
* Class that encapsulates the behavior for a connection drag.
* A connection drag behavior means that instead of moving a shape when dragging
* occurs, it creates a connection segment that let's us connect to shapes
* @extends PMUI.behavior.DragBehavior
*
* @constructor Creates a new instance of the class
*
*/
var ConnectionDragBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.DragBehavior', ConnectionDragBehavior);
/**
* Type of the instances
* @property {String}
*/
ConnectionDragBehavior.prototype.type = "ConnectionDragBehavior";
/**
* Attach the drag behavior and ui properties to the corresponding shape
* @param {PMUI.draw.Shape} shape
*/
ConnectionDragBehavior.prototype.attachDragBehavior = function (shape) {
var $shape = $(shape.getHTML()),
dragOptions;
dragOptions = {
helper: shape.createDragHelper,
cursorAt: {top: 0, left: 0},
revert: true
};
ConnectionDragBehavior.superclass.prototype.attachDragBehavior.call(this, shape);
$shape.draggable(dragOptions);
$shape.draggable('enable');
};
/**
* On drag start handler, initializes all properties needed to start a
* connection drag
* @param {PMUI.draw.CustomShape} customShape
* @return {Function}
*/
ConnectionDragBehavior.prototype.onDragStart = function (customShape) {
return function (e, ui) {
var canvas = customShape.canvas,
currentLabel = canvas.currentLabel,
realPoint = canvas.relativePoint(e),
startPortX = e.pageX - customShape.getAbsoluteX(),
startPortY = e.pageY - customShape.getAbsoluteY();
customShape.startConnectionPoint.portX = startPortX;
customShape.startConnectionPoint.portY = startPortY;
customShape.canvas.emptyCurrentSelection();
if (currentLabel) {
currentLabel.loseFocus();
$(currentLabel.textField).focusout();
}
if (customShape.family !== "CustomShape") {
return false;
}
customShape.setOldX(customShape.getX());
customShape.setOldY(customShape.getY());
customShape.startConnectionPoint.x = customShape.canvas.zoomFactor * realPoint.x;
customShape.startConnectionPoint.y = customShape.canvas.zoomFactor * realPoint.y;
return true;
};
};
/**
* On drag handler, creates a connection segment from the shape to the current
* mouse position
* @param {PMUI.draw.CustomShape} customShape
* @return {Function}
*/
ConnectionDragBehavior.prototype.onDrag = function (customShape) {
return function (e, ui) {
var canvas = customShape.getCanvas(),
endPoint = new PMUI.util.Point(),
realPoint = canvas.relativePoint(e);
if (canvas.connectionSegment) {
$(canvas.connectionSegment.getHTML()).remove();
}
// Determine the point where the mouse currently is
endPoint.x = realPoint.x * customShape.canvas.zoomFactor;
endPoint.y = realPoint.y * customShape.canvas.zoomFactor;
// Creates a new segment from where the helper was created to the
// currently mouse location
canvas.connectionSegment = new PMUI.draw.Segment({
startPoint: customShape.startConnectionPoint,
endPoint: endPoint,
parent: canvas,
zOrder: PMUI.util.Style.MAX_ZINDEX * 2
});
// We make the connection segment point to helper in order to get
// information when the drop occurs
canvas.connectionSegment.pointsTo = customShape;
// Create HTML and paint
canvas.connectionSegment.paint();
};
};
/**
* On drag end handler, deletes the connection segment created while dragging
* @param {PMUI.draw.CustomShape} customShape
* @return {Function}
*/
ConnectionDragBehavior.prototype.onDragEnd = function (customShape) {
return function (e, ui) {
if (customShape.canvas.connectionSegment) {
$(customShape.canvas.connectionSegment.getHTML()).remove();
}
customShape.setPosition(customShape.getOldX(), customShape.getOldY());
customShape.dragging = false;
};
};
PMUI.extendNamespace('PMUI.behavior.ConnectionDragBehavior', ConnectionDragBehavior);
if (typeof exports !== 'undefined') {
module.exports = ConnectionDragBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.NoDragBehavior
* Class that encapsulates the drag behavior corresponding to the elements that
* cannot be dragged
* @extends PMUI.behavior.DragBehavior
*
* @constructor Creates a new instance of the class
*
*/
var NoDragBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.DragBehavior', NoDragBehavior);
/**
* Type of the instances
* @property {String}
*/
NoDragBehavior.prototype.type = "NoDragBehavior";
/**
* On drag start handler, this method prevents drag from occurring
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
NoDragBehavior.prototype.onDragStart = function (shape) {
// hide the current connection if there was one
return function (e, ui) {
shape.canvas.hideCurrentConnection();
return false;
};
};
PMUI.extendNamespace('PMUI.behavior.NoDragBehavior', NoDragBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = NoDragBehavior;
}
}());
(function () {
/**
* @abstract
* @class PMUI.behavior.DropBehavior
* Abstract class where all the drop behavior classes inherit from
* Strategy Pattern
* @constructor
* Creates a new instance of the class
* @param {Object} [options] css selectors that the drop behavior
* will accept
*/
var DropBehavior = function (options) {
/**
* css selectors that the used for the drop behaviors beside the defaults
* @property {Array}
*/
this.selectors = [];
DropBehavior.prototype.init.call(this, options);
};
/**
* Type of the instances
* @property {String}
*/
DropBehavior.prototype.type = "DropBehavior";
/**
* Family of the instances
* @property {String}
*/
DropBehavior.prototype.family = "DropBehavior";
/**
* Default css selectors for the drop behavior
* @property {String}
*/
DropBehavior.prototype.defaultSelector = "";
/**
* @private
* Initializes the object with the default options
* @param {[type]} options
*/
DropBehavior.prototype.init = function (options) {
var defaults = {
selectors: []
};
jQuery.extend(true, defaults, options);
this.setSelectors(defaults.selectors);
};
/**
* Attach the drop behaviors and assign the handlers to the corresponding shape
* @param {PMUI.draw.Shape} shape
*/
DropBehavior.prototype.attachDropBehavior = function (shape) {
var $shape = $(shape.getHTML()),
dropOptions = {
accept: this.defaultSelector,
drop: this.onDrop(shape),
over: this.onDragEnter(shape),
out: this.onDragLeave(shape),
greedy: true
};
$shape.droppable(dropOptions);
};
/**
* @event dragEnter
* @abstract Handler for the drag enter event
* @param {PMUI.draw.Shape} shape
* @template
* @protected
*/
DropBehavior.prototype.onDragEnter = function (shape) {
return function (e, ui) {
};
};
/**
* @event dragLeave
* @abstract Handler for the drag leave event
* @param {PMUI.draw.Shape} shape
* @template
* @protected
*/
DropBehavior.prototype.onDragLeave = function (shape) {
return function (e, ui) {
};
};
/**
* @event drop
* @abstract Handler for the on drop event
* @param {PMUI.draw.Shape} shape
* @template
* @protected
*/
DropBehavior.prototype.onDrop = function (shape) {
return function (e, ui) {
};
};
/**
* Sets the selectors that the drop behavior will accept
* @param {Array} selectors css selectors
* @param {boolean} overwrite determines whether the default selectors will be
* overridden or not
* @chainable
*/
DropBehavior.prototype.setSelectors = function (selectors, overwrite) {
var currentSelectors = "",
index,
i;
if (selectors) {
this.selectors = selectors;
}
if (!overwrite) {
currentSelectors = this.defaultSelector;
index = 0;
} else if (selectors.length > 0) {
currentSelectors = selectors[0];
index = 1;
}
for (i = index; i < selectors.length; i += 1) {
currentSelectors += "," + this.selectors[i];
}
return this;
};
/**
* Updates the accepted drop selectors
* @param {PMUI.draw.Shape} shape
* @param {Array} selectors
* @chainable
*/
DropBehavior.prototype.updateSelectors = function (shape, selectors) {
var $shape = $(shape.getHTML()),
currentSelectors,
i;
if (selectors) {
this.selectors = selectors;
}
if (this.selectors.length > 0) {
currentSelectors = this.selectors[0];
}
for (i = 1; i < this.selectors.length; i += 1) {
currentSelectors += ',' + this.selectors[i];
}
$shape.droppable({"accept": currentSelectors});
return this;
};
/**
* Hook for the drag enter handler
* @template
* @protected
*/
DropBehavior.prototype.dragEnterHook = function () {
return true;
};
/**
* Hook for the drag leave handler
* @template
* @protected
*/
DropBehavior.prototype.dragLeaveHook = function () {
return true;
};
/**
* Hook for the drop handler, executes before the on drop handler logic
* @param {PMUI.draw.Shape} shape
* @param {Object} e jQuery object that contains the properties on the
* drop event
* @param {Object} ui jQuery object that contains the properties on the
* drop event
* @template
* @protected
*/
DropBehavior.prototype.dropStartHook = function (shape, e, ui) {
return true;
};
/**
* Hook for the on drop handler
* @param {PMUI.draw.Shape} shape
* @param {Object} e jQuery object that contains the properties on the
* drop event
* @param {Object} ui jQuery object that contains the properties on the
* drop event
* @template
* @protected
*/
DropBehavior.prototype.dropHook = function (shape, e, ui) {
return true;
};
/**
* Hook for the on drop handler, executes after the drop logic has concluded
* @param {PMUI.draw.Shape} shape
* @param {Object} e jQuery object that contains the properties on the
* drop event
* @param {Object} ui jQuery object that contains the properties on the
* drop event
* @template
* @protected
*/
DropBehavior.prototype.dropEndHook = function (shape, e, ui) {
return true;
};
PMUI.extendNamespace('PMUI.behavior.DropBehavior', DropBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = DropBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.ConnectionDropBehavior
* Class that encapsulates the drop behavior for dropped connections in shapes
* @extends PMUI.behavior.DropBehavior
*
* @constructor
* Creates a new instance of the class
* @param selectors
*/
var ConnectionDropBehavior = function (selectors) {
ConnectionDropBehavior.superclass.call(this, selectors);
};
PMUI.inheritFrom('PMUI.behavior.DropBehavior', ConnectionDropBehavior);
/**
* Type of the instances
* @property {String}
*/
ConnectionDropBehavior.prototype.type = "ConnectionDropBehavior";
/**
* Defaults selectors for this drop behavior
* @property {String}
*/
ConnectionDropBehavior.prototype.defaultSelector = ".custom_shape,.port";
/**
* Sets the selectors for this drop behavior including the defaults
* @param selectors
* @param overwrite
* @return {*}
*/
ConnectionDropBehavior.prototype.setSelectors = function (selectors, overwrite) {
ConnectionDropBehavior.superclass.prototype.setSelectors.call(this, selectors, overwrite);
this.selectors.push(".port");
this.selectors.push(".custom_shape");
return this;
};
/**
* Drag enter hook for this drop behavior, marks that a shape is over a
* droppable element
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
ConnectionDropBehavior.prototype.onDragEnter = function (shape) {
return function (e, ui) {
shape.entered = true;
};
};
/**
* Drag leave hook for this drop behavior, marks that a shape has left a
* droppable element
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
ConnectionDropBehavior.prototype.onDragLeave = function (shape) {
return function (e, ui) {
shape.entered = false;
};
};
/**
* On drop handler for this drop behavior, creates a connection between the
* droppable element and the dropped element, or move ports among those shapes
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
ConnectionDropBehavior.prototype.onDrop = function (shape) {
var that = this;
return function (e, ui) {
var canvas = shape.getCanvas(),
id = ui.draggable.attr('id'),
x,
y,
currLeft,
currTop,
startPoint,
sourceShape,
sourcePort,
realPoint,
endPort,
endPortXCoord,
endPortYCoord,
connection,
currentConnection = canvas.currentConnection,
srcPort,
dstPort,
port,
success = false,
command;
shape.entered = false;
if (!shape.dropBehavior.dropStartHook(shape, e, ui)) {
return false;
}
if (shape.getConnectionType() === "none") {
return true;
}
if (currentConnection) {
srcPort = currentConnection.srcPort;
dstPort = currentConnection.destPort;
if (srcPort.id === id) {
port = srcPort;
} else if (dstPort.id === id) {
port = dstPort;
} else {
port = null;
}
}
if (ui.helper && ui.helper.attr('id') === "drag-helper") {
// If its the helper then we need to create two ports and draw a
// connection.
// We get the points and the corresponding shapes involved
startPoint = shape.canvas.connectionSegment.startPoint;
sourceShape = shape.canvas.connectionSegment.pointsTo;
// Determine the points where the helper was created
if (sourceShape.parent && sourceShape.parent.id === shape.id) {
return true;
}
sourceShape.setPosition(sourceShape.oldX, sourceShape.oldY);
// Create the ports
sourcePort = new PMUI.draw.Port({
width: 8,
height: 8
});
endPort = new PMUI.draw.Port({
width: 8,
height: 8
});
realPoint = shape.canvas.relativePoint(e);
// Determine the position where the helper was dropped
endPortXCoord = ui.offset.left - shape.canvas.getX() -
shape.getAbsoluteX() + shape.canvas.getLeftScroll();
endPortYCoord = ui.offset.top - shape.canvas.getY() -
shape.getAbsoluteY() + shape.canvas.getTopScroll();
// Add ports to the corresponding shapes
// AddPort() determines the position of the ports
sourceShape.addPort(sourcePort, startPoint.x, startPoint.y);
shape.addPort(endPort, endPortXCoord, endPortYCoord,
false, sourcePort);
// Create the connection
connection = new PMUI.draw.Connection({
srcPort: sourcePort,
destPort: endPort,
canvas: shape.canvas,
segmentStyle: shape.connectionType
});
// Set its decorators
connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
width: 11,
height: 11,
canvas: canvas,
decoratorPrefix: "con_normal",
decoratorType: "source",
parent: connection
}));
connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
width: 11,
height: 11,
canvas: canvas,
decoratorPrefix: "con_normal",
decoratorType: "target",
parent: connection
}));
connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));
// Connect the two ports
connection.connect();
connection.setSegmentMoveHandlers();
// Add the connection to the canvas, that means insert its html to
// the DOM and adding it to the connections array
canvas.addConnection(connection);
// Now that the connection was drawn try to create the intersections
connection.checkAndCreateIntersectionsWithAll();
// Attaching port listeners
sourcePort.attachListeners(sourcePort);
endPort.attachListeners(endPort);
// Finally trigger createEvent
canvas.triggerCreateEvent(connection, []);
} else if (port) {
port.setOldParent(port.getParent());
port.setOldX(port.getX());
port.setOldY(port.getY());
x = ui.position.left;
y = ui.position.top;
port.setPosition(x, y);
shape.dragging = false;
if (shape.getID() !== port.parent.getID()) {
port.parent.removePort(port);
currLeft = ui.offset.left - canvas.getX() -
shape.absoluteX + shape.canvas.getLeftScroll();
currTop = ui.offset.top - canvas.getY() -
shape.absoluteY + shape.canvas.getTopScroll();
shape.addPort(port, currLeft, currTop, true);
canvas.regularShapes.insert(port);
} else {
shape.definePortPosition(port, port.getPoint(true));
}
// LOGIC: when portChangeEvent is triggered it gathers the state
// of the connection but since at this point there's only a segment
// let's paint the connection, gather the state and then disconnect
// it (the connection is later repainted on, I don't know how)
port.connection.connect();
canvas.triggerPortChangeEvent(port);
port.connection.disconnect();
command = new PMUI.command.CommandReconnect(port);
port.canvas.commandStack.add(command);
}
return false;
};
};
PMUI.extendNamespace('PMUI.behavior.ConnectionDropBehavior', ConnectionDropBehavior);
}());
(function () {
/**
* @class PMUI.behavior.ContainerDropBehavior
* Encapsulates the drop behavior of a container
* @extends PMUI.behavior.DropBehavior
*
* @constructor
* Creates a new instance of the class
* @param {Array} [selectors=[]] css selectors that this drop behavior will
* accept
*/
var ContainerDropBehavior = function (selectors) {
ContainerDropBehavior.superclass.call(this, selectors);
};
PMUI.inheritFrom('PMUI.behavior.DropBehavior', ContainerDropBehavior);
/**
* Type of the instances
* @property {String}
*/
ContainerDropBehavior.prototype.type = "ContainerDropBehavior";
/**
* Default selectors for this drop behavior
* @property {String}
*/
ContainerDropBehavior.prototype.defaultSelector = ".custom_shape";
/**
* On drop handler for this drop behavior, creates shapes when dropped from the
* toolbar, or move shapes among containers
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
ContainerDropBehavior.prototype.onDrop = function (shape) {
return function (e, ui) {
var customShape = null,
canvas = shape.getCanvas(),
selection,
sibling,
i,
command,
coordinates,
id,
shapesAdded = [],
containerBehavior = shape.containerBehavior;
if (canvas.readOnly) {
return false;
}
shape.entered = false;
if (ui.helper && ui.helper.attr('id') === "drag-helper") {
return false;
}
id = ui.draggable.attr('id');
customShape = canvas.shapeFactory(id);
if (customShape === null) {
customShape = canvas.customShapes.find('id', id);
if (!customShape || !shape.dropBehavior.dropHook(shape, e, ui)) {
return false;
}
if (!(customShape.parent &&
customShape.parent.id === shape.id)) {
selection = canvas.currentSelection;
for (i = 0; i < selection.getSize(); i += 1) {
sibling = selection.get(i);
coordinates = PMUI.getPointRelativeToPage(sibling);
coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, null,
coordinates.x, coordinates.y, customShape);
shapesAdded.push({
shape: sibling,
container: shape,
x: coordinates.x,
y: coordinates.y,
topLeft: false
});
}
command = new PMUI.command.CommandSwitchContainer(shapesAdded);
command.execute();
canvas.commandStack.add(command);
canvas.multipleDrop = true;
}
// Fix resize minWidth and minHeight and also fix the dimension
// of this shape (if a child made it grow)
shape.updateDimensions(10);
canvas.updatedElement = null;
} else {
coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, e,null,null,customShape);
if (PMUI.validCoordinatedToCreate(shape, e, customShape)) {
shape.addElement(customShape, coordinates.x, coordinates.y,
customShape.topLeftOnCreation);
// Since it is a new element in the designer, we triggered the
// custom on create element event
canvas.updatedElement = customShape;
// Create the command for this new shape
command = new PMUI.command.CommandCreate(customShape);
canvas.commandStack.add(command);
command.execute();
canvas.hideAllFocusLabels();
if (customShape.label) {
customShape.label.getFocus();
}
}
}
};
};
PMUI.extendNamespace('PMUI.behavior.ContainerDropBehavior', ContainerDropBehavior);
}());
(function () {
/**
* @class PMUI.behavior.ConnectionContainerDropBehavior
* Class that encapsulates the drop behaviors for containers that can also be
* connected
* @extends PMUI.behavior.DropBehavior
*
* @constructor
* Creates a new instance of the class
* @param {Array} [selectors=[]] css selectors that this drop behavior will
* accept
*/
var ConnectionContainerDropBehavior = function (selectors) {
ConnectionContainerDropBehavior.superclass.call(this, selectors);
};
PMUI.inheritFrom('PMUI.behavior.DropBehavior', ConnectionContainerDropBehavior);
/**
* Type of the instances
* @property {String}
*/
ConnectionContainerDropBehavior.prototype.type = "ConnectionContainerDropBehavior";
/**
* Default selectors for this drop behavior
* @property {String}
*/
ConnectionContainerDropBehavior.prototype.defaultSelector = ".custom_shape,.port";
/**
* Set the selectors for this drop behavior including the default selectors
* @param {Array} selectors css selectors
* @param {boolean} overwrite
* @return {*}
*/
ConnectionContainerDropBehavior.prototype.setSelectors = function (selectors, overwrite) {
ConnectionContainerDropBehavior.superclass.prototype
.setSelectors.call(this, selectors, overwrite);
this.selectors.push(".port");
this.selectors.push(".custom_shape");
return this;
};
/**
* On drop handler for this drop behavior, determines whether to create a
* connection or add a shape to the container that is using this drop behavior
* @param {PMUI.draw.Shape} shape
* @return {Function}
*/
ConnectionContainerDropBehavior.prototype.onDrop = function (shape) {
return function (e, ui) {
if (!PMUI.behavior.ConnectionDropBehavior.prototype.onDrop.call(this, shape)(e, ui)) {
PMUI.behavior.ContainerDropBehavior.prototype.onDrop.call(this, shape)(e, ui);
}
};
};
PMUI.extendNamespace('PMUI.behavior.ConnectionContainerDropBehavior',
ConnectionContainerDropBehavior);
}());
(function () {
/**
* @class PMUI.behavior.NoDropBehavior
* Encapsulates the drop behavior representing an object that can't be droppable
* @extends PMUI.behavior.DropBehavior
*
* @constructor
* Creates a new instance of the class
*/
var NoDropBehavior = function (selectors) {
NoDropBehavior.superclass.call(this, selectors);
};
PMUI.inheritFrom('PMUI.behavior.DropBehavior', NoDropBehavior);
/**
* Type of the instances
* @property {String}
*/
NoDropBehavior.prototype.type = "NoDropBehavior";
/**
* Attach the drop behavior, sets the accepted elements to none
* @param {PMUI.draw.Shape} shape
*/
NoDropBehavior.prototype.attachDropBehavior = function (shape) {
//var $shape = $(shape.getHTML());
//NoDropBehavior.superclass.prototype.attachDropBehavior.call(this, shape);
//$(shape).droppable({ accept: "" });
};
PMUI.extendNamespace('PMUI.behavior.NoDropBehavior', NoDropBehavior);
if (typeof exports !== 'undefined') {
module.exports = NoDropBehavior;
}
}());
(function () {
/**
* @abstract
* @class PMUI.behavior.ContainerBehavior
* Object that encapsulates the container of shapes, this is an abstract class,
* so all its methods should be implemented by its subclasses
* @constructor
* Creates a new instance of the class
*/
var ContainerBehavior = function () {
};
/**
* Type of the instances
* @property {String}
*/
ContainerBehavior.prototype.type = "ContainerBehavior";
/**
* Family of the instances
* @property {String}
*/
ContainerBehavior.prototype.family = "ContainerBehavior";
/**
* @abstract
* Sets a shape's container to a given container
* @param {PMUI.draw.BehavioralElement} container element using this behavior
* @param {PMUI.draw.Shape} shape shape to be added
* @template
* @protected
*/
ContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
};
/**
* @abstract
* Removes shape from its current container
* @param {PMUI.draw.Shape} shape shape to be removed
* @template
* @protected
*/
ContainerBehavior.prototype.removeFromContainer = function (shape) {
};
/**
* @abstract
* Adds a shape to a given container
* @param {PMUI.draw.BehavioralElement} container container element using this behavior
* @param {PMUI.draw.Shape} shape shape to be added to the container
* @template
* @protected
*/
ContainerBehavior.prototype.addShape = function (container, shape, x, y) {
};
/**
* Returns whether a shape is a container or not
* @return {boolean}
*/
ContainerBehavior.prototype.isContainer = function () {
return false;
};
PMUI.extendNamespace('PMUI.behavior.ContainerBehavior', ContainerBehavior);
if (typeof exports !== 'undefined') {
module.exports = ContainerBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.RegularContainerBehavior
* Encapsulates the behavior of a regular container
* @extends PMUI.behavior.ContainerBehavior
*
* @constructor
* Creates a new instance of the class
*/
var RegularContainerBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.ContainerBehavior', RegularContainerBehavior);
/**
* Type of the instances
* @property {String}
*/
RegularContainerBehavior.prototype.type = "RegularContainerBehavior";
/**
* Adds a shape to a given container given its coordinates
* @param {PMUI.draw.BehavioralElement} container container using this behavior
* @param {PMUI.draw.Shape} shape shape to be added
* @param {number} x x coordinate where the shape will be added
* @param {number} y y coordinate where the shape will be added
* @param {boolean} topLeftCorner Determines whether the x and y coordinates
* will be considered from the top left corner or from the center
*/
RegularContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
var shapeLeft = 0,
shapeTop = 0,
shapeWidth,
shapeHeight,
canvas,
topLeftFactor = (topLeftCorner === true) ? 0 : 1;
if (container.family === "Canvas") {
canvas = container;
} else {
canvas = container.canvas;
}
shapeWidth = shape.getZoomWidth();
shapeHeight = shape.getZoomHeight();
shapeLeft += x - (shapeWidth / 2) * topLeftFactor;
shapeTop += y - (shapeHeight / 2) * topLeftFactor;
shapeLeft /= canvas.zoomFactor;
shapeTop /= canvas.zoomFactor;
shape.setParent(container);
container.getChildren().insert(shape);
this.addShape(container, shape, shapeLeft, shapeTop);
// fix the zIndex of this shape and it's children
shape.fixZIndex(shape, 0);
// fix resize minWidth and minHeight and also fix the dimension
// of this shape (if a child made it grow)
container.updateDimensions(10);
// adds the shape to either the customShape arrayList or the regularShapes
// arrayList if possible
canvas.addToList(shape);
};
/**
* Removes a shape from the container implementing this behavior
* @param {PMUI.draw.Shape} shape shape to be removed
*/
RegularContainerBehavior.prototype.removeFromContainer = function (shape) {
var parent = shape.parent;
parent.getChildren().remove(shape);
if (parent.isResizable()) {
parent.resizeBehavior.updateResizeMinimums(shape.parent);
}
shape.parent = null;
};
/**
* Sets the position of the shape, and append its html
* @param {PMUI.draw.BehavioralElement} container element implementing this behavior
* @param {PMUI.draw.Shape} shape shape added to the container
* @param {number} x x coordinate of the position that will be set relative to
* the container
* @param {number} y y coordinate of the position that will be set relative to
* the container
* @chainable
*/
RegularContainerBehavior.prototype.addShape = function (container, shape, x, y) {
shape.setPosition(x, y);
//insert the shape HTML to the DOM
container.getHTML().appendChild(shape.getHTML());
shape.updateHTML();
shape.paint();
shape.applyBehaviors();
shape.attachListeners();
return this;
};
PMUI.extendNamespace('PMUI.behavior.RegularContainerBehavior', RegularContainerBehavior);
if (typeof exports !== 'undefined') {
module.exports = RegularContainerBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.NoContainerBehavior
* Encapsulates the behavior of elements that has no container behavior, useful
* for implementing the strategy pattern
* @extends PMUI.behavior.ContainerBehavior
*
*
* @constructor
* Creates a new instance of the class
*/
var NoContainerBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.ContainerBehavior', NoContainerBehavior);
/**
* Type of the instances
* @property {String}
*/
NoContainerBehavior.prototype.type = "NoContainerBehavior";
PMUI.extendNamespace('PMUI.behavior.NoContainerBehavior', NoContainerBehavior);
if (typeof exports !== 'undefined') {
module.exports = NoContainerBehavior;
}
}());
(function () {
/**
* @abstract
* @class PMUI.behavior.ResizeBehavior
* Abstract class which inherited classes' instances are used for delegation of the resize behavior of a shape.
*
* @constructor Creates an instance of the class ResizeBehavior
*/
var ResizeBehavior = function () {
};
/**
* The type of each instance of this class.
* @property {String}
*/
ResizeBehavior.prototype.type = "ResizeBehavior";
/**
* The family of each instance of this class.
* @property {String}
*/
ResizeBehavior.prototype.family = "ResizeBehavior";
/**
* Initialize JQueryUI's resize plugin
* @param {PMUI.draw.Shape} shape
*/
ResizeBehavior.prototype.init = function (shape) {
var $shape = $(shape.getHTML()),
shapeResizeOptions = {
handles: shape.getHandlesIDs(),
disable: false,
start: this.onResizeStart(shape),
resize: this.onResize(shape),
stop: this.onResizeEnd(shape)
};
$shape.resizable(shapeResizeOptions);
};
/**
* @abstract
* @event resizeStart
* Abstract method to be implemented in inherited classes
* @param {PMUI.draw.Shape} shape
*/
ResizeBehavior.prototype.onResizeStart = function (shape) {
};
/**
* @abstract
* @event resize
* Abstract method to be implemented in inherited classes
* @param {PMUI.draw.Shape} shape
*/
ResizeBehavior.prototype.onResize = function (shape) {
};
/**
* @abstract
* @event resizeEnd
* Abstract method to be implemented in inherited classes
* @param {PMUI.draw.Shape} shape
*/
ResizeBehavior.prototype.onResizeEnd = function (shape) {
};
/**
* Updates the minimum height and maximum height of the JQqueryUI's resizable plugin.
* @param {PMUI.draw.Shape} shape
* @chainable
*/
ResizeBehavior.prototype.updateResizeMinimums = function (shape) {
var minW,
minH,
children = shape.getChildren(),
limits = children.getDimensionLimit(),
margin = 15,
$shape = $(shape.getHTML());
// TODO: consider the labels width and height
minW = limits[1] + margin;
minH = limits[2] + margin;
// update jQueryUI's minWidth and minHeight
$shape.resizable();
$shape.resizable('option', 'minWidth', minW);
$shape.resizable('option', 'minHeight', minH);
return this;
};
PMUI.extendNamespace('PMUI.behavior.ResizeBehavior', ResizeBehavior);
if (typeof exports !== 'undefined') {
module.exports = ResizeBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.NoResizeBehavior
* Class that encapsulates the regular resize behavior of a shape when it's not supposed to be resizable
* @extends PMUI.behavior.ResizeBehavior
*
* @constructor Creates a new instance of the class RegularResizeBehavior
*/
var NoResizeBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.ResizeBehavior', NoResizeBehavior);
/**
* The type of each instance of this class.
* @property {String}
*/
NoResizeBehavior.prototype.type = "NoResizeBehavior";
/**
* Initialize JQueryUI's resize plugin (disables the resizable plugin).
* @param {PMUI.draw.Shape} shape
*/
NoResizeBehavior.prototype.init = function (shape) {
var $shape = $(shape.getHTML());
NoResizeBehavior.superclass.prototype.init.call(this, shape);
$shape.resizable('disable');
$shape
.removeClass('ui-state-disabled');
shape.applyStyleToHandlers('nonResizableStyle');
shape.showOrHideResizeHandlers(false);
};
/**
* Overwrites the method {@link PMUI.behavior.ResizeBehavior#updateResizeMinimums} since
* a shape that is not resizable shouldn't update its resize minimums.
* @param {PMUI.draw.Shape} shape
*/
NoResizeBehavior.prototype.updateResizeMinimums = function (shape) {
};
PMUI.extendNamespace('PMUI.behavior.NoResizeBehavior', NoResizeBehavior);
if (typeof exports !== 'undefined') {
module.exports = NoResizeBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.RegularResizeBehavior
* Class that encapsulates the regular resize behavior of a shape
* @extends PMUI.behavior.ResizeBehavior
*
* @constructor Creates a new instance of the class RegularResizeBehavior
*/
var RegularResizeBehavior = function () {
};
PMUI.inheritFrom('PMUI.behavior.ResizeBehavior', RegularResizeBehavior);
/**
* The type of each instance of this class
* @property {String}
*/
RegularResizeBehavior.prototype.type = "RegularResizeBehavior";
/**
* Initialize JQueryUI's resizable plugin
* @param {PMUI.draw.Shape} shape
*/
RegularResizeBehavior.prototype.init = function (shape) {
var $shape = $(shape.getHTML());
RegularResizeBehavior.superclass.prototype.init.call(this, shape);
$shape.resizable('enable');
shape.applyStyleToHandlers('resizableStyle');
// hide its handles (jQueryUI's resizable shows the handles by default)
shape.showOrHideResizeHandlers(false);
};
/**
* @event resizeStart
* ResizeStart event fired when the user resizes a shape.
* It does the following:
*
* - Save old values (for the undo-redo stack)
* - Empties the {@link PMUI.draw.Canvas#property-currentSelection}, and adds `shape` to that arrayList
* - Hides the resize handlers of the shape
*
* @param {PMUI.draw.Shape} shape
*/
RegularResizeBehavior.prototype.onResizeStart = function (shape) {
return function (e, ui) {
shape.resizing = true;
shape.canvas.isResizing = true;
shape.dragging = false;
shape.oldWidth = shape.width;
shape.oldHeight = shape.height;
shape.oldX = shape.x;
shape.oldY = shape.y;
shape.oldAbsoluteX = shape.absoluteX;
shape.oldAbsoluteY = shape.absoluteY;
if (shape.ports) {
shape.initPortsChange();
}
if (shape.canvas.currentSelection.getSize() > 1) {
shape.canvas.emptyCurrentSelection();
shape.canvas.addToSelection(shape);
}
shape.showOrHideResizeHandlers(false);
shape.calculateLabelsPercentage();
shape.canvas.hideAllFocusLabels();
return true;
};
};
/**
* @event resize
* Resize event fired when the user is resizing a shape.
* It does the following:
*
* - Sets the position and dimensions of the shape
* - Fixes the ports of `shape` and from the its children (recursively)
* - Updates the position of its labels
*
* @param {PMUI.draw.Shape} shape
*/
RegularResizeBehavior.prototype.onResize = function (shape) {
return function (e, ui) {
var i,
port,
canvas = shape.canvas;
shape.setPosition(ui.position.left / canvas.zoomFactor,
ui.position.top / canvas.zoomFactor);
shape.setDimension(ui.size.width / canvas.zoomFactor,
ui.size.height / canvas.zoomFactor);
// fix the position of the shape's ports (and the positions and port
// position of its children)
// parameters (shape, resizing, root)
shape.fixConnectionsOnResize(shape.resizing, true);
// fix the labels positions on resize (on x = true and y = true)
shape.updateLabelsPosition();
shape.canvas.hideAllFocusLabels();
};
};
/**
* @event resizeEnd
* ResizeEnd event fired when the user stops resizing a shape.
* It does the following:
*
* - Shows the handlers of `shape`
* - Updates the dimension of its parent (this shape might have outgrown the shape)
* - Creates an instance of {@link PMUI.command.CommandResize} to add it to the undo-redo stack
*
* @param {PMUI.draw.Shape} shape
*/
RegularResizeBehavior.prototype.onResizeEnd = function (shape) {
return function (e, ui) {
var i,
label,
command;
shape.resizing = false;
shape.canvas.isResizing = false;
// last resize
RegularResizeBehavior.prototype.onResize.call(this, shape)(e, ui);
// show the handlers again
shape.showOrHideResizeHandlers(true);
// update the dimensions of the parent if possible (a shape might
// have been resized out of the dimensions of its parent)
shape.parent.updateDimensions(10);
if (shape.ports) {
shape.firePortsChange();
}
// TESTING COMMANDS
command = new PMUI.command.CommandResize(shape);
shape.canvas.commandStack.add(command);
command.execute();
for (i = 0; i < shape.labels.getSize(); i += 1) {
label = shape.labels.get(i);
label.setLabelPosition(label.location, label.diffX, label.diffY);
}
return true;
};
};
PMUI.extendNamespace('PMUI.behavior.RegularResizeBehavior', RegularResizeBehavior);
if (typeof exports !== 'undefined'){
module.exports = RegularResizeBehavior;
}
}());
(function() {
var DataControlDropBehavior = function(settings) {
DataControlDropBehavior.superclass.call(this, settings);
this.targetObject = null;
this.scope = null;
this.hoverClass = null;
DataControlDropBehavior.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.DropBehavior', DataControlDropBehavior);
DataControlDropBehavior.prototype.init = function(settings) {
var defaults = {
scope: 'pmui-datacontrol-dragdrop-scope',
hoverClass: 'pmui-dataControl-drag-over'
};
jQuery.extend(true, defaults, settings);
if(!defaults.targetObject) {
throw new Error("The target object wasn't specified.");
}
this.targetObject = defaults.targetObject;
this.hoverClass = defaults.hoverClass;
this.setScope(defaults.scope);
};
DataControlDropBehavior.prototype.setScope = function(scope) {
if(typeof scope === 'string') {
this.scope = scope;
} else {
throw new Error("setScope(): the parameter must be a string.");
}
return this;
};
DataControlDropBehavior.prototype.attachDropBehavior = function() {
var html = this.targetObject.getHTML();
$(html).droppable({
greedy: true,
scope: this.scope,
hoverClass: this.hoverClass,
drop: this.onDrop(),
over: this.onDragEnter(),
out: this.onDragLeave()
});
return this;
};
DataControlDropBehavior.prototype.onDragLeave = function() {
return (this.targetObject.onDragLeaveHandler && this.targetObject.onDragLeaveHandler()) || null;
};
DataControlDropBehavior.prototype.onDragEnter = function() {
return (this.targetObject.onDragEnterHandler && this.targetObject.onDragEnterHandler()) || null;
};
DataControlDropBehavior.prototype.onDrop = function() {
var targetObject = this.targetObject;
return function(e, ui) {
var data = $(ui.draggable).data('pmui-datacontrol-data');
data.currentContainer.removeItem(data.index);
ui.helper.remove();
targetObject.addItem(data.data);
};
};
PMUI.extendNamespace('PMUI.behavior.DataControlDropBehavior', DataControlDropBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = DataControlDropBehavior;
}
}());
(function() {
var DataControlNoDropBehavior = function(settings) {
DataControlNoDropBehavior.superclass.call(this, settings);
this.targetObject = null;
DataControlNoDropBehavior.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.NoDropBehavior', DataControlNoDropBehavior);
DataControlNoDropBehavior.prototype.init = function(settings) {
if(!settings.targetObject) {
throw new Error("The target object wasn't specified.");
}
this.targetObject = settings.targetObject;
};
DataControlNoDropBehavior.prototype.attachDropBehavior = function() {
var html = this.targetObject.getHTML();
try {
$(html).droppable('destroy');
} catch(e) {}
return this;
};
PMUI.extendNamespace('PMUI.behavior.DataControlNoDropBehavior', DataControlNoDropBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = DataControlNoDropBehavior;
}
}());
(function () {
var DataControlDragBehavior = function (settings) {
DataControlDragBehavior.superclass.call(this, settings);
this.targetObject = null;
this.scope = null;
this.helper = null;
DataControlDragBehavior.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.DragBehavior', DataControlDragBehavior);
DataControlDragBehavior.prototype.init = function (settings) {
var defaults = {
scope: 'pmui-datacontrol-dragdrop-scope',
helper: 'original'
};
jQuery.extend(true, defaults, settings);
//TODO: uncomment the following validation when the GridPanel's rows are handled through objects.
//by now it will accept html elements as the targetObject option.
/*if(!defaults.targetObject || typeof defaults.targetObject !== 'object') {
throw new Error("The target object is null or invalid.");
}*/
if (!defaults.targetObject) {
throw new Error("The target object is null or invalid.");
}
this.targetObject = defaults.targetObject;
this.helper = defaults.helper;
this.scope = defaults.scope;
};
DataControlDragBehavior.prototype.onReceiveHandler = function () {
var targetObject = this.targetObject;
return function (e, ui) {
var row,
index,
newIndex,
diff,
globalIndex,
grid,
auxArray;
row = ui.item.data("pmui-datacontrol-data");
grid = row.parent;
//remove the item from its previous grid
index = grid.getRowIndex(row);
grid.items.asArray().splice(index, 1);
grid.goToPage(grid.currentPage);
//append the object to the new grid
newIndex = $(targetObject.dom.tbody).find('tr').index(ui.item);
$(ui.item).detach();
targetObject.addItem(row, newIndex);
row.setCells();
};
};
DataControlDragBehavior.prototype.onDropHandler = function () {
return function (e, ui) {
if (ui.item.parent().get(0) === this) {
var row,
index,
newIndex,
diff,
globalIndex,
grid,
auxArray;
row = ui.item.data("pmui-datacontrol-data");
grid = row.parent;
index = row.parent.indexInPage(row);
newIndex = $(grid.dom.tbody).find('tr').index(ui.item);
diff = newIndex - index;
newIndex = index + diff;
globalIndex = grid.getRowIndex(row);
row = grid.items.asArray().splice(globalIndex, 1)[0];
newIndex = globalIndex + diff;
grid.items.asArray().splice(newIndex, 0, row);
}
};
};
DataControlDragBehavior.prototype.attachDragBehavior = function () {
var $html,
i,
items,
item,
index,
row,
object = this.targetObject;
items = this.targetObject.getItems();
for (i = 0; i < items.length; i += 1) {
item = $(items[i].getHTML()).data("pmui-datacontrol-data", (function (j) {
return object.items.get(j);
}(i)));
if (!$html) {
$html = jQuery(item);
} else {
$html = $html.add(item);
}
}
try {
$(this.targetObject.dom.tbody).sortable("destroy");
} catch (e) {
}
$(this.targetObject.dom.tbody).sortable({
connectWith: '.pmui-gridpanel-tbody',
update: this.onDropHandler(),
receive: this.onReceiveHandler()
});
return this;
};
PMUI.extendNamespace('PMUI.behavior.DataControlDragBehavior', DataControlDragBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = DataControlDragBehavior;
}
}());
(function () {
var DataControlNoDragBehavior = function (settings) {
DataControlNoDragBehavior.superclass.call(this, settings);
this.targetObject = null;
DataControlNoDragBehavior.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.NoDragBehavior', DataControlNoDragBehavior);
DataControlNoDragBehavior.prototype.init = function (settings) {
//TODO: uncomment the following validation when the GridPanel's rows are handled through objects.
//by now it will accept html elements as the targetObject option.
/*if(!defaults.targetObject || typeof defaults.targetObject !== 'object') {
throw new Error("The target object is null or invalid.");
}*/
this.targetObject = settings.targetObject;
};
DataControlNoDragBehavior.prototype.attachDragBehavior = function () {
var $html,
i,
items,
item;
if (this.targetObject instanceof PMUI.panel.GridPanel) {
html = $(this.targetObject.getHTML()).find('tr');
} else {
items = this.targetObject.getItems();
for (i = 0; i < items.length; i += 1) {
item = $(items[i].getHTML()).data("'pmui-datacontrol-data'", items[i]);
if (!$html) {
$html = jQuery(item);
} else {
$html = $html.add(item);
}
}
}
try {
$(html).draggable("destroy");
} catch (e) {
}
return this;
};
PMUI.extendNamespace('PMUI.behavior.DataControlNoDragBehavior', DataControlNoDragBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = DataControlNoDragBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.ContainerItemBehavior
* Abstract class that encapsulates common properties ans functionality for the specific behaviors that can be
* attached to the Container's subclasses.
* @extends {PMUI.core.Base}
* @abstract
*
* @cfg {PMUI.core.Container} targetObject The object the behavior will be applied to.
* @cfg {String} [scope="pmui-containeritem-behavior"] A string that identifies group sets of items that can
* interact with this target object's behavior.
* @cfg {String} [handle=null] If specified, restricts dragging from starting unless the mousedown occurs on the
* specified element(s). Only elements that descend from the draggable element are permitted.
*/
var ContainerItemBehavior = function (settings) {
ContainerItemBehavior.superclass.call(this, settings);
/**
* The object the behavior will be applied to.
* @type {PMUI.core.Container}
*/
this.targetObject = null;
/**
* The string that identifies group sets of items that can interact with this target object's behavior.
* @type {String}
*/
this.scope = null;
/**
* If the behavior has been attached to its
* {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
* @type {Boolean}
* @readonly
*/
this.behaviorAttached = null;
/**
* The JQuery Object the JQuery UI behavior is currently applied to.
* @type {Object}
* @private
*/
this.targetHTML = null;
/**
* The selector for the sortable items in case a related behavior is applied.
* @type {String}
* @private
*/
this.sortableItems = null;
/**
* If the behavior is disabled or not.
* @type {Boolean}
* @readonly
*/
this.disabled = null;
/**
* The current object that currently is being dragged.
* @type {PMUI.core.Element}
* @readonly
*/
this.draggedObject = null;
/**
* The class for the placeholder used in some behaviors.
* @type {String}
* @readonly
*/
this.placeholderClass = null;
/**
* The class to be applied to the element when it is droppable and has a draggable element being dragged over it.
* @type {String}
* @readonly
*/
this.hoverClass = null;
this.sortItem = null;
this.sortIObject = null;
/**
* Restricts dragging/sorting from starting unless the mousedown occurs on the specified element(s).
* Only elements that descend from the draggable element are permitted.
* @type {String}
*/
this.handle = null;
ContainerItemBehavior.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Base', ContainerItemBehavior);
ContainerItemBehavior.prototype.init = function (settings) {
var defaults = {
scope: 'pmui-containeritem-behavior',
disabled: false,
placeholderClass: 'pmui-containeritembehavior-placeholder',
hoverClass: 'pmui-containeritembehavior-hover',
handle: null
};
$.extend(true, defaults, settings);
this.scope = defaults.scope;
this.placeholderClass = defaults.placeholderClass;
this.hoverClass = defaults.hoverClass;
this.setTargetObject(defaults.targetObject);
this.handle = defaults.handle;
if (defaults.disabled) {
this.disable();
} else {
this.enable();
}
};
/**
* Updates the behavior status (enabled/disabled).
* @chainable
* @abstract
* @private
*/
ContainerItemBehavior.prototype.updateBehaviorAvailability = function () {
};
/**
* Enables the behavior.
* @chainable
*/
ContainerItemBehavior.prototype.enable = function () {
this.disabled = false;
if (this.targetHTML) {
this.updateBehaviorAvailability();
}
return this;
};
/**
* Disables the behavior.
* @chainable
*/
ContainerItemBehavior.prototype.disable = function () {
this.disabled = true;
if (this.targetHTML) {
this.updateBehaviorAvailability();
}
return this;
};
/**
* Sets the object the behavior will be applied to.
* @param {PMUI.core.Container} targetObject
* @private
*/
ContainerItemBehavior.prototype.setTargetObject = function (targetObject) {
if (targetObject instanceof PMUI.core.Container) {
this.targetObject = targetObject;
this.sortableItems = this.targetObject.sortableItems;
} else {
throw new Error("setTargetObject(): the parameter must be an instance of PMUI.core.Container.");
}
return this;
};
/**
* Returns the target object the behavior will be applied to.
* @return {PMUI.core.Container}
*/
ContainerItemBehavior.prototype.getTargetObject = function () {
return this.targetObject;
};
/**
* Returns true if the behavior has been attached to its
* {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
* @return {Boolean}
*/
ContainerItemBehavior.prototype.isBehaviorAttached = function () {
return this.behaviorAttached;
};
/**
* Defines the handler to be executed when the
* {@link #property-targetObject targetObject}'s item dragging starts.
* @return {Function} The handler.
* @private
*/
ContainerItemBehavior.prototype.onStart = function () {
var that = this;
return function (e, ui) {
var targetObject = that.targetObject;
ui.helper.get(0).style.width = $(e.target).width() + "px";
$(this).hide();
that.draggedObject = PMUI.getPMUIObject(this);
if (typeof targetObject.onDragStart === 'function') {
targetObject.onDragStart(targetObject, that.draggedObject);
}
};
};
/**
* Defines the handler to be executed while a {@link #property-targetObject targetObject}'s item is being dragged.
* @return {Function} The handler.
* @private
*/
ContainerItemBehavior.prototype.onDrag = function () {
var that = this;
return function (e, ui) {
};
};
/**
* Defines the handler to be executed when the
* {@link #property-targetObject targetObject}'s item dragging stops.
* @return {Function} The handler.
* @private
*/
ContainerItemBehavior.prototype.onStop = function () {
var that = this;
return function (e, ui) {
var targetObject = that.targetObject;
that.draggedObject = PMUI.getPMUIObject(this);
if (typeof targetObject.onStopDrag === 'function') {
targetObject.onStopDrag(targetObject, that.draggedObject);
}
targetObject = $(this);
if (targetObject.parent().length) {
targetObject.show();
} else {
targetObject.remove();
}
that.draggedObject = null;
};
};
/**
* Defines the handler to be executed when an accepted draggable is dropped on the
* {@link #property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemBehavior.prototype.onDrop = function () {
var that = this;
return function (e, ui) {
var item = PMUI.getPMUIObject(ui.draggable.get(0)),
data,
placeholder = ui.draggable.data().sortableItem && ui.draggable.data().sortableItem.placeholder[0],
targetObject = that.targetObject, performDrop;
jQuery(ui.helper).remove();
if (!item || targetObject.isDirectParentOf(item)) {
return;
}
if (typeof targetObject.onBeforeDrop === 'function') {
targetObject.onBeforeDrop(targetObject, item, targetObject.items.getSize());
}
if (typeof targetObject.onDrop === 'function') {
performDrop = targetObject.onDrop(targetObject, item, targetObject.items.getSize() - 1);
}
if (performDrop || performDrop === undefined) {
item.getParent().behaviorObject.onDropOut(item, targetObject);
}
//The next lines avoid error when the dropped element is a sortable element
if (placeholder) {
placeholder.style.display = 'none';
targetObject.getContainmentArea().appendChild(placeholder);
}
};
};
/**
* The function to be perform when a draggable item is drop out from the container. Only applicable the behavior id
* of the drag type.
* @param {PMUI.core.Element} item The draggable item involved in the action.
* @param {PMUI.core.Container} destiny The container the draggable item was dropped on.
* @chainable
* @private
*/
ContainerItemBehavior.prototype.onDropOut = function (item, destiny, index) {
var targetObject = this.targetObject;
targetObject.removeItem(item);
if (destiny.canContain(item)) {
destiny.addItem(item, index);
item.setVisible(true);
} else {
data = item.getData();
destiny.addDataItem(data, index);
}
if (typeof targetObject.onDropOut === 'function') {
targetObject.onDropOut(item, targetObject, destiny);
}
return this;
};
/**
* Attaches event handlers for the mouse events triggered by the draggable/sortableItems.
* @chainable
* @private
*/
ContainerItemBehavior.prototype.attachOnDraggableMouseEvents = function () {
var that = this;
this.detachOnDraggableMouseEvents();
this.targetHTML.on('mouseover.pmui-containeritemdrag', function () {
var pmuiObject = PMUI.getPMUIObject(this);
that.targetObject.onDraggableMouseOver(pmuiObject);
return true;
}).on('mouseout.pmui-containeritemdrag', function () {
var pmuiObject = PMUI.getPMUIObject(this);
that.targetObject.onDraggableMouseOut(pmuiObject);
return true;
});
return this;
};
/**
* Attaches event handlers for the mouse events triggered by the draggable/sortableItems.
* @chainable
* @private
*/
ContainerItemBehavior.prototype.detachOnDraggableMouseEvents = function () {
this.targetHTML.off('mouseover.pmui-containeritemdrag')
.off('mouseout.pmui-containeritemdrag');
return this;
};
/**
* Attaches the behavior to its
* {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
* @chainable
*/
ContainerItemBehavior.prototype.attachBehavior = function () {
this.behaviorAttached = true;
this.updateBehaviorAvailability();
return this;
};
/**
* Detaches the behavior from its
* {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
* @chainable
*/
ContainerItemBehavior.prototype.detachBehavior = function () {
this.behaviorAttached = false;
return this;
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemBehavior', ContainerItemBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemNoBehavior
* @extends PMUI.behavior.ContainerItemBehavior
*/
var ContainerItemNoBehavior = function(settings) {
ContainerItemNoBehavior.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemNoBehavior);
/** [attachBehavior description] */
ContainerItemNoBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
items = targetObject.getItems();
for(i = 0; i < items.length; i += 1) {
item = items[i].getHTML();
if(!$html) {
$html = jQuery(item);
} else {
$html = $html.add(item);
}
}
try {
$html.draggable('destroy');
} catch(e) {}
try {
$(targetObject.getContainmentArea()).droppable('destroy');
} catch(e) {}
try {
$(targetObject.getContainmentArea()).sortable('destroy');
} catch(e) {}
}
return ContainerItemNoBehavior.superclass.prototype.attachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemNoBehavior', ContainerItemNoBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemNoBehavior;
}
}());
(function () {
/**
* @class PMUI.behavior.ContainerItemDragBehavior
* @extends PMUI.behavior.ContainerItemBehavior
* Class that encapsulates the drag behavior for the items of a {@link PMUI.core.Container Container}. The object
* that applies this behavior will be able to be dragged to another Containers that are applying dropping behaviors
* ({@link PMUI.behavior.ContainerItemDropBehavior drop},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "drag" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: 'Drop Here!'
* }
* ]
* });
*
* tree.setBehavior("drag");
* tree2.setBehavior("drop");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDragBehavior = function (settings) {
ContainerItemDragBehavior.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemDragBehavior);
/**
* @inheritdoc
*/
ContainerItemDragBehavior.prototype.updateBehaviorAvailability = function () {
this.targetHTML.draggable(this.disabled ? 'disable' : 'enable');
if (this.disabled) {
this.detachOnDraggableMouseEvents();
} else {
this.attachOnDraggableMouseEvents();
}
return this;
};
/**
* Returns the items from the {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}
* related to the behavior.
* @return {Object} A jQuery object.
* @private
*/
ContainerItemDragBehavior.prototype.getTargetObjectItems = function () {
var $html = $(null),
i,
item,
items,
targetObject = this.targetObject;
items = targetObject.getBehavioralItems();
for (i = 0; i < items.length; i += 1) {
item = items[i].getHTML();
$html = $html.add(item);
}
return $html;
};
/**
* @inheritdoc
*/
ContainerItemDragBehavior.prototype.onDropOut = function (item, targetObject, index) {
var aux;
ContainerItemDragBehavior.superclass.prototype.onDropOut.apply(this, arguments);
//The next lines avoid an uncontrolable error generated on the jquery ui's stop callback
//when it try to access certain data that will be undefined because of the destruction of the
//drag behavior when it is added to the new container in which it was dropped on.
//What it does is add some data in the jquery data of the object's html.
this.draggedObject = item;
aux = jQuery(this.draggedObject.html).data('ui-draggable');
if (!aux) {
aux = {};
jQuery(this.draggedObject.html).data('ui-draggable', aux);
}
if (!aux.sortables) {
aux.sortables = [];
}
if (!aux.options) {
aux.options = {};
}
return this;
};
/**
* Attaches the behavior to the {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragBehavior.prototype.attachBehavior = function (index) {
var $html,
i,
items,
item,
targetObject = this.targetObject,
behaviorAttached;
if (index !== undefined) {
$html = $(this.targetObject.items.get(index).getHTML());
behaviorAttached = this.targetObject.items.get(index).behaviorObject.behaviorAttached;
} else {
$html = this.getTargetObjectItems();
behaviorAttached = this.behaviorAttached;
}
if (!behaviorAttached) {
if (index !== undefined) {
$html = $(this.targetObject.items.get(index).getHTML());
} else {
$html = this.getTargetObjectItems();
}
this.targetHTML = $html;
$html.draggable({
appendTo: document.body,
connectToSortable: '.pmui-containeritembehavior-sort',
helper: 'clone',
revert: "invalid",
scope: this.scope,
drag: this.onDrag(),
start: this.onStart(),
stop: this.onStop(),
items: this.draggableItems,
handle: this.handle || false,
zIndex: 1000
});
this.attachOnDraggableMouseEvents();
if (index !== undefined) {
this.targetObject.items.get(index).behaviorObject.behaviorAttached = true;
this.targetObject.items.get(index).behaviorObject.updateBehaviorAvailability();
} else {
ContainerItemDragBehavior.superclass.prototype.attachBehavior.call(this);
}
}
return this;
};
/**
* Detaches the behavior from the
* {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragBehavior.prototype.detachBehavior = function () {
if (this.behaviorAttached) {
try {
this.targetHTML.draggable('destroy');
this.detachOnDraggableMouseEvents();
} catch (e) {
}
}
return ContainerItemDragBehavior.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDragBehavior', ContainerItemDragBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDragBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemDragCloneBehavior
* @extends PMUI.behavior.ContainerItemDragBehavior
* Class that encapsulates the drag behavior for the items of a {@link PMUI.core.Container Container}. The object
* that applies this behavior will be able to create a clone item to be dragged to another Containers that are
* applying dropping behaviors
* ({@link PMUI.behavior.ContainerItemDropBehavior drop},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragclone" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: 'Drop Here!'
* }
* ]
* });
*
* tree.setBehavior("dragclone");
* tree2.setBehavior("drop");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDragCloneBehavior = function(settings) {
ContainerItemDragCloneBehavior.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemDragBehavior', ContainerItemDragCloneBehavior);
/**
* @inheritdoc
*/
ContainerItemDragCloneBehavior.prototype.onStart = function() {
var that = this;
return function(e, ui) {
var targetObject = that.targetObject;
ui.helper.get(0).style.width = $(e.target).width() + "px";
that.draggedObject = PMUI.getPMUIObject(this);
if(typeof targetObject.onDragStart === 'function') {
targetObject.onDragStart(targetObject, that.draggedObject);
}
};
};
/**
* @inheritodc
*/
ContainerItemDragCloneBehavior.prototype.onDropOut = function(item, destiny, index) {
var targetObject = this.targetObject;
data = item.getData();
destiny.addDataItem(data, index);
if(typeof targetObject.onDropOut === 'function') {
targetObject.onDropOut(item, targetObject, destiny);
}
return this;
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDragCloneBehavior', ContainerItemDragCloneBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDragCloneBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemDropBehavior
* @extends PMUI.behavior.ContainerItemBehavior
* Class that encapsulates the drop behavior for {@link PMUI.core.Container Container} objects. The object that
* applies this behavior will be able to accept draggable items from other Containers which ones are applying
* dragging behaviors ({@link PMUI.behavior.ContainerItemDragBehavior drag},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}). .
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "drop" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: 'Drop Here!'
* }
* ]
* });
*
* tree.setBehavior("drag");
* tree2.setBehavior("drop");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDropBehavior = function(settings) {
ContainerItemDropBehavior.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemDropBehavior);
/**
* @inheritdoc
*/
ContainerItemDropBehavior.prototype.updateBehaviorAvailability = function() {
this.targetHTML.droppable(this.disabled ? 'disable' : 'enable');
return this;
};
/**
* Defines the handler to be executed when an accepted draggable is dragged over the
* {@link #property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemDropBehavior.prototype.onOver = function() {
var that = this;
return function(e, ui) {
(that.targetObject.onDragOver()());
};
};
/**
* Defines the handler to be executed when an accepted draggable is dragged out of the
* {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemDropBehavior.prototype.onOut = function() {
var that = this;
return function(e, ui) {
};
};
/**
* Attaches the behavior to the {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDropBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
this.targetHTML = $(this.targetObject.getContainmentArea());
this.targetHTML.droppable({
scope: this.scope,
drop: this.onDrop(),
out: this.onOut(),
over: this.onOver(),
hoverClass: this.hoverClass,
tolerance: 'pointer'
});
}
return ContainerItemDropBehavior.superclass.prototype.attachBehavior.call(this);
};
/**
* Detaches the behavior from the
* {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDropBehavior.prototype.detachBehavior = function() {
if(this.behaviorAttached) {
try {
this.targetHTML.droppable('destroy');
} catch(e) {}
}
return ContainerItemDropBehavior.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDropBehavior', ContainerItemDropBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDropBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemSortBehavior
* @extends PMUI.behavior.ContainerItemDragBehavior
* Class that encapsulates the sort behavior for {@link PMUI.core.Container Container} objects. This behavior
* enables the {@link #property-targetObject targetObject}'s' items to be sortable.
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "sort" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "sortable item #1"
* }, {
* label: "sortable item #2"
* }, {
* label: "sortable item #3"
* }, {
* label: "sortable item #4"
* }, {
* label: "sortable item #5"
* }, {
* label: "sortable item #6"
* }
* ]
* });
*
* tree.setBehavior("sort");
* document.body.appendChild(tree.getHTML());
*/
var ContainerItemSortBehavior = function(settings) {
ContainerItemSortBehavior.superclass.call(this, settings);
this.alignment = null;
ContainerItemSortBehavior.prototype.init.call(this,settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemDragBehavior', ContainerItemSortBehavior);/**
* @inheritdoc
*/
ContainerItemSortBehavior.prototype.init = function (settings) {
var defaults = {
alignment : 'vertical'
};
jQuery.extend(true, defaults, settings);
this.setAlignment(defaults.alignment);
};
ContainerItemSortBehavior.prototype.setAlignment = function (alignment) {
if (!(typeof alignment === "string" && (alignment === "vertical" || alignment === "horizontal"))){
throw new Error ("ContainerItemSortBehavior.setAlignment(): the parameter is no valid, must be a vertical or horizontal value");
}
this.alignment = alignment;
return this;
};
ContainerItemSortBehavior.prototype.updateBehaviorAvailability = function() {
this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
return this;
};
/**
* Defines the handler to be executed when the
* {@link #property-targetObject targetObject}'s item sorting starts.
* @return {Function} The handler.
* @private
*/
ContainerItemSortBehavior.prototype.onStart = function() {
var that = this;
return function(e, ui) {
var targetObject,
o;
that.cancelledDrop = null;
o = PMUI.getPMUIObject(ui.item.get(0));
if(that.targetObject.isDirectParentOf(o)) {
targetObject = that.targetObject;
if ( that.alignment == "vertical" ) {
ui.helper.get(0).style.width = $(e.target).width() + "px";
}
that.sortIObject = o;
if(typeof targetObject.onSortStart === 'function') {
targetObject.onSortStart(targetObject, that.sortIObject);
}
}
};
};
/**
* Defines the handler to be executed when the
* {@link #property-targetObject targetObject}'s item sorting stops.
* @return {Function} The handler.
* @private
*/
ContainerItemSortBehavior.prototype.onStop = function() {
var that = this;
return function() {};
};
/**
* Defines the handler to be executed when the user stopped sorting and the
* {@link #property-targetObject targetObject}'s items positions have
* changed.
* @return {Function} The handler.
* @private
*/
ContainerItemSortBehavior.prototype.onUpdate = function() {
return this.targetObject.onSortingChange();
};
/**
* Attaches the behavior to the {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemSortBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
this.targetHTML = jQuery(this.targetObject.getContainmentArea());
this.targetHTML.sortable({
containment: 'parent',
sort: this.onDrag(),
start: this.onStart(),
stop: this.onStop(),
update: this.onUpdate(),
handle: this.handle || false
});
}
return ContainerItemSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
};
/**
* Detaches the behavior from the
* {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemSortBehavior.prototype.detachBehavior = function() {
if(this.behaviorAttached) {
try {
targetHTML.sortable('destroy');
} catch(e) {}
}
return ContainerItemSortBehavior.superclass.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemSortBehavior', ContainerItemSortBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemSortBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemDragDropBehavior
* @extends PMUI.behavior.ContainerItemBehavior
* Class that encapsulates the drop behavior for a {@link PMUI.core.Container Container} object and the drag
* behavior for its items. The object that applies this behavior, will be able to:
*
* - Accept draggable items from other Containers which ones are applying dragging behaviors
* ({@link PMUI.behavior.ContainerItemDragBehavior drag},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
* - The object's items will be able to be dragged to another Containers that are applying dropping behaviors
* ({@link PMUI.behavior.ContainerItemDropBehavior drop},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragdrop" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: "draggable item #7"
* }, {
* label: "draggable item #8"
* }, {
* label: "draggable item #9"
* }, {
* label: "draggable item #10"
* }, {
* label: "draggable item #11"
* }, {
* label: "draggable item #12"
* }
* ]
* });
*
* tree.setBehavior("dragdrop");
* tree2.setBehavior("dragdrop");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDragDropBehavior = function(settings) {
ContainerItemDragDropBehavior.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemDragDropBehavior);
/**
* @inheritdoc
*/
ContainerItemDragDropBehavior.prototype.updateBehaviorAvailability = function() {
var d = this.disabled ? 'disable' : 'enable';
this.targetHTML.droppable(d);
jQuery(this.getTargetObjectItems()).draggable(d);
if(this.disabled) {
this.detachOnDraggableMouseEvents();
} else {
this.attachOnDraggableMouseEvents();
}
return this;
};
/**
* Returns the html from the items of the {@link #property-targetObject targetObject}
* @return {Object} A jQuery object with all the target object' items html.
*/
ContainerItemDragDropBehavior.prototype.getTargetObjectItems = function() {
var $html = $(null),
i,
item,
items,
targetObject = this.targetObject;
items = targetObject.getBehavioralItems();
for(i = 0; i < items.length; i += 1) {
item = items[i].getHTML();
$html = $html.add(item);
}
return $html;
};
/**
* @inheritdoc
*/
ContainerItemDragDropBehavior.prototype.onDropOut = function() {
var aux;
ContainerItemDragDropBehavior.superclass.prototype.onDropOut.apply(this, arguments);
//The next lines avoid an uncontrolable error generated on the jquery ui's stop callback
//when it try to access certain data that will be undefined because of the destruction of the
//drag behavior when it is added to the new container in which it was dropped on.
//What it does is add some data in the jquery data of the object's html.
aux = jQuery(this.draggedObject.html).data('ui-draggable');
if(!aux) {
aux = {};
jQuery(this.draggedObject.html).data('ui-draggable', aux);
}
if(!aux.sortables) {
aux.sortables = [];
}
if(!aux.options) {
aux.options = {};
}
return this;
};
/**
* Defines the handler to be executed when an accepted draggable is dragged over the
* {@link #property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemDragDropBehavior.prototype.onOver = function() {
var that = this;
return function(e, ui) {
(that.targetObject.onDragOver()());
};
};
/**
* Defines the handler to be executed when an accepted draggable is dragged out of the
* {@link #property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemDragDropBehavior.prototype.onOut = function() {
var that = this;
return function(e, ui) {
};
};
/**
* Attaches the behavior to the {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragDropBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
$html = this.getTargetObjectItems();
$html.draggable({
appendTo: document.body,
connectToSortable: '.pmui-containeritembehavior-sort',
helper: 'clone',
revert: "invalid",
drag: this.onDrag(),
start: this.onStart(),
stop: this.onStop(),
scope: this.scope,
handle: this.handle || false
});
this.targetHTML = jQuery(this.targetObject.getContainmentArea());
this.targetHTML.droppable({
drop: this.onDrop(),
out: this.onOut(),
over: this.onOver(),
tolerance: 'pointer',
scope: this.scope
});
this.attachOnDraggableMouseEvents();
}
return ContainerItemDragDropBehavior.superclass.prototype.attachBehavior.call(this);
};
/**
* Detaches the behavior from the
* {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragDropBehavior.prototype.detachBehavior = function() {
if(this.behaviorAttached) {
try {
this.getTargetObjectItems().draggable('destroy');
this.targetHTML.droppable('destroy');
this.detachOnDraggableMouseEvents();
} catch(e) {}
}
return ContainerItemDragDropBehavior.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDragDropBehavior', ContainerItemDragDropBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDragDropBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemDragSortBehavior
* @extends PMUI.behavior.ContainerItemDragBehavior
* Class that encapsulates the drag and sort behaviors for a {@link PMUI.core.Container Container} object items.
* The items of the object that applies this behavior, will be able to:
*
* - Be dragged to another Containers that are applying dropping behaviors
* ({@link PMUI.behavior.ContainerItemDropBehavior drop},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
* - Be sortable.
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragsort" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: 'Drop Here!'
* }
* ]
* });
*
* tree.setBehavior("dragsort");
* tree2.setBehavior("drop");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDragSortBehavior = function(settings) {
ContainerItemDragSortBehavior.superclass.call(this, settings);
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemDragBehavior', ContainerItemDragSortBehavior);
/**
* @inheritdoc
*/
ContainerItemDragSortBehavior.prototype.updateBehaviorAvailability = function() {
this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
if(this.disabled) {
this.detachOnDraggableMouseEvents();
} else {
this.attachOnDraggableMouseEvents();
}
return this;
};
/**
* @inheritdoc
*/
ContainerItemDragSortBehavior.prototype.onStart = function() {
var that = this;
return function(e, ui) {
var targetObject, o;
//Since the jQuery sortable's start callback is executed when the sorting starts and not neccesarily when
//one of its own items starts dragging we need to do the next validation:
//check if the item dragged belongs to the sortable html. In that case we fire the onDragStart callback.
o = PMUI.getPMUIObject(ui.item.get(0));
if(that.targetObject.isDirectParentOf(o)) {
targetObject = that.targetObject;
ui.helper.get(0).style.width = $(e.target).width() + "px";
ui.item.hide();
that.draggedObject = o;
if(typeof targetObject.onDragStart === 'function') {
targetObject.onDragStart(targetObject, that.draggedObject);
}
}
};
};
/**
* Defines the handler to be executed when the user stopped sorting and the
* {@link #property-targetObject targetObject}'s items positions have
* changed.
* @return {Function} The handler.
* @private
*/
ContainerItemDragSortBehavior.prototype.onUpdate = function() {
var that = this;
return function(e, ui) {
var parentHTML = that.targetObject.getHTML(),
item = PMUI.getPMUIObject(ui.item.get(0));
if(item && jQuery(parentHTML).has(item.getHTML()).length) {
(that.targetObject.onSortingChange()(e, ui));
}
};
};
/**
* Attaches the behavior to the {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragSortBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
this.targetHTML = jQuery(targetObject.getContainmentArea());
this.targetHTML.sortable({
appendTo: document.body,
connectWith: '.pmui-containeritembehavior-sort',
helper: 'clone',
revert: "invalid",
start: this.onStart(),
update: this.onUpdate(),
items: this.sortableItems,
placeholder: this.placeholderClass,
handle: this.handle || false,
zIndex: 1000
});
this.attachOnDraggableMouseEvents();
}
return ContainerItemDragSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
};
/**
* Detaches the behavior from the
* {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragSortBehavior.prototype.detachBehavior = function() {
if(this.behaviorAttached) {
try {
this.targetHTML.sortable('destroy');
} catch(e) {}
}
return ContainerItemDragSortBehavior.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDragSortBehavior', ContainerItemDragSortBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDragSortBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemDropSortBehavior
* @extends PMUI.behavior.ContainerItemDropBehavior
* Class that encapsulates the drop behavior for a {@link PMUI.core.Container Container} object and the drag
* behavior for its items. The object that applies this behavior, will be able to:
*
* - Accept draggable items from other Containers which ones are applying dragging behaviors
* ({@link PMUI.behavior.ContainerItemDragBehavior drag},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
*
* - The object items will be sortable.
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dropsort" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "sortable item #1"
* }, {
* label: "sortable item #2"
* }, {
* label: "sortable item #3"
* }, {
* label: "sortable item #4"
* }, {
* label: "sortable item #5"
* }, {
* label: "sortable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* });
*
* tree.setBehavior("dropsort");
* tree2.setBehavior("drag");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDropSortBehavior = function(settings) {
ContainerItemDropSortBehavior.superclass.call(this, settings);
this.dropped = null;
this.cancelledDrop = null;
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemDropBehavior', ContainerItemDropSortBehavior);
/**
* @inheritdoc
*/
ContainerItemDropSortBehavior.prototype.updateBehaviorAvailability = function() {
this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
return this;
};
/**
* Defines the handler to be executed when an accepted draggable is dropped on the
* {@link #property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemDropSortBehavior.prototype.onDrop = function() {
var that = this;
return function(e, ui) {
var droppable = $(this),
targetObject = that.targetObject,
item = PMUI.getPMUIObject(ui.item.get(0)),
index = !targetObject.getItems().length ? 0 : droppable.find('>*').index(item.getHTML()),
data,
performDrop;
if(index < 0) {
index = droppable.find('>*').index(droppable.find('.ui-draggable'));
}
if(typeof targetObject.onBeforeDrop === 'function') {
targetObject.onBeforeDrop(targetObject, item, index);
}
if(typeof targetObject.onDrop === 'function') {
performDrop = targetObject.onDrop(targetObject, item, index);
}
if(performDrop || performDrop === undefined) {
item.getParent().behaviorObject.onDropOut(item, targetObject, index);
that.dropped = true;
if(!targetObject.canContain(item)) {
$(e.target).find('.ui-draggable').detach();
}
that.cancelledDrop = false;
} else {
//if the dropped object came from a container with draggable or sortable behavior
if(ui.helper) {
ui.sender.draggable('cancel');
} else {
ui.sender.sortable('cancel');
}
that.cancelledDrop = true;
}
};
};
/**
* Defines the handler to be executed when the user stopped sorting and the
* {@link #property-targetObject targetObject}'s items positions have
* changed.
* @return {Function} The handler.
* @private
*/
ContainerItemDropSortBehavior.prototype.onUpdate = function() {
var that = this;
return function(e, ui) {
var parentHTML = that.targetObject.getHTML(),
item = PMUI.getPMUIObject(ui.item.get(0));
if(that.dropped) {
that.dropped = false;
return;
}
if(item && jQuery(parentHTML).has(item.getHTML()).length) {
(that.targetObject.onSortingChange()(e, ui));
}
};
};
/**
* @inheritdoc
*/
ContainerItemDropSortBehavior.prototype.onStop = function() {
var that = this;
return function(e, ui) {
if(that.cancelledDrop) {
ui.item.remove();
}
that.cancelledDrop = null;
};
};
/**
* @inherit
*/
ContainerItemDropSortBehavior.prototype.onStart = function() {
var that = this;
return function(e, ui) {
that.cancelledDrop = null;
};
};
/**
* Attaches the behavior to the {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDropSortBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
this.targetHTML = jQuery(targetObject.getContainmentArea());
this.targetHTML.addClass('pmui-containeritembehavior-sort').sortable({
containment: targetObject.getContainmentArea(),
receive: this.onDrop(),
start: this.onStart(),
update: this.onUpdate(),
items: this.sortableItems,
stop: this.onStop(),
tolerance: 'pointer',
handle: this.handle || false,
placeholder: this.placeholderClass
});
}
return ContainerItemDropSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
};
/**
* Detaches the behavior from the
* {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDropSortBehavior.prototype.detachBehavior = function() {
if(this.behaviorAttached) {
try {
this.targetHTML.removeClass('pmui-containeritembehavior-sort').sortable('destroy');
} catch(e) {}
}
return ContainerItemDropSortBehavior.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDropSortBehavior', ContainerItemDropSortBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDropSortBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemDragDropSortBehavior
* @extends PMUI.behavior.ContainerItemDragDropBehavior
* Class that encapsulates the drop behavior for a {@link PMUI.core.Container Container} object and the drag and
* sort behaviors for its items. The object that applies this behavior, will be able to:
*
* - Accept draggable items from other Containers which ones are applying dragging behaviors
* ({@link PMUI.behavior.ContainerItemDragBehavior drag},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
*
* - The object's items will be able to be dragged to another Containers that are applying dropping behaviors
* ({@link PMUI.behavior.ContainerItemDropBehavior drop},
* {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
* {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort},
* {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
*
* - The object's items will be enabled for sorting.
*
* Since this class is created through the
* {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
* most cases (it is instantiated internally by the object of the Container class).
* If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
* {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragdropsort" or set it
* at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
*
* Usage example:
*
* @example
* var tree = new PMUI.panel.TreePanel({
* items: [
* {
* label: "draggable item #1"
* }, {
* label: "draggable item #2"
* }, {
* label: "draggable item #3"
* }, {
* label: "draggable item #4"
* }, {
* label: "draggable item #5"
* }, {
* label: "draggable item #6"
* }
* ]
* }), tree2 = new PMUI.panel.TreePanel({
* style: {
* cssClasses: ["droppable-list"]
* },
* items: [
* {
* label: "draggable item #7"
* }, {
* label: "draggable item #8"
* }, {
* label: "draggable item #9"
* }, {
* label: "draggable item #10"
* }, {
* label: "draggable item #11"
* }, {
* label: "draggable item #12"
* }
* ]
* });
*
* tree.setBehavior("dragdropsort");
* tree2.setBehavior("dragdropsort");
* document.body.appendChild(tree.getHTML());
* //Add a separator
* document.body.appendChild(document.createElement('hr'));
* document.body.appendChild(tree2.getHTML());
*/
var ContainerItemDragDropSortBehavior = function(settings) {
ContainerItemDragDropSortBehavior.superclass.call(this, settings);
this.dropped = null;
this.cancelledDrop = null;
};
PMUI.inheritFrom('PMUI.behavior.ContainerItemDragDropBehavior', ContainerItemDragDropSortBehavior);
/**
* @inheritdoc
*/
ContainerItemDragDropSortBehavior.prototype.updateBehaviorAvailability = function() {
this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
if(this.disabled) {
this.detachOnDraggableMouseEvents();
} else {
this.attachOnDraggableMouseEvents();
}
return this;
};
/**
* Defines the handler to be executed when the user stopped sorting and the
* {@link #property-targetObject targetObject}'s items positions have
* changed.
* @return {Function} The handler.
* @private
*/
ContainerItemDragDropSortBehavior.prototype.onUpdate = function() {
var that = this;
return function(e, ui) {
var parentHTML = that.targetObject.getHTML(),
item = PMUI.getPMUIObject(ui.item.get(0));
if(that.dropped) {
that.dropped = false;
return;
}
if(!item) {
return;
}
if(jQuery(parentHTML).has(item.getHTML()).length) {
(that.targetObject.onSortingChange()(e, ui));
}
};
};
/**
* @inheritdoc
*/
ContainerItemDragDropSortBehavior.prototype.onStart = function() {
var that = this;
return function(e, ui) {
var targetObject, o;
that.cancelledDrop = null;
//Since the jQuery sortable's start callback is executed when the sorting starts and not neccesarily when
//one of its own items starts dragging we need to do the next validation:
//check if the item dragged belongs to the sortable html. In that case we fire the onDragStart callback.
o = PMUI.getPMUIObject(ui.item.get(0));
if(that.targetObject.isDirectParentOf(o)) {
targetObject = that.targetObject;
ui.helper.get(0).style.width = $(e.target).width() + "px";
ui.item.hide();
that.draggedObject = o;
if(typeof targetObject.onDragStart === 'function') {
targetObject.onDragStart(targetObject, that.draggedObject);
}
}
};
};
/**
* Defines the handler to be executed when an accepted draggable is dropped on the
* {@link #property-targetObject targetObject}.
* @return {Function} The handler.
* @private
*/
ContainerItemDragDropSortBehavior.prototype.onDrop = function() {
var that = this;
return function(e, ui) {
var targetObject = that.targetObject,
droppable = $(this),
item = PMUI.getPMUIObject(ui.item.get(0)),
data,
index = !targetObject.getItems().length ? 0
: droppable.find('>*').index(item.getHTML()),
performDrop;
if(index < 0) {
index = droppable.find('>*').index(droppable.find('.ui-draggable'));
}
if(typeof targetObject.onBeforeDrop === 'function') {
targetObject.onBeforeDrop(targetObject, item, index);
}
if(typeof targetObject.onDrop === 'function') {
performDrop = targetObject.onDrop(targetObject, item, index);
}
if(performDrop || performDrop === undefined) {
item.getParent().behaviorObject.onDropOut(item, targetObject, index);
that.dropped = true;
if(!targetObject.canContain(item)) {
$(e.target).find('.ui-draggable').detach();
}
that.cancelledDrop = false;
} else {
//if the dropped object came from a container with draggable or sortable behavior
if(ui.helper) {
//from draggable
ui.sender.draggable('cancel');
} else {
//from sortable
ui.sender.sortable('cancel');
}
that.cancelledDrop = true;
}
};
};
/**
* @inheritdoc
*/
ContainerItemDragDropSortBehavior.prototype.onStop = function() {
var that = this;
return function(e, ui) {
if(that.cancelledDrop) {
ui.item.remove();
}
that.cancelledDrop = null;
};
};
/**
* Attaches the behavior to the {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragDropSortBehavior.prototype.attachBehavior = function() {
var $html,
i,
items,
item,
targetObject = this.targetObject;
if(!this.behaviorAttached) {
this.targetHTML = jQuery(this.targetObject.getContainmentArea());
this.targetHTML.addClass('pmui-containeritembehavior-sort').sortable({
appendTo: document.body,
connectWith: '.pmui-containeritembehavior-sort',
helper: 'clone',
sort: this.onDrag(),
start: this.onStart(),
stop: this.onStop(),
receive: this.onDrop(),
out: this.onOut(),
over: this.onOver(),
update: this.onUpdate(),
items: this.sortableItems,
tolerance: 'pointer',
placeholder: this.placeholderClass,
handle: this.handle || false,
zIndex: 1000
});
this.attachOnDraggableMouseEvents();
}
return ContainerItemDragDropSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
};
/**
* Detaches the behavior from the
* {@link #property-targetObject targetObject}.
* @chainable
*/
ContainerItemDragDropSortBehavior.prototype.detachBehavior = function() {
if(this.behaviorAttached) {
try {
this.targetHTML.removeClass('pmui-containeritembehavior-sort').sortable('destroy');
this.detachOnDraggableMouseEvents();
} catch(e) {}
}
return ContainerItemDragDropSortBehavior.superclass.prototype.detachBehavior.call(this);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemDragDropSortBehavior', ContainerItemDragDropSortBehavior);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemDragDropSortBehavior;
}
}());
(function() {
/**
* @class PMUI.behavior.ContainerItemBehaviorFactory
* A factory that produces the behavior objects for {@link PMUI.core.Container Container} objects.
*
* It accepts the following pmTypes:
*
* - using "nobehavior" you get a {@link PMUI.behavior.ContainerItemNoBehavior ContainerItemNoBehavior object}.
* - using "drag" you get a {@link PMUI.behavior.ContainerItemDragBehavior ContainerItemDragBehavior object}.
* - using "drop" you get a {@link PMUI.behavior.ContainerItemDropBehavior ContainerItemDropBehavior object}.
* - using "sort" you get a {@link PMUI.behavior.ContainerItemSortBehavior ContainerItemSortBehavior object}.
* - using "dragdrop" you get a {@link PMUI.behavior.ContainerItemDragDropBehavior ContainerItemDragDropBehavior object}.
* - using "dragsort" you get a {@link PMUI.behavior.ContainerItemDragSortBehavior ContainerItemDragSortBehavior object}.
* - using "dropsort" you get a {@link PMUI.behavior.ContainerItemDropSortBehavior ContainerItemDropSortBehavior object}.
* - using "dragdropsort" you get a {@link PMUI.behavior.ContainerItemDragDropSortBehavior ContainerItemDragDropSortBehavior object}.
*
* The default pmType is 'nobehavior'.
* @extends {PMUI.util.Factory}
*/
var ContainerItemBehaviorFactory = function(settings) {
ContainerItemBehaviorFactory.superclass.call(this, settings);
ContainerItemBehaviorFactory.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.util.Factory', ContainerItemBehaviorFactory);
/**
* Initializes the new instance.
* @param {Object} settings An object with the config options.
* @private
*/
ContainerItemBehaviorFactory.prototype.init = function(settings) {
var defaults = {
products: {
'nobehavior': PMUI.behavior.ContainerItemNoBehavior,
'drag': PMUI.behavior.ContainerItemDragBehavior,
'dragclone': PMUI.behavior.ContainerItemDragCloneBehavior,
'drop': PMUI.behavior.ContainerItemDropBehavior,
'sort': PMUI.behavior.ContainerItemSortBehavior,
'dragdrop': PMUI.behavior.ContainerItemDragDropBehavior,
'dragsort': PMUI.behavior.ContainerItemDragSortBehavior,
'dropsort': PMUI.behavior.ContainerItemDropSortBehavior,
'dragdropsort': PMUI.behavior.ContainerItemDragDropSortBehavior
},
defaultProduct: 'nobehavior'
};
jQuery.extend(true, defaults, settings);
this.setProducts(defaults.products)
.setDefaultProduct(defaults.defaultProduct);
};
PMUI.extendNamespace('PMUI.behavior.ContainerItemBehaviorFactory', ContainerItemBehaviorFactory);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = ContainerItemBehaviorFactory;
}
}());
(function () {
/**
* @class PMUI.command.Command
* Abstract class command which declares some abstract methods such as
* execute (redo) and inverseExecute (undo) a command.
*
* A command is implemented in the library as follows:
*
* - The command must define a method execute which does the operation desired (e.g. commandDelete's execute
* method deletes shapes and connections from a canvas).
* - The command must define a method undo which undoes what the method execute did.
* - The command must define a method redo which simply calls the execute method (redo must do the
* same operation as execute).
*
* Finally to execute and save the command let's use the
{@link PMUI.draw.Canvas#property-commandStack} property that any
* canvas has so:
*
* // e.g.
* // let's assume that canvas is an instance of the class Canvas
* // let's create an instance of commandDelete
* // let's assume that config has the correct configuration options of this command
* var command = new PMUI.command.CommandDelete(config)
* // let's add the command to the canvas's commandStack
* canvas.commandStack.add(command);
* // finally let's execute the command
* command.execute(); // this line actually removes the shapes!
*
* // if we want to undo the last command
* canvas.commandStack.undo(); // this line recreates the shapes
*
* // if we want to redo the last command
* canvas.commandStack.redo(); // this line removes the shapes again
*
* @abstract
* @constructor Creates an instance of the class command
* @param {Object} receiver The object that will execute the command
*/
var Command = function (receiver) {
/**
* The object that executes the command
* @property {Object}
*/
this.receiver = receiver;
/**
* Reference to the canvas
* @property {PMUI.draw.Canvas}
*/
this.canvas = this.getCanvas(receiver);
};
/**
* Family of this command
* @property {String}
*/
Command.prototype.family = "Command";
/**
* Executes the command
* @template
* @protected
*/
Command.prototype.execute = function (stopTrigger) {
};
/**
* InverseExecutes the command (a.k.a. undo)
* @template
* @protected
*/
Command.prototype.undo = function (stopTrigger) {
};
/**
* Executes the command (a.k.a. redo)
* @template
* @protected
*/
Command.prototype.redo = function (stopTrigger) {
};
Command.prototype.getCanvas = function (obj) {
var aux,
canvas;
if (obj.getSize) {
aux = obj.get(0);
canvas = aux.getCanvas();
} else if (obj.getCanvas) {
canvas = obj.getCanvas();
} else {
canvas = null;
}
return canvas;
};
// extend the namespace
PMUI.extendNamespace('PMUI.command.Command', Command);
}());
(function () {
/**
* @class PMUI.command.CommandConnect
* Class CommandConnect determines the actions executed when a connection is created (redo) and the actions
* executed when it's destroyed (undo).
*
* Instances of this class are created in {@link PMUI.draw.Canvas#removeElements}.
*
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandConnect.
* @param {Object} receiver The object that will execute the command
*/
var CommandConnect = function (receiver) {
CommandConnect.superclass.call(this, receiver);
};
PMUI.inheritFrom('PMUI.command.Command', CommandConnect);
/**
* Type of command
* @property {String}
*/
CommandConnect.prototype.type = "CommandConnect";
/**
* Build the connection.
* The steps are:
*
* 1. Insert the ports in their respective parents (shapes)
* 2. Append the html of the ports
* 3. Add the connection html to the canvas
* 4. Trigger the create event *
* @chainable
*/
CommandConnect.prototype.buildConnection = function() {
var connection = this.receiver,
canvas = connection.canvas,
srcPort = connection.getSrcPort(),
destPort = connection.getDestPort();
// save the ports in its parents' ports array
srcPort.parent.ports.insert(srcPort);
destPort.parent.ports.insert(destPort);
// append the html of the ports to its parents (customShapes)
srcPort.parent.html.appendChild(srcPort.getHTML());
destPort.parent.html.appendChild(destPort.getHTML());
// add the connection to the canvas (its html is appended)
canvas.addConnection(connection);
canvas.updatedElement = connection;
return connection;
};
/**
* Executes the command.
* The steps are:
*
* 1. Insert the ports in their respective parents (shapes)
* 2. Append the html of the ports
* 3. Add the connection html to the canvas
* 4. Trigger the create event
*
* @chainable
*/
CommandConnect.prototype.execute = function () {
var connection = this.receiver,
canvas = connection.canvas,
srcPort = connection.getSrcPort(),
destPort = connection.getDestPort();
// save the ports in its parents' ports array
srcPort.parent.ports.insert(srcPort);
destPort.parent.ports.insert(destPort);
// append the html of the ports to its parents (customShapes)
srcPort.parent.html.appendChild(srcPort.getHTML());
destPort.parent.html.appendChild(destPort.getHTML());
// add the connection to the canvas (its html is appended)
canvas.addConnection(connection);
connection.checkAndCreateIntersectionsWithAll()
canvas.updatedElement = connection;
canvas.triggerCreateEvent(connection, []);
return this;
};
/**
* Inverse executes the command a.k.a. undo.
* The steps are:
*
* 1. Save the connection (detach it from the DOM)
* 2. Trigger the remove event
*
* @chainable
*/
CommandConnect.prototype.undo = function () {
this.receiver.saveAndDestroy();
this.receiver.canvas.triggerRemoveEvent(this.receiver, []);
return this;
};
/**
* Executes the command a.k.a. redo by calling `this.execute`
* @chainable
*/
CommandConnect.prototype.redo = function () {
this.execute();
return this;
};
// extend the namespace
PMUI.extendNamespace('PMUI.command.CommandConnect', CommandConnect);
}());
(function () {
/**
* @class PMUI.command.CommandCreate
* Class CommandCreate determines the actions executed when some shapes are created (redo) and the actions
* executed when they're destroyed (undo).
*
* Instances of this class are created in {@link PMUI.behavior.ConnectionDropBehavior#onDrop}.
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandCreate
* @param {Object} receiver The object that will execute the command
*
*/
var CommandCreate = function (receiver) {
CommandCreate.superclass.call(this, receiver);
/**
* Object that represents the state of the receiver before
* it was created
* @property {Object}
*/
this.before = null;
/**
* Object that represents the state of the receiver after
* it was created
* @property {Object}
*/
this.after = null;
CommandCreate.prototype.initObject.call(this, receiver);
};
PMUI.inheritFrom('PMUI.command.Command', CommandCreate);
/**
* Type of command
* @property {String}
*/
CommandCreate.prototype.type = "CommandCreate";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} receiver
* @private
*/
CommandCreate.prototype.initObject = function (receiver) {
this.before = {};
this.after = {
x: receiver.getX(),
y: receiver.getY(),
parent: receiver.getParent()
};
};
/**
* Executes the command.
* The steps are:
*
* 1. Insert the current shape to the children of its parent if it's possible
* 2. Append it to the HTML of its parent
* 3. Add the shape to either `canvas.customShapes` or `canvas.regularShapes`
* 4. Trigger the create event
*
* @chainable
*/
CommandCreate.prototype.execute = function () {
// execute the trigger
var shape = this.receiver,
parent = shape.parent;
// append the html to its parent
// NOTE: in the first execution (in containerDropBehavior) the html is
// already in the parent so the following line appends it again (html
// is not created)
// note that during the execution of this command the next line may called twice (one in
// RegularContainerBehavior.addToContainer and the other here) so check if it's not
// already in its children
if (!parent.getChildren().contains(shape)) {
parent.getChildren().insert(shape);
}
this.after.parent.html.appendChild(shape.getHTML());
shape.canvas.addToList(shape);
shape.showOrHideResizeHandlers(false);
shape.canvas.triggerCreateEvent(shape, []);
return this;
};
/**
* Inverse executes the command a.k.a. undo
*
* The steps are:
*
* 1. Remove the current shape from the children of its parent if it's possible
* 2. Remove its HTML (detach it from the DOM)
* 4. Trigger the remove event
*
* @chainable
*/
CommandCreate.prototype.undo = function () {
this.receiver.parent.getChildren().remove(this.receiver);
this.receiver.saveAndDestroy();
this.receiver.canvas.triggerRemoveEvent(this.receiver, []);
return this;
};
/**
* Executes the command a.k.a redo
* @chainable
*/
CommandCreate.prototype.redo = function () {
this.execute();
return this;
};
// extend the namespace
PMUI.extendNamespace('PMUI.command.CommandCreate', CommandCreate);
}());
(function () {
/**
* @class PMUI.command.CommandDelete
* Class CommandDelete determines the actions executed when some shapes are deleted (redo) and the actions
* executed when they're recreated (undo).
*
* Instances of this class are created in {@link PMUI.draw.Canvas#removeElements}.
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandDelete
* @param {Object} receiver The object that will execute the command
*/
var CommandDelete = function (receiver) {
CommandDelete.superclass.call(this, receiver);
/**
* A stack of commandsConnect
* @property {Array}
*/
this.stackCommandConnect = [];
/**
* ArrayList that represents the selection that was active before deleting the elements
* @property {PMUI.util.ArrayList}
*/
this.currentSelection = new PMUI.util.ArrayList();
/**
* Reference to the current connection in the canvas
* @property {PMUI.draw.Connection}
*/
this.currentConnection = null;
/**
* List of all the elements related to the commands
* @property {Array}
*/
this.relatedElements = [];
CommandDelete.prototype.initObject.call(this, receiver);
};
PMUI.inheritFrom('PMUI.command.Command', CommandDelete);
/**
* Type of command
* @property {String}
*/
CommandDelete.prototype.type = "CommandDelete";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} receiver The object that will execute the command
* @private
*/
CommandDelete.prototype.initObject = function (receiver) {
var i,
shape;
// move the current selection to this.currentSelection array
for (i = 0; i < receiver.getCurrentSelection().getSize() > 0; i += 1) {
shape = receiver.getCurrentSelection().get(i);
this.currentSelection.insert(shape);
}
// save the currentConnection of the canvas if possible
if (receiver.currentConnection) {
this.currentConnection = receiver.currentConnection;
}
};
/**
* Saves and destroys connections and shapes
* @private
* @param {Object} shape
* @param {boolean} root True if `shape` is a root element in the tree
* @param {boolean} [fillArray] If set to true it'll fill `this.relatedElements` with the objects erased
* @return {boolean}
*/
CommandDelete.prototype.saveAndDestroy = function (shape, root, fillArray) {
var i,
child,
parent,
children = null,
connection,
canvas = shape.canvas;
if (shape.hasOwnProperty("children")) {
children = shape.children;
}
// special function to be called as an afterwards
// BIG NOTE: doesn't have to delete html
if (shape.destroy) {
shape.destroy();
}
for (i = 0; i < children.getSize(); i += 1) {
child = children.get(i);
this.saveAndDestroy(child, false, fillArray);
}
while (shape.ports && shape.ports.getSize() > 0) {
connection = shape.ports.getFirst().connection;
if (fillArray) {
this.relatedElements.push(connection);
}
this.stackCommandConnect.push(
new PMUI.command.CommandConnect(connection)
);
connection.saveAndDestroy();
}
// remove from the children array of its parent
if (root) {
parent = shape.parent;
parent.getChildren().remove(shape);
if (parent.isResizable()) {
parent.resizeBehavior.updateResizeMinimums(shape.parent);
}
// remove from the currentSelection and from either the customShapes
// arrayList or the regularShapes arrayList
canvas.removeFromList(shape);
// remove the html only from the root
shape.html = $(shape.html).detach()[0];
}
if (fillArray) {
this.relatedElements.push(shape);
}
return true;
};
/**
* Executes the command
* The steps are:
*
* 1. Retrieve the old currentSelection (saved in `this.initObject()`)
* 2. Remove the shapes (detaching them from the DOM)
* 3. Remove the currentConnection if there's one
* 4. Trigger the remove event
*
* @chainable
*/
CommandDelete.prototype.execute = function () {
var shape,
i,
canvas = this.receiver,
currentConnection,
stringified,
fillArray = false,
mainShape = null;
if (this.relatedElements.length === 0) {
fillArray = true;
}
canvas.emptyCurrentSelection();
// copy from this.currentConnection
for (i = 0; i < this.currentSelection.getSize(); i += 1) {
shape = this.currentSelection.get(i);
canvas.addToSelection(shape);
}
if (canvas.currentSelection.getSize() === 1) {
mainShape = shape;
}
// remove the elements in the canvas current selection
stringified = [];
while (canvas.getCurrentSelection().getSize() > 0) {
shape = canvas.getCurrentSelection().getFirst();
this.saveAndDestroy(shape, true, fillArray);
stringified.push(shape.stringify());
}
// destroy the currentConnection
canvas.currentConnection = this.currentConnection;
currentConnection = canvas.currentConnection;
if (currentConnection) {
// add to relatedElements just in the case when only a connection is
// selected and deleted
this.relatedElements.push(currentConnection);
this.stackCommandConnect.push(
new PMUI.command.CommandConnect(currentConnection)
);
currentConnection.saveAndDestroy();
currentConnection = null;
}
canvas.triggerRemoveEvent(mainShape, this.relatedElements);
return this;
};
/**
* Inverse executes the command a.k.a. undo
*
* The steps are:
*
* 1. Retrieve the old currentSelection (saved in `this.initObject()`)
* 2. Restore the shapes (attaching them to the DOM)
* 3. Restore the currentConnection if there was one
* 4. Trigger the create event
*
* @chainable
*/
CommandDelete.prototype.undo = function () {
// undo recreates the shapes
var i,
shape,
mainShape = this.receiver.currentSelection.getFirst();
for (i = 0; i < this.currentSelection.getSize(); i += 1) {
shape = this.currentSelection.get(i);
// add to the canvas array of regularShapes and customShapes
shape.canvas.addToList(shape);
// add to the children of the parent
shape.parent.getChildren().insert(shape);
shape.parent.html.appendChild(shape.getHTML());
shape.showOrHideResizeHandlers(false);
}
// reconnect using the stack of commandConnect
for (i = this.stackCommandConnect.length - 1; i >= 0; i -= 1) {
this.stackCommandConnect[i].redo();
}
this.receiver.triggerCreateEvent(mainShape, this.relatedElements);
return this;
};
/**
* Executes the command (a.k.a redo)
* @chainable
*/
CommandDelete.prototype.redo = function () {
this.execute();
return this;
};
// extend namespace
PMUI.extendNamespace('PMUI.command.CommandDelete', CommandDelete);
}());
(function () {
/**
* @class PMUI.command.CommandEditLabel
* Encapsulates the action of editing a label
*
* //e.g.
* // var command = new PMUI.command.CommandEditLabel(label, "new message");
* @extends PMUI.command.Command
*
* @constructor
* Creates an instance of this command
* @param {PMUI.draw.Label} receiver The object that will perform the action
* @param {String} newMessage
*/
var CommandEditLabel = function (receiver, newMessage) {
CommandEditLabel.superclass.call(this, receiver);
this.before = null;
this.after = null;
CommandEditLabel.prototype.initObject.call(this, receiver, newMessage);
};
PMUI.inheritFrom('PMUI.command.Command', CommandEditLabel);
/**
* Type of the instances
* @property {String}
*/
CommandEditLabel.prototype.type = "CommandEditLabel";
/**
* Initializes the command
* @param {PMUI.draw.Label} receiver The object that will perform the action
* @param {String} newMessage
*/
CommandEditLabel.prototype.initObject = function (receiver, newMessage) {
var parentHeight = 0,
parentWidth = 0;
if (receiver.parent) {
parentHeight = receiver.parent.height;
parentWidth = receiver.parent.width;
}
this.before = {
message: receiver.message,
width: receiver.width,
height: receiver.height,
parentHeight: parentHeight,
parentWidth: parentWidth
};
this.after = {
message: newMessage,
width: 0,
height: 0,
parentHeight: parentWidth,
parentWidth: parentHeight
};
};
/**
* Executes the command, sets the new message updates the dimensions and its,
* parent if necessary
*/
CommandEditLabel.prototype.execute = function (stopTrigger) {
this.receiver.setMessage(this.after.message);
this.receiver.updateDimension();
if (this.after.width === 0) {
this.after.width = this.receiver.width;
this.after.height = this.receiver.height;
if (this.after.parentWidth !== 0) {
this.after.parentWidth = this.receiver.parent.width;
this.after.parentHeight = this.receiver.parent.height;
}
}
this.receiver.paint();
if (!stopTrigger) {
this.receiver.canvas.triggerTextChangeEvent(
this.receiver,
this.before.message,
this.after.message
);
if ((this.after.parentWidth !== this.before.parentWidth) &&
(this.before.parentHeight !== this.after.parentHeight)) {
this.receiver.canvas.triggerDimensionChangeEvent(
this.receiver.parent,
this.before.parentWidth,
this.before.parentHeight,
this.after.parentWidth,
this.after.parentHeight
);
}
}
};
/**
* Returns to the previous state before executing the command
*/
CommandEditLabel.prototype.undo = function (stopTrigger) {
this.receiver.setMessage(this.before.message);
if (this.receiver.parent) {
this.receiver.parent.setDimension(this.before.parentWidth, this.before.parentHeight);
}
this.receiver.setDimension(this.before.width, this.before.height);
this.receiver.updateDimension();
this.receiver.paint();
this.receiver.canvas.triggerTextChangeEvent(
this.receiver,
this.after.message,
this.before.message
);
if ((this.after.parentWidth !== this.before.parentWidth) &&
(this.before.parentHeight !== this.after.parentHeight)) {
this.receiver.canvas.triggerDimensionChangeEvent(
this.receiver.parent,
this.after.parentWidth,
this.after.parentHeight,
this.before.parentWidth,
this.before.parentHeight
);
}
};
/**
* Executes the command again after an undo action has been done
*/
CommandEditLabel.prototype.redo = function () {
this.execute();
};
PMUI.extendNamespace('PMUI.command.CommandEditLabel', CommandEditLabel);
}());
(function () {
/**
* @class PMUI.command.CommandMove
* Encapsulates the action of moving an element
*
* //e.g.
* var command = new PMUI.command.CommandMove(shape);
* @extends PMUI.command.Command
*
* @constructor
* Creates an instance of CommandMove
* @param {Object} receiver The object that will perform the action
*/
var CommandMove = function (receiver) {
CommandMove.superclass.call(this, receiver);
this.before = null;
this.after = null;
this.relatedShapes = [];
CommandMove.prototype.initObject.call(this, receiver);
};
PMUI.inheritFrom('PMUI.command.Command', CommandMove);
/**
* Type of the instances of this class
* @property {String}
*/
CommandMove.prototype.type = "CommandMove";
/**
* Initializes the command parameters
* @param {PMUI.draw.Core} receiver The object that will perform the action
*/
CommandMove.prototype.initObject = function (receiver) {
var i,
beforeShapes = [],
afterShapes = [];
for (i = 0; i < receiver.getSize(); i += 1) {
this.relatedShapes.push(receiver.get(i));
beforeShapes.push({
x: receiver.get(i).getOldX(),
y: receiver.get(i).getOldY()
});
afterShapes.push({
x: receiver.get(i).getX(),
y: receiver.get(i).getY()
});
}
this.before = {
shapes: beforeShapes
};
this.after = {
shapes: afterShapes
};
this.moveUndo = false;
};
/**
* Executes the command, changes the position of the element, and if necessary
* updates the position of its children, and refreshes all connections
*/
CommandMove.prototype.execute = function () {
var i,
j,
max,
connection,
shape,
delta;
//clear used array
PMUI.getActiveCanvas().refreshArray.clear();
PMUI.getActiveCanvas().connToRefresh.clear();
for (i = 0; i < this.relatedShapes.length; i += 1) {
shape = this.relatedShapes[i];
//delta to move user connections
delta = {
dx: this.after.shapes[i].x - this.before.shapes[i].x,
dy: this.after.shapes[i].y - this.before.shapes[i].y
};
shape.setPosition(this.after.shapes[i].x, this.after.shapes[i].y)
.refreshChildrenPositions(true, delta);
for (j = 0, max = this.canvas.connToRefresh.getSize(); j < max; j += 1) {
connection = this.canvas.connToRefresh.get(j);
connection.reconectSwitcher(delta, false);
}
shape.refreshConnections(false, this.relatedShapes, delta);
}
this.canvas.triggerPositionChangeEvent(this.relatedShapes,
this.before.shapes, this.after.shapes);
if (this.moveUndo) {
for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
connection = PMUI.getActiveCanvas().refreshArray.get(j);
connection.reconnectUser(delta, false);
connection.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
PMUI.getActiveCanvas().triggerConnectionStateChangeEvent(connection);
}
this.moveUndo = false;
}
};
/**
* Returns to the state before the command was executed
*/
CommandMove.prototype.undo = function () {
var i,
delta,
shape,
connection,
j, max;
PMUI.getActiveCanvas().refreshArray.clear();
PMUI.getActiveCanvas().connToRefresh.clear();
for (i = 0; i < this.relatedShapes.length; i += 1) {
shape = this.relatedShapes[i];
delta = {
dx: this.before.shapes[i].x - this.after.shapes[i].x,
dy: this.before.shapes[i].y - this.after.shapes[i].y
};
shape.setPosition(this.before.shapes[i].x, this.before.shapes[i].y)
.refreshChildrenPositions(true, delta);
for (j = 0, max = this.canvas.connToRefresh.getSize(); j < max; j += 1) {
connection = this.canvas.connToRefresh.get(j);
connection.reconectSwitcher(delta, false);
}
shape.refreshConnections(false, this.relatedShapes, delta);
}
this.canvas.triggerPositionChangeEvent(this.relatedShapes,
this.after.shapes, this.before.shapes);
for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
connection = PMUI.getActiveCanvas().refreshArray.get(j);
connection.reconnectUser(delta, false);
connection.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
PMUI.getActiveCanvas()
.triggerConnectionStateChangeEvent(connection);
}
this.moveUndo = true;
};
/**
* Executes the command again after an undo action has been done
*/
CommandMove.prototype.redo = function () {
this.execute();
};
PMUI.extendNamespace('PMUI.command.CommandMove', CommandMove);
}());
(function () {
/**
* @class PMUI.command.CommandPaste
* Class CommandPaste determines the actions executed when some shapes are pasted (redo) and the actions
* executed when they're removed (undo).
*
* Instances of this class are created in {@link PMUI.draw.Canvas#paste}.
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandPaste
* @param {Object} receiver The object that will execute the command
* @param {Object} options Initialization options
* @cfg {Array} [stackCommandConnect=[]] Array of commands connect
* @cfg {Array} [stackCommandCreate=[]] Array of commands create
*/
var CommandPaste = function (receiver, options) {
CommandPaste.superclass.call(this, receiver);
/**
* A stack of commandsConnect (for connections)
* @property {Array}
*/
this.stackCommandConnect = [];
/**
* A stack of commandsCreate (for shapes)
* @property {Array}
*/
this.stackCommandCreate = [];
CommandPaste.prototype.initObject.call(this, receiver, options);
};
PMUI.inheritFrom('PMUI.command.Command', CommandPaste);
/**
* Type of command
* @property {String}
*/
CommandPaste.prototype.type = "CommandPaste";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} receiver The object that will execute the command
* @private
*/
CommandPaste.prototype.initObject = function (receiver, options) {
var i,
shape,
defaults = {
stackCommandConnect: [],
stackCommandCreate: []
};
$.extend(true, defaults, options);
this.stackCommandConnect = defaults.stackCommandConnect;
this.stackCommandCreate = defaults.stackCommandCreate;
};
/**
* Executes the command.
* The steps are:
*
* 1. Execute the redo operation for each command create
* 2. Execute the redo operation for each command connect
*
* @chainable
*/
CommandPaste.prototype.execute = function () {
var i,
command;
for (i = 0; i < this.stackCommandCreate.length; i += 1) {
command = this.stackCommandCreate[i];
command.redo();
}
for (i = 0; i < this.stackCommandConnect.length; i += 1) {
command = this.stackCommandConnect[i];
command.redo();
}
return this;
};
/**
* Inverse executes the command a.k.a. undo.
* The steps are:
*
* 1. Execute the undo operation for each command create
* 2. Execute the undo operation for each command connect
*
* @chainable
*/
CommandPaste.prototype.undo = function () {
var i,
command;
for (i = 0; i < this.stackCommandCreate.length; i += 1) {
command = this.stackCommandCreate[i];
command.undo();
}
for (i = 0; i < this.stackCommandConnect.length; i += 1) {
command = this.stackCommandConnect[i];
command.undo();
}
return this;
};
/**
* Executes the command a.k.a redo
* @chainable
*/
CommandPaste.prototype.redo = function () {
this.execute();
return this;
};
PMUI.extendNamespace('PMUI.command.CommandPaste', CommandPaste);
}());
(function () {
/**
* @class PMUI.command.CommandReconnect
* Class CommandReconnect determines the actions executed when a connection is reconnected, e.g. when a connection
* source port or end port are dragged to another shape or another position in the same shape (redo)
* and the actions executed to revert the last drag to another shape or another position in the same shape (undo).
*
* Instances of this class are created in {@link PMUI.behavior.ConnectionDropBehavior#onDrop}.
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandReconnect
* @param {Object} receiver The object that will execute the command
*/
var CommandReconnect = function (receiver) {
CommandReconnect.superclass.call(this, receiver);
/**
* Object that represents the state of the shape before changing
* its dimension
* @property {Object}
*/
this.before = {
x: this.receiver.getOldX(),
y: this.receiver.getOldY(),
parent: this.receiver.getOldParent()
};
/**
* Object that represents the state of the shape after changing
* its dimension
* @property {Object}
*/
this.after = {
x: this.receiver.getX(),
y: this.receiver.getY(),
parent: this.receiver.getParent()
};
};
PMUI.inheritFrom('PMUI.command.Command', CommandReconnect);
/**
* Type of command.
* @property {String}
*/
CommandReconnect.prototype.type = "CommandReconnect";
/**
* Executes the command
* The steps are:
*
* 1. Hide the currentConnection of the canvas if there's one
* 2. If the new parent of the dragged port is different than the old parent
* - Remove the port from its old parent
* - Add the port to the new parent
* 3. If the new parent of the dragged port is equal to the old parent
* - Redefine its position in the shape
* 4. Reconnect the connection (using the new ports) and check for intersections
* 4. Trigger the port change event
*
* @chainable
*/
CommandReconnect.prototype.execute = function () {
var port = this.receiver,
parent = this.after.parent,
oldParent = this.before.parent;
// hide the connection if its visible
if (parent.canvas.currentConnection) {
parent.canvas.currentConnection.hidePortsAndHandlers();
parent.canvas.currentConnection = null;
}
if (parent.getID() !== oldParent.getID()) {
oldParent.removePort(port);
parent.addPort(port, this.after.x, this.after.y, true);
port.oldParent = oldParent;
} else {
parent.definePortPosition(port,
new PMUI.util.Point(this.after.x, this.after.y));
}
port.connection
.disconnect()
.connect()
.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
// custom trigger
this.receiver.canvas.triggerPortChangeEvent(port);
return this;
};
/**
* Inverse executes a command e.g. undo.
* The steps are:
*
* 1. Hide the currentConnection of the canvas if there's one
* 2. If the old parent of the port is different than the new parent
* - Remove the port from its new parent
* - Add the port to the old parent
* 3. If the old parent of the port is equal to the new parent
* - Redefine its position in the shape
* 4. Reconnect the connection (using the new ports) and check for intersections
* 4. Trigger the port change event
*
* @chainable
*/
CommandReconnect.prototype.undo = function () {
var port = this.receiver,
parent = this.after.parent,
oldParent = this.before.parent;
// hide the connection if its visible
if (parent.canvas.currentConnection) {
parent.canvas.currentConnection.hidePortsAndHandlers();
parent.canvas.currentConnection = null;
}
if (parent.getID() !== oldParent.getID()) {
parent.removePort(port);
oldParent.addPort(port, this.before.x, this.before.y, true);
port.canvas.regularShapes.insert(port);
port.oldParent = parent;
} else {
parent.definePortPosition(port,
new PMUI.util.Point(this.before.x, this.before.y));
}
port.connection
.disconnect()
.connect()
.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
// custom trigger
this.receiver.canvas.triggerPortChangeEvent(port);
return this;
};
/**
* Inverse executes a command e.g. undo
* @chainable
*/
CommandReconnect.prototype.redo = function () {
this.execute();
return this;
};
PMUI.extendNamespace('PMUI.command.CommandReconnect', CommandReconnect);
}());
(function () {
/**
* @class PMUI.command.CommandResize
* Class CommandResize determines the actions executed when some shapes are resized (redo) and the actions
* executed when they're resized back (undo).
*
* Instances of this class are created in {@link PMUI.behavior.RegularResizeBehavior#event-resizeEnd}.
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandResize
* @param {Object} receiver The object that will execute the command
*/
var CommandResize = function (receiver) {
CommandResize.superclass.call(this, receiver);
/**
* Object that represents the state of the shape before changing
* its dimension
* @property {Object}
*/
this.before = {
x: this.receiver.getOldX(),
y: this.receiver.getOldY(),
width: this.receiver.getOldWidth(),
height: this.receiver.getOldHeight()
};
/**
* Object that represents the state of the shape after changing
* its dimension
* @property {Object}
*/
this.after = {
x: this.receiver.getX(),
y: this.receiver.getY(),
width: this.receiver.getWidth(),
height: this.receiver.getHeight()
};
};
PMUI.inheritFrom('PMUI.command.Command', CommandResize);
/**
* Type of command of this object
* @property {String}
*/
CommandResize.prototype.type = "CommandResize";
/**
* Executes the command.
* The steps are:
*
* 1. Set the new position and dimension of the shape (using `this.after`)
* 2. Fix its connections on resize
* 3. Trigger the dimension change event
* 4. Trigger the position change event
*
* @chainable
*/
CommandResize.prototype.execute = function () {
var shape = this.receiver,
canvas = shape.getCanvas();
shape.setPosition(this.after.x, this.after.y)
.setDimension(this.after.width, this.after.height);
canvas.triggerDimensionChangeEvent(shape, this.before.width,
this.before.height, this.after.width, this.after.height);
if ((this.after.x !== this.before.x) || (this.after.y !== this.before.y)) {
canvas.triggerPositionChangeEvent(
[shape],
[
{
x: this.before.x,
y: this.before.y
}
],
[
{
x: this.after.x,
y: this.after.y
}
]
);
}
return this;
};
/**
* Inverse executes a command a.k.a undo.
* The steps are:
*
* 1. Set the new position and dimension of the shape (using `this.before`)
* 2. Fix its connections on resize
* 3. Trigger the dimension change event
* 4. Trigger the position change event
*
* @chainable
*/
CommandResize.prototype.undo = function () {
var shape = this.receiver,
canvas = shape.getCanvas();
shape.setPosition(this.before.x, this.before.y)
.setDimension(this.before.width, this.before.height);
shape.fixConnectionsOnResize(shape.resizing, true);
canvas.triggerDimensionChangeEvent(shape, this.after.width,
this.after.height, this.before.width, this.before.height);
if ((this.after.x !== this.before.x) || (this.after.y !== this.before.y)) {
canvas.triggerPositionChangeEvent(
[shape],
[
{
x: this.after.x,
y: this.after.y
}
],
[
{
x: this.before.x,
y: this.before.y
}
]
);
}
return this;
};
/**
* Executes the command a.k.a redo.
* @chainable
*/
CommandResize.prototype.redo = function () {
this.execute();
return this;
};
PMUI.extendNamespace('PMUI.command.CommandResize', CommandResize);
}());
(function () {
/**
* @class PMUI.command.CommandSegmentMove
* Class CommandSegmentMove determines the actions executed when a segment is moved through its move handler (redo)
* and the actions executed when the segment is moved back (undo).
*
* Instances of this class are created in {@link PMUI.draw.SegmentMoveHandler#event-dragEnd}.
* @extends PMUI.command.Command
*
* @constructor Creates an instance of the class CommandSegmentMove
* @param {PMUI.draw.Connection} receiver The object that will execute the command
* @param {Object} options Initialization options
* @cfg {Array} [oldPoints=[]] Array of old points of the connection
* @cfg {Array} [newPoints=[]] Array of new points of the connection
*/
var CommandSegmentMove = function (receiver, options) {
CommandSegmentMove.superclass.call(this, receiver);
/**
* Array of points that represent the state of the connection before moving the segment move handler
* @property {Array}
*/
this.oldPoints = [];
/**
* Array of points that represent the state of the connection after moving the segment move handler
* @property {Array}
*/
this.newPoints = [];
CommandSegmentMove.prototype.initObject.call(this, options);
};
PMUI.inheritFrom('PMUI.command.Command', CommandSegmentMove);
/**
* Type of command of this object
* @property {String}
*/
CommandSegmentMove.prototype.type = "CommandResize";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains old points and new points
* @private
*/
CommandSegmentMove.prototype.initObject = function (options) {
var defaults = {
oldPoints: [],
newPoints: []
},
i,
point;
$.extend(true, defaults, options);
this.oldPoints = [];
for (i = 0; i < defaults.oldPoints.length; i += 1) {
point = defaults.oldPoints[i];
this.oldPoints.push(new PMUI.util.Point(point.x, point.y));
}
this.newPoints = [];
for (i = 0; i < defaults.newPoints.length; i += 1) {
point = defaults.newPoints[i];
this.newPoints.push(new PMUI.util.Point(point.x, point.y));
}
};
/**
* There's a common behavior between execute and inverseExecute in
* this command so merge both files and use a parameter to choose
* between execute and inverseExecute.
* The steps are:
*
* 1. Select the connection by triggering a click in its destination decorator
* 2. Hide the ports and handlers of the connection and reconnect the connection using points
* 3. Check and create the intersections and show the ports of the connection
* 4. Trigger the segment move event
*
* @private
* @chainable
*/
CommandSegmentMove.prototype.common = function (action) {
var connection = this.receiver;
// trigger targetSpriteDecorator onClick
$(connection.destDecorator.getHTML()).trigger('click');
connection.hidePortsAndHandlers();
connection.disconnect(true).connect({
algorithm: 'user',
points: this[action]
});
connection.setSegmentMoveHandlers();
// create intersections with all other connections
connection.checkAndCreateIntersectionsWithAll();
// show the ports and handlers again
connection.showPortsAndHandlers();
// trigger event
connection.canvas.triggerConnectionStateChangeEvent(connection);
return this;
};
/**
* Executes a command
* @chainable
*/
CommandSegmentMove.prototype.execute = function () {
this.common("newPoints");
return this;
};
/**
* Inverse executes the command a.k.a. undo
* @chainable
*/
CommandSegmentMove.prototype.undo = function () {
this.common("oldPoints");
return this;
};
/**
* Executes the command a.k.a. redo
* @chainable
*/
CommandSegmentMove.prototype.redo = function () {
this.execute();
return this;
};
PMUI.extendNamespace('PMUI.command.CommandSegmentMove', CommandSegmentMove);
}());
(function () {
/**
* @class PMUI.command.CommandStack
* Command stack stores the commands executed to perform undos and redos, it consists of 2 stacks:
*
* - undoStack (represented as an array)
* - redoStack (represented as an array)
*
* Every time an undo or redo action is executed the stacks automatically get updated and the function passed
* during the instantiation is called.
*
* // e.g.
* // let's assume that commandCreateInstance is an instance of CommandCreate
* // let's assume that commandResizeInstance is an instance of CommandResize
*
* // first let's create the stacks (max size of the redo stack is 5)
* var commandStack = new PMUI.command.CommandStack(5);
*
* // commandStack.add() inserts the command to the undoStack (emptying the redo stack too)
* commandStack.add(commandCreateInstance);
* commandStack.add(commandResizeInstance);
* commandStack.add(commandResizeInstance);
* commandStack.add(commandResizeInstance);
* commandStack.add(commandResizeInstance);
*
* // at this point the redo stack is full (we defined a max size of 5), so the following add will remove the
* // last element of the stack (which is commandCreateInstance) and the undoStack will only consist of
* // command resize instances
* commandStack.add(commandResizeInstance);
*
* // whenever an undo operation is executed in the commandStack, the first command (which is the last command
* // in the undoStack) executes its undo operation and the command is removed from the undoStack and pushed to
* // the redoStack, graphically:
*
* // Let's define an stack graphically as '[['
* // if an element (e1) is pushed to the stack the stack becomes: [[e1
* // if an element (e2) is pushed to the stack the stack becomes: [[e1, e2
* // if an element (e3) is pushed to the stack the stack becomes: [[e1, e2, e3
* // if an element is removed from the stack the stack becomes: [[e1, e2
* // Note the direction of the stack, if it's defined as ']]'
* // the operations executed above turn the stack into:
* // e1]]; e2, e1]]; e3, e2, e1]]; e2, e1]]
*
* // Let's alias commandResizeInstance as cRI and commandCreateInstance cCI.
* // With the example defined above of commandResizeInstance, the following line does:
* // pre state:
* // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4, cRI_5
* // redoStack = ]]
* // post state:
* // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
* // redoStack = cRI_5]]
* commandStack.undo();
*
* // executing undo again leads to:
* // pre state:
* // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
* // redoStack = cRI_5]]
* // post state:
* // undoStack = [[cRI_1, cRI_2, cRI_3
* // redoStack = cRI_4, cRI_5]]
* commandStack.undo();
*
* // executing redo leads to:
* // pre state:
* // undoStack = [[cRI_1, cRI_2, cRI_3
* // redoStack = cRI_4, cRI_5]]
* // post state:
* // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
* // redoStack = cRI_5]]
* commandStack.redo();
*
* // adding a new command to the stack empties the redo stack so:
* // pre state:
* // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
* // redoStack = cRI_5]]
* // post state:
* // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4, cCI_1
* // redoStack = ]]
* commandStack.add(commandCreateInstance);
*
* @constructor Creates an instance of the class CommandStack
* @param {number} stackSize The maximum number of operations to be saved
* @param {Function} successCallback Function to be executed after add, undo or redo,
* `this` will refer to the object itself, not the constructor
*/
var CommandStack = function (stackSize, successCallback) {
var undoStack,
redoStack,
maxSize;
/**
* Stacks that contains commands (when pushed to the undoStack)
* @property {Array} [undoStack=[]]
* @private
*/
undoStack = [];
/**
* Stacks that contains commands (when pushed to the redoStack)
* @property {Array} [redoStack=[]]
* @private
*/
redoStack = [];
/**
* Maximum size of the undo stack
* @property {number} [maxSize=20]
* @private
*/
maxSize = stackSize || 20;
/**
* Empties the redo stack (when a new event is added to the undoStack)
* @private
*/
function emptyRedoStack() {
redoStack = [];
}
/**
* Handler to be called when a special action occurs
*/
function onSuccess() {
}
if (successCallback && {}.toString.call(successCallback) === '[object Function]') {
onSuccess = successCallback;
}
return {
/**
* Adds an action (command) to the undoStack
* @param {PMUI.command.Command} action
*/
add: function (action) {
emptyRedoStack();
undoStack.push(action);
if (undoStack.length > maxSize) {
// got to the max size of the stack
undoStack.shift();
}
onSuccess();
},
/**
* Adds an action (command) to the redoStack
* @param {PMUI.command.Command} action
*/
addToRedo: function (action) {
redoStack.push(action);
},
/**
* Undoes the last action executing undoStack's first item undo
* @return {boolean}
*/
undo: function () {
var action; // action to be inverse executed
if (undoStack.length === 0) {
//console.log("undo(): can't undo because there are no " +
// "actions to undo");
return false;
}
action = undoStack.pop();
// inverse execute the action
action.undo();
redoStack.unshift(action);
// execute on success handler
onSuccess();
return true;
},
/**
* Redoes the last action executing redoStack's first item redo
* @return {boolean}
*/
redo: function () {
var action; // action to be inverse executed
if (redoStack.length === 0) {
return false;
}
action = redoStack.shift();
// execute the action
action.redo();
undoStack.push(action);
// execute on success handler
onSuccess();
return true;
},
/**
* Clear both stacks
*/
clearStack: function () {
redoStack = [];
undoStack = [];
},
/**
* Debugging method to show the state of each stack
* @param {boolean} showDetailed
*/
debug: function (showDetailed) {
var i;
if (showDetailed) {
for (i = 0; i < undoStack.length; i += 1) {
console.log((i + 1) + ") " + undoStack[i].type);
}
}
if (showDetailed) {
for (i = 0; i < redoStack.length; i += 1) {
console.log((i + 1) + ") " + redoStack[i].type);
}
}
},
/**
* Gets the size of the redo stack
* @return {Number}
*/
getRedoSize: function () {
return redoStack.length;
},
/**
* Gets the size of the redo stack
* @return {Number}
*/
getUndoSize: function () {
return undoStack.length;
},
/**
* Sets the onSuccess handler of this object
* @param successCallback
* @chainable
*/
setHandler: function (successCallback) {
if (successCallback && {}.toString.call(successCallback) === '[object Function]') {
onSuccess = successCallback;
}
return this;
}
};
};
// extend the namespace
PMUI.extendNamespace('PMUI.command.CommandStack', CommandStack);
}());
(function () {
/**
* @class PMUI.command.CommandSwitchContainer
* Class that encapsulates the action of switching containers
*
* //e.g.
* var command = new PMUI.command.CommandSwitchContainer(arrayOfShapes);
* @extends PMUI.command.Command
*
* @constructor
* Creates an instance of this command
* @param {Array} shapesAdded array of shapes that are going to switch container
*/
var CommandSwitchContainer = function (shapesAdded) {
CommandSwitchContainer.superclass.call(this, shapesAdded[0].shape);
/**
* Properties of the object before the command is executed
* @property {Object}
*/
this.before = null;
/**
* Properties of the object after the command is executed
* @property {Object}
*/
this.after = null;
/**
* Reference to all objects involved in this command
* @type {Array}
*/
this.relatedShapes = [];
CommandSwitchContainer.prototype.initObject.call(this, shapesAdded);
};
PMUI.inheritFrom('PMUI.command.Command', CommandSwitchContainer);
/**
* Type of the instances of this command
* @property {String}
*/
CommandSwitchContainer.prototype.type = "CommandSwitchContainer";
/**
* Initializer of the command
* @param {Array} shapesAdded array of shapes that are going to switch container
*/
CommandSwitchContainer.prototype.initObject = function (shapesAdded) {
var i,
shape,
beforeShapes = [],
afterShapes = [];
for (i = 0; i < shapesAdded.length; i += 1) {
shape = shapesAdded[i];
this.relatedShapes.push(shape.shape);
beforeShapes.push({
parent: shape.shape.parent,
x: shape.shape.getOldX() * shape.shape.canvas.getZoomFactor(),
y: shape.shape.getOldY() * shape.shape.canvas.getZoomFactor(),
topLeft: true
});
afterShapes.push({
parent: shape.container,
x: shape.x,
y: shape.y,
topLeft: shape.topLeft
});
}
this.before = {
shapes: beforeShapes
};
this.after = {
shapes: afterShapes
};
this.containerUndo = false;
};
/**
* The command execution implementation, updates the parents, and if necessary,
* updates the children positions and connections.
*/
CommandSwitchContainer.prototype.execute = function () {
var i,
j,
max,
connection,
shape,
delta;
for (i = 0; i < this.relatedShapes.length; i += 1) {
shape = this.relatedShapes[i];
delta = {
dx: this.before.shapes[i].x - this.after.shapes[i].x,
dy: this.before.shapes[i].y - this.after.shapes[i].y
};
this.before.shapes[i].parent.swapElementContainer(
shape,
this.after.shapes[i].parent,
this.after.shapes[i].x,
this.after.shapes[i].y,
this.after.shapes[i].topLeft
);
shape.refreshChildrenPositions(true, delta);
shape.refreshConnections(false, this.relatedShapes, delta);
}
this.canvas.triggerParentChangeEvent(this.relatedShapes,
this.before.shapes, this.after.shapes);
if (this.containerUndo) {
for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
connection = PMUI.getActiveCanvas().refreshArray.get(j);
connection.reconnectManhattah(false);
connection.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
PMUI.getActiveCanvas()
.triggerConnectionStateChangeEvent(connection);
}
this.containerUndo = false;
}
};
/**
* Returns to the state before this command was executed
*/
CommandSwitchContainer.prototype.undo = function () {
var i,
delta,
shape,
connection,
j,
max;
for (i = 0; i < this.relatedShapes.length; i += 1) {
shape = this.relatedShapes[i];
delta = {
dx: this.before.shapes[i].x - this.after.shapes[i].x,
dy: this.before.shapes[i].y - this.after.shapes[i].y
};
this.before.shapes[i].parent.swapElementContainer(
shape,
this.before.shapes[i].parent,
this.before.shapes[i].x,
this.before.shapes[i].y,
this.before.shapes[i].topLeft
);
shape.refreshChildrenPositions(true, delta);
shape.refreshConnections(false, this.relatedShapes, delta);
}
this.canvas.triggerParentChangeEvent(this.relatedShapes,
this.after.shapes, this.before.shapes);
for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
connection = PMUI.getActiveCanvas().refreshArray.get(j);
connection.reconnectManhattah(false);
connection.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
PMUI.getActiveCanvas()
.triggerConnectionStateChangeEvent(connection);
}
this.containerUndo = true;
};
/**
* Executes the command again after an undo action has been done
*/
CommandSwitchContainer.prototype.redo = function () {
this.execute();
};
PMUI.extendNamespace('PMUI.command.CommandSwitchContainer', CommandSwitchContainer);
}());
(function () {
/**
* @class PMUI.draw.Canvas
* Is the object where all the shapes and drawings will be placed on, in addition it handles zoom operations
* and also the triggering of events.
*
* Below are some unique characteristics of the instances of this class:
*
* - Each instance of this class has an instance of the following:
* - {@link PMUI.command.CommandStack Command stack}
* - {@link PMUI.draw.Snapper Two snappers} (one horizontal snapper and one vertical snapper).
* - {@link PMUI.draw.MultipleSelectionContainer} to select multiple shapes.
* - {@link PMUI.draw.Segment}. To create connections between shapes.
* - {@link PMUI.draw.Canvas#property-customShapes CustomShapes arrayList}. To save all the custom shapes
* created in this canvas.
* - {@link PMUI.draw.Canvas#property-connections Connections arrayList}. To save all the connections
* created in this canvas.
* - {@link PMUI.draw.Canvas#property-currentSelection Current selection arrayList}.
To save all the custom shapes
* that are select (by clicking, ctrl clicking them or selecting them using the
* multipleSelectionContainer instance).
* - {@link PMUI.draw.Canvas#property-currentConnection Current connection}. A pointer to the selected
* connection.
* - {@link PMUI.draw.Canvas#property-currentLabel Current label}. A pointer to the active label.
*
* Besides this class does the following:
*
* - Parses the JSON retrieved from the database (through {@link PMUI.draw.Canvas#parse})
* - Creates some custom events (defined in {@link PMUI.draw.Canvas#attachListeners})
* - Creates, stores and executes the commands (through its {@link PMUI.draw.Canvas#property-commandStack}) property
*
* Below is an example of instantiation of this class:
*
* // The canvas needs an object containing the reference to existing classes outside the library
* // e.g. let's define two classes
* var BpmnActivity = function (options) {
* ...
* };
* var BpmnEvent = function (options) {
* ...
* };
*
* // Next, the canvas needs a factory function to create custom shapes dragged from a toolbar
* // this function needs an ID to create the shape
* function toolbarFactory (id) {
* var customShape = null;
* switch(id) {
* case: 'BpmnActivity':
* customShape = new PMUI.draw.BpmnActivity({
* ....
* });
* break;
* case: 'BpmnEvent':
* customShape = new PMUI.draw.BpmnEvent({
* ....
* });
* break;
* }
* return customShape;
* }
*
* // finally an instance of this class can be defined
* var canvas = new PMUI.draw.Canvas({
* width: 4000,
* height: 4000,
* toolbarFactory: toolbarFactory,
* copyAndPasteReferences: {
* bpmnActivity: BpmnActivity,
* bpmnEvent: BpmnEvent
* }
* });
*
* @extends PMUI.draw.BehavioralElement
*
* @constructor
* Creates an instance of the class
* @param {Object} options configuration options of the canvas
* @cfg {number} [width=4000] Width of this canvas.
* @cfg {number} [height=4000] Height of this canvas.
* @cfg {Function} toolbarFactory Function that will handle object creation
* from a custom toolbar
* @cfg {Object} [copyAndPasteReferences={}] References to the constructors of the classes
* (so that a shape is easily created from the canvas)
* @cfg {boolean} [readOnly=false] Property that determines the permission a
* user has over the canvas
*/
var Canvas = function (options) {
Canvas.superclass.call(this, options);
/**
* Variable that points to the HTML in the DOM of this object.
* @property {HTMLElement} [html=null]
*/
this.html = null;
/**
* A list of all the custom shapes in the canvas.
* @property {PMUI.util.ArrayList}
*/
this.customShapes = null;
/**
* A list of all the regular shapes in the canvas.
* @property {PMUI.util.ArrayList}
*/
this.regularShapes = null;
/**
* A list of all the connections in the canvas.
* @property {PMUI.util.ArrayList}
*/
this.connections = null;
/**
* A list of all the shapes that are currently selected.
* @property {PMUI.util.ArrayList}
*/
this.currentSelection = null;
/**
* A list of all the connections that will not be repainted (using the ManhattanRouter algorithm),
* but will be moved only.
* @property {PMUI.util.ArrayList}
*/
this.sharedConnections = null;
/**
* Left Scroll coordinate of the canvas
* @property {number} [leftScroll=0]
*/
this.leftScroll = 0;
/**
* Top scroll coordinate of the canvas
* @property {number} [topScroll=0]
*/
this.topScroll = 0;
/**
* Reference to the current selected connection in the canvas
* @property {PMUI.draw.Connection}
*/
this.currentConnection = null;
/**
* Pointer to the last connection selected in the canvas
* (this variable is set from the commandDelete)
* @property {PMUI.draw.Connection}
*/
this.oldCurrentConnection = null;
/**
* Instance of the class {@link PMUI.draw.Segment} used to make connections in the canvas.
* @property {PMUI.draw.Segment}
*/
this.connectionSegment = null;
/**
* Instance of the class {@link PMUI.draw.MultipleSelectionContainer} created to do multiple selection
* @property {PMUI.draw.MultipleSelectionContainer}
*/
this.multipleSelectionHelper = null;
/**
* Instance of the class {@link PMUI.draw.Snapper} which represents the horizontal line used for snapping
* @property {PMUI.draw.Snapper}
*/
this.horizontalSnapper = null;
/**
* Instance of the class {@link PMUI.draw.Snapper} which represents the vertical line used for snapping
* @property {PMUI.draw.Snapper}
*/
this.verticalSnapper = null;
/**
* Current zoom Factor of the diagram
* @property {number} [zoomFactor=1]
*/
this.zoomFactor = 1;
/**
* Index for the zoom properties for shapes corresponding to the current
* zoom factor
* @property {number} [zoomPropertiesIndex=2]
*/
this.zoomPropertiesIndex = 2;
/**
* zOrder of the HTML Representation
* @property {number} [zOrder=0]
*/
this.zOrder = 0;
/**
* Boolean set true if the {@link PMUI.draw.Canvas#event-mousedown} event of the canvas is fired,
* it's set to false in the {@link PMUI.draw.Canvas#event-mouseup} event.
* @property {boolean} [isMouseDown=false]
*/
this.isMouseDown = false;
/**
* Current selected shape
* @property {PMUI.draw.Shape}
*/
this.currentShape = null;
/**
* True if the {@link PMUI.draw.Canvas#event-mousedown} event of the canvas is triggered and
* the {@link PMUI.draw.Canvas#event-mousemove} event is triggered, it's set to false in mouseUp event.
* @property {boolean} [isMouseDownAndMove=false]
*/
this.isMouseDownAndMove = false;
/**
* Denotes if there's been a multiple drop prior to a drag end.
* @property {boolean} [multipleDrop=false]
*/
this.multipleDrop = false;
/**
* Denotes if a segment move handler is being dragged. in order not to
* trigger events in the canvas [draggingASegmentHandler=false]
* @property {boolean}
*/
this.draggingASegmentHandler = false;
/**
* Elements that was added, changed or deleted in the canvas.
* @property {Object}
*/
this.updatedElement = null;
/**
* Determines if the canvas has been right clicked at {@link PMUI.draw.Canvas#event-mousedown}
* @property {boolean} [rightClick=false]
*/
this.rightClick = false;
/**
* Each time a shape is moved using the cursors, the following code is executed:
*
* // for each 'connection' that is not in this.sharedConnection and that it's being
* // recalculated (using ManhattanRouter algorithm)
* connection.disconnect().connect()
* .setSegmentMoveHandlers()
* .checkAndCreateIntersectionsWithAll();
*
* So to avoid these operations for each key press of the cursors, let's create a timeout,
* so that only after that timeout has expired the code above will run.
* This variable is a reference to that timeout.
* @property {Object}
*/
this.intersectionTimeout = null;
/**
* Point to the current label that is being edited
* @property {Object}
*/
this.currentLabel = null;
/**
* Instance of the class {@link PMUI.command.CommandStack} to be used in this canvas
* @property {PMUI.command.CommandStack}
*/
this.commandStack = null;
/**
* Array which contains a list of all the objects that were duplicated
* (during copy)
* @property {Array} [shapesToCopy=[]]
*/
this.shapesToCopy = [];
/**
* Array which contains a list of all the connections that were duplicated
* (during copy)
* @property {Array} [connectionsToCopy=[]]
*/
this.connectionsToCopy = [];
/**
* Property that determines the permissions a user has over the canvas
* @property {boolean} [readOnly=false]
*/
this.readOnly = false;
/**
* Layer that prevents the canvas to be altered
* @property {PMUI.draw.ReadOnlyLayer}
*/
this.readOnlyLayer = null;
/**
* Object which holds references to the constructors of the classes
* (so that a shape is easily created from the canvas)
* @property {Object} [copyAndPasteReferences={}]
*/
this.copyAndPasteReferences = {};
/**
* Previous zoom properties index
* @property {number} [prevZoom=1]
*/
this.prevZoom = 1;
/**
* Initializer for labels, so that jQuery can measure the width of a message
* in the first time its created
* @type {HTMLElement}
*/
this.dummyLabelInitializer = null;
this.isDragging = false;
this.isResizing = false;
this.refreshArray = new PMUI.util.ArrayList();
this.connToRefresh = new PMUI.util.ArrayList();
Canvas.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.BehavioralElement', Canvas);
/**
* Type of the instances
* @property {String}
*/
Canvas.prototype.type = "Canvas";
/**
* Family of the instances, this attribute must not be overridden
* @property {String}
* @readonly
*/
Canvas.prototype.family = "Canvas";
/**
* Instance initializer which uses options to extend the config options to initialize the instance.
* The following properties are instantiated in this method:
*
* this.children = new PMUI.util.ArrayList();
* this.customShapes = new PMUI.util.ArrayList();
* this.regularShapes = new PMUI.util.ArrayList();
* this.connections = new PMUI.util.ArrayList();
* this.currentSelection = new PMUI.util.ArrayList();
* this.sharedConnections = new PMUI.util.ArrayList();
* this.commandStack = new PMUI.command.CommandStack(20);
* this.multipleSelectionHelper = new PMUI.draw.MultipleSelectionContainer(this);
* this.horizontalSnapper = new PMUI.draw.Snapper({orientation: 'horizontal', canvas: this});
* this.verticalSnapper = new PMUI.draw.Snapper({orientation: 'vertical', canvas: this});
*
* @param {Object} options The object that contains the config
* @private
*/
Canvas.prototype.init = function (options) {
var canvasPosition,
defaults;
defaults = {
left: 0,
top: 0,
width: 4000,
height: 4000,
copyAndPasteReferences: (options && options.copyAndPasteReferences) || {},
readOnly: false
};
jQuery.extend(true, defaults, options);
if (options) {
this.children = new PMUI.util.ArrayList();
this.customShapes = new PMUI.util.ArrayList();
this.regularShapes = new PMUI.util.ArrayList();
this.connections = new PMUI.util.ArrayList();
this.currentSelection = new PMUI.util.ArrayList();
this.sharedConnections = new PMUI.util.ArrayList();
this.commandStack = new PMUI.command.CommandStack(20);
this.multipleSelectionHelper = new PMUI.draw.MultipleSelectionContainer({
canvas: this
});
this.copyAndPaste = false;
this.copyAndPasteReferences = defaults.copyAndPasteReferences;
this.setShapeFactory(defaults.shapeFactory);
this.setPosition(defaults.left, defaults.top)
.setDimension(defaults.width, defaults.height)
.setCanvas(this)
.setReadOnly(defaults.readOnly);
// create snappers
this.horizontalSnapper = new PMUI.draw.Snapper({
orientation: 'horizontal',
canvas: this
});
this.verticalSnapper = new PMUI.draw.Snapper({
orientation: 'vertical',
canvas: this
});
if (defaults.absoluteX) {
this.absoluteX = defaults.absoluteX;
}
if (defaults.absoluteY) {
this.absoluteY = defaults.absoluteY;
}
if (defaults.readOnly) {
}
}
};
/**
* Overwrite createHTML method
* @return {HTMLElement}
*/
Canvas.prototype.createHTML = function () {
Canvas.superclass.prototype.createHTML.call(this);
this.addElement(this.multipleSelectionHelper, 0, 0, true);
return this.html;
};
/**
* Sets the read and write permissions of the canvas.
* @param {boolean} readOnly Determines if the canvas will be set to read only
* or if it will be editable
* @chainable
*/
Canvas.prototype.setReadOnly = function (readOnly) {
if (readOnly) {
this.readOnly = readOnly;
}
return this;
};
/**
* Sets the canvas to readOnly mode by creating a ReadOnlyLayer instance and appending its html to
* this html
* @chainable
*/
Canvas.prototype.setToReadOnly = function () {
var readOnlyLayer = this.readOnlyLayer;
if (readOnlyLayer && readOnlyLayer.html) {
this.html.appendChild(this.readOnlyLayer.html);
} else {
this.readOnlyLayer = new PMUI.draw.ReadOnlyLayer({
width: this.width,
height: this.height
});
this.html.appendChild(this.readOnlyLayer.html);
}
this.readOnly = true;
return this;
};
/**
* Sets the canvas to read and write mode.
* @chainable
*/
Canvas.prototype.unsetReadOnly = function () {
var readOnlyLayer = this.readOnlyLayer;
this.html.removeChild(readOnlyLayer.getHTML());
this.readOnly = false;
return this;
};
/**
* Sets the position of the canvas.
* @param {number} x x coordinate relative to where the canvas is contained
* @param {number} y y coordinate relative to where the canvas is contained
* @chainable
*/
Canvas.prototype.setPosition = function (x, y) {
this.setX(x);
this.setY(y);
return this;
};
/**
* Sets the x coordinate of the canvas, its zoomX and absoluteX to an equal value.
* @param {number} newX new x coordinate to be applied in the canvas
* @chainable
*/
Canvas.prototype.setX = function (newX) {
this.x = this.zoomX = newX;
this.absoluteX = 0;
return this;
};
/**
* Set the y coordinate of the canvas, its zoomY and absoluteY to an equal value
* @param {number} newY new y coordinate to be applied in the canvas
* @chainable
*/
Canvas.prototype.setY = function (newY) {
this.y = this.zoomY = newY;
this.absoluteY = 0;
return this;
};
/**
* Retrieves the div element that has this canvas id
* @return {HTMLElement}
*/
Canvas.prototype.createHTMLDiv = function () {
return document.getElementById(this.id);
};
/**
* Default shape factory for creating shapes.
* @param {String} id
* @return {PMUI.draw.CustomShape}
* @template
*/
Canvas.prototype.shapeFactory = function (id) {
var customShape = null;
return customShape;
};
/**
* Identifies the family of the shape (which might be *"CustomShape"* or *"RegularShape"*)
* and adds `shape` to either `this.customShapes` or `this.regularShapes`.
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.addToList = function (shape) {
switch (shape.family) {
case "CustomShape":
if (!this.customShapes.contains(shape)) {
this.customShapes.insert(shape);
}
break;
case "RegularShape":
if (!this.regularShapes.contains(shape)) {
this.regularShapes.insert(shape);
}
break;
default:
}
return this;
};
/**
* Hides `this.currentConnection` if there is one.
* @chainable
*/
Canvas.prototype.hideCurrentConnection = function () {
// hide the current connection if there was one
if (this.currentConnection) {
this.currentConnection.hidePortsAndHandlers();
this.currentConnection = null;
}
return this;
};
/**
* Applies a zoom scale to the canvas and all its elements
* @param {number} scale numbered from 1 to n
* @chainable
*/
Canvas.prototype.applyZoom = function (scale) {
// TODO Implement Zoom Constants in utils
var i,
shape,
max,
conn;
if (scale > 0) {
scale -= 1;
this.prevZoom = this.zoomPropertiesIndex;
this.zoomPropertiesIndex = scale;
this.zoomFactor = (scale * 25 + 50) / 100;
}
for (i = 0; i < this.customShapes.getSize(); i += 1) {
shape = this.customShapes.get(i);
shape.applyZoom();
shape.paint();
}
//connection zoom
this.canvas.refreshArray.clear();
for (i = 0, max = this.connections.getSize(); i < max; i += 1) {
conn = this.connections.get(i);
conn.applyZoom();
conn.savePoints();
}
for (i = 0; i < this.regularShapes.getSize(); i += 1) {
shape = this.regularShapes.get(i);
shape.applyZoom();
shape.paint();
}
return this;
};
/**
* Adds a connection to the canvas, appending its html to the DOM and inserting
* it in the list of connections
* @param {PMUI.draw.Connection} conn
* @chainable
*/
Canvas.prototype.addConnection = function (conn) {
this.html.appendChild(conn.getHTML());
this.connections.insert(conn);
this.updatedElement = conn;
return this;
};
/**
* Remove all selected elements, it destroy the shapes and all references to them.
* @chainable
*/
Canvas.prototype.removeElements = function () {
// destroy the shapes (also destroy all the references to them)
var shape,
command;
command = new PMUI.command.CommandDelete(this);
this.commandStack.add(command);
command.execute();
return this;
};
/**
* Moves all the connections of the children of this shape (shape was moved using the cursors but the children
* connections don't know that so move those connections), this method is called from #moveElements.
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.moveAllChildConnections = function (shape) {
var i,
child,
j,
port;
if (shape.child !== null) {
for (i = 0; i < shape.children.getSize(); i += 1) {
child = shape.children.get(i);
child.setPosition(child.x, child.y);
for (j = 0; j < child.getPorts().getSize(); j += 1) {
port = child.getPorts().get(j);
port.setPosition(port.x, port.y);
port.connection.disconnect();
port.connection.connect();
port.connection.savePoints();
}
this.moveAllChildConnections(child);
}
}
return this;
};
/**
* Move all selected elements in one direction, used mainly for keyboard events
* @param {PMUI.draw.Canvas} canvas
* @param {string} direction The direction to move the shapes to
* @param {Function} [hook] Hook used to determine which shapes can be moved with the keyboard,
* the function must receive a shape as its parameter and return true if the shape can be moved, false
* otherwise (if this function is not defined then it's assumed that all shapes are valid
* to be moved).
* // e.g.
* hook = function(shape) {
* return shape.isValidToMove();
* }
*
* @chainable
*/
Canvas.prototype.moveElements = function (canvas, direction, hook) {
var i, j,
shape,
hfactor = 0,
vfactor = 0,
port,
currentSelection = [],
canMove = true,
selection;
switch (direction) {
case 'LEFT':
hfactor = -1;
break;
case 'RIGHT':
hfactor = 1;
break;
case 'TOP':
vfactor = -1;
break;
case 'BOTTOM':
vfactor = 1;
break;
}
selection = canvas.getCurrentSelection();
if (selection.getSize() > 0) {
selection.sort(function (shape1, shape2) {
return shape1.y > shape2.y;
});
if (selection.get(0).y - 4 + vfactor < 0) {
canMove = false;
}
selection.sort(function (shape1, shape2) {
return shape1.x > shape2.x;
});
if (selection.get(0).x - 3 + hfactor < 0) {
canMove = false;
}
for (i = 0; i < selection.getSize(); i += 1) {
shape = selection.get(i);
currentSelection.push(shape);
if (hook && typeof hook === "function" && !hook(shape)) {
canMove = false;
}
if (canMove) {
shape.oldX = shape.x;
shape.oldY = shape.y;
shape.oldAbsoluteX = shape.absoluteX;
shape.oldAbsoluteY = shape.absoluteY;
shape.setPosition(shape.getX() + hfactor, shape.getY() + vfactor);
shape.changePosition(shape.oldX, shape.oldY, shape.oldAbsoluteX,
shape.oldAbsoluteY);
for (j = 0; j < shape.ports.getSize(); j += 1) {
// for each port update its absolute position and repaint its
port = shape.ports.get(j);
port.setPosition(port.x, port.y);
port.connection.disconnect().connect();
port.connection.savePoints();
}
this.moveAllChildConnections(shape);
}
}
clearTimeout(this.intersectionTimeout);
this.intersectionTimeout = window.setTimeout(function (currentSelection) {
var stack = [],
selection = currentSelection || [];
for (i = 0; i < selection.length; i += 1) {
shape = selection[i];
stack.push(shape);
}
while (stack.length > 0) {
shape = stack.pop();
// add the children to the stack
for (i = 0; i < shape.getChildren().getSize(); i += 1) {
stack.push(shape.getChildren().get(i));
}
for (j = 0; j < shape.ports.getSize(); j += 1) {
// for each port update its absolute position and repaint its
port = shape.ports.get(j);
port.connection.disconnect().connect();
port.connection.setSegmentMoveHandlers();
port.connection.checkAndCreateIntersectionsWithAll();
}
}
}, 1000, currentSelection);
}
return this;
};
/**
* Removes `shape` from the its corresponding list in the canvas (the shape has a reference either in
* `this.customShapes` or `this.regularShapes`).
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.removeFromList = function (shape) {
// remove from the current selection
this.currentSelection.remove(shape);
if (shape.family === "CustomShape") {
this.customShapes.remove(shape);
} else if (shape.family === "RegularShape") {
this.regularShapes.remove(shape);
}
return this;
};
/**
* Fixes the data of the snappers recreating the arrays and sorting them,
* this method is called from {@link PMUI.behavior.RegularDragBehavior#onDragStart} (it might
* be an overrided method `onDragStart` if the instance of {@link PMUI.behavior.RegularDragBehavior} was changed).
* @chainable
*/
Canvas.prototype.fixSnapData = function () {
this.horizontalSnapper.createSnapData();
this.verticalSnapper.createSnapData();
this.horizontalSnapper.sortData();
this.verticalSnapper.sortData();
return this;
};
/**
* Shows or hides the snappers according to this criteria:
*
* - To show the vertical snapper
* - `shape.absoluteX` must equal a value in the data of `this.verticalSnapper`
* - `shape.absoluteX + shape.width` must equal a value in the data of `this.verticalSnapper`
*
* - To show the horizontal snapper
* - `shape.absoluteY` must equal a value in the data of `this.horizontalSnapper`
* - `shape.absoluteY + shape.height` must equal a value in the data of `this.horizontalSnapper`
*
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.showOrHideSnappers = function (shape) {
var hSnapper = this.horizontalSnapper,
vSnapper = this.verticalSnapper,
x = shape.getAbsoluteX(),
y = shape.getAbsoluteY(),
width = shape.getZoomWidth(),
height = shape.getZoomHeight();
if (hSnapper.binarySearch(y)) {
hSnapper.setPosition(
this.getLeftScroll() / this.zoomFactor,
y / this.zoomFactor
);
hSnapper.show();
} else if (hSnapper.binarySearch(y + height)) {
hSnapper.setPosition(
this.getLeftScroll() / this.zoomFactor,
(y + height) / this.zoomFactor
);
hSnapper.show();
} else {
hSnapper.hide();
}
if (vSnapper.binarySearch(x)) {
vSnapper.setPosition(
x / this.zoomFactor - this.absoluteX,
this.getTopScroll() / this.zoomFactor
);
vSnapper.show();
} else if (vSnapper.binarySearch(x + width)) {
vSnapper.setPosition(
(x + width) / this.zoomFactor,
this.getTopScroll() / this.zoomFactor
);
vSnapper.show();
} else {
vSnapper.hide();
}
return this;
};
/**
* Empties `this.currentSelection` arrayList, thus hiding the resize handlers
* of each shape that was in it, it also clears `this.sharedConnections` array
* (there's a direct relationship between them).
* @chainable
*/
Canvas.prototype.emptyCurrentSelection = function () {
var i,
shape;
while (this.currentSelection.getSize() > 0) {
shape = this.currentSelection.get(0);
this.removeFromSelection(shape);
}
// also clear the sharedConnections
this.sharedConnections.clear();
return this;
};
/**
* Determines if it's possible to select `newShape` using `referenceShape` as a reference (`newShape` is a valid
* shape to be added to the selection if it has the same parent as `referenceShape`).
* @param {PMUI.draw.Shape} referenceShape shape which parent will be taken as reference
* @param {PMUI.draw.Shape} newShape new selected shape
* @return {boolean}
*/
Canvas.prototype.isValidSelection = function (referenceShape, newShape) {
if (referenceShape.parent === null) {
return newShape.parent === null;
}
if (newShape.parent === null) {
return false;
}
return newShape.parent.id === referenceShape.parent.id;
};
/**
* Adds `shape` to `this.currentSelection` if it meets one of the following rules:
*
* - If `this.currentSelection` is empty then add it to the arrayList
* - If `this.currentSelection` is not empty then check if this candidate shape
* has the same parent as any element in `this.currentSelection`, if so then add it to
* the arrayList.
*
* This method also shows the resize handlers of the shape and adds its connections
* to `this.sharedConnections` if possible.
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.addToSelection = function (shape) {
var currentSelection = this.currentSelection,
firstSelected,
valid,
isEmpty = currentSelection.isEmpty();
if (!isEmpty) {
firstSelected = currentSelection.get(0);
valid = this.isValidSelection(firstSelected, shape);
} else {
valid = true;
}
if (!currentSelection.contains(shape) && valid) {
// increase this shape zIndex
shape.increaseZIndex();
currentSelection.insert(shape);
// add the connections from this shape that are connected
// to another shape in the currentSelection to the
// canvas sharedConnections array
// NOTE: the shape is passed as an argument but its
// connections are stored
if (shape.family === "CustomShape") {
this.addToSharedConnections(shape);
}
shape.selected = true;
shape.showOrHideResizeHandlers(true);
}
return this;
};
/**
* Removes `shape` from `this.currentSelection` (also hiding its resize handlers).
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.removeFromSelection = function (shape) {
shape.decreaseZIndex();
this.removeFromSharedConnections(shape);
this.currentSelection.remove(shape);
shape.selected = false;
shape.showOrHideResizeHandlers(false);
return this;
};
/**
* Removes all the shared connections between `customShape` and every shape
* found in `this.currentSelection`, also the connections inside `customShape` are removed from
* `this.sharedConnections` array.
* @param {PMUI.draw.CustomShape} customShape
* @chainable
*/
Canvas.prototype.removeFromSharedConnections = function (customShape) {
var i,
child,
connection,
sharedConnections = this.sharedConnections;
for (i = 0; i < customShape.getChildren().getSize(); i += 1) {
child = customShape.getChildren().get(i);
this.removeFromSharedConnections(child);
}
if (customShape.ports) {
for (i = 0; i < customShape.ports.getSize(); i += 1) {
connection = customShape.ports.get(i).connection;
if (sharedConnections.find('id', connection.getID())) {
this.sharedConnections.remove(connection);
}
}
}
return this;
};
/**
* Checks if an ancestor of `shape` is in `this.currentSelection`.
* @return {boolean}
*/
Canvas.prototype.findAncestorInCurrentSelection = function (shape) {
if (this.currentSelection.find('id', shape.getID())) {
return true;
}
if (!shape.parent) {
return false;
}
return this.findAncestorInCurrentSelection(shape.parent);
};
/**
* Adds all the connections between `customShape` and another shape in the
* currentSelection to the `sharedConnections` arrayList, also the connections inside
* `customShape` are added to `this.sharedConnections` array.
* @param {PMUI.draw.CustomShape} customShape
* @chainable
*/
Canvas.prototype.addToSharedConnections = function (customShape) {
var i,
child,
connection,
sourceShape,
destShape,
sharedConnections = this.sharedConnections;
for (i = 0; i < customShape.getChildren().getSize(); i += 1) {
child = customShape.getChildren().get(i);
this.addToSharedConnections(child);
}
if (customShape.ports) {
for (i = 0; i < customShape.ports.getSize(); i += 1) {
connection = customShape.ports.get(i).connection;
sourceShape = connection.srcPort.parent;
destShape = connection.destPort.parent;
if (this.findAncestorInCurrentSelection(sourceShape) &&
this.findAncestorInCurrentSelection(destShape) && !sharedConnections.find('id', connection.getID())) {
sharedConnections.insert(connection);
}
}
}
return this;
};
/**
* Removes a connection from `this.connections`.
* @param {PMUI.draw.Connection} conn
* @chainable
*/
Canvas.prototype.removeConnection = function (conn) {
//this.currentSelection.remove(conn);
this.connections.remove(conn);
return this;
};
/**
* Attaches event listeners to this canvas, it also creates some custom triggers
* used to save the data (to send it to the database later).
*
* The events attached to this canvas are:
*
* - {@link PMUI.draw.Canvas#event-mousedown Mouse down event}
* - {@link PMUI.draw.Canvas#event-mousemove Mouse move event}
* - {@link PMUI.draw.Canvas#event-mouseup Mouse up event}
* - {@link PMUI.draw.Canvas#event-click Click event}
* - {@link PMUI.draw.Canvas#event-scroll Scroll event}
*
* The custom events are:
*
* - {@link PMUI.draw.Canvas#event-createelement Create element event}
* - {@link PMUI.draw.Canvas#event-removeelement Remove element event}
* - {@link PMUI.draw.Canvas#event-changeelement Change element event}
* - {@link PMUI.draw.Canvas#event-selectelement Select element event}
* - {@link PMUI.draw.Canvas#event-rightclick Right click event}
*
* This method also initializes jQueryUI's droppable plugin (instantiated as `this.dropBehavior`)
* @chainable
*/
Canvas.prototype.attachListeners = function () {
var $canvas = $(this.html).click(this.onClick(this)),
$canvasContainer = $canvas.parent();
$canvas.mousedown(this.onMouseDown(this));
$canvasContainer.scroll(this.onScroll(this, $canvasContainer));
$canvas.mousemove(this.onMouseMove(this));
$canvas.mouseup(this.onMouseUp(this));
$canvas.on("createelement", this.onCreateElement(this));
$canvas.on("removeelement", this.onRemoveElement(this));
$canvas.on("changeelement", this.onChangeElement(this));
$canvas.on("selectelement", this.onSelectElement(this));
$canvas.on("rightclick", this.onRightClick(this));
$canvas.on("contextmenu", function (e) {
e.preventDefault();
});
this.updateBehaviors();
return this;
};
/**
* This is a hook that will be executed after an element has been created in
* the canvas.
* This hook will be executed every time a shape, a connection, or an
* independent label is created.
* @param {Object} updatedElement
* @param {string} [updatedElement.id] ID of the updated element
* @param {string} [updatedElement.type] Type of the updated element
* @param {PMUI.draw.Shape} [updatedElement.relatedObject] The updated element
* @param {Array} [updatedElement.relatedElements] An array with all the other elements created
* e.g. When executing {@link PMUI.command.CommandDelete#undo CommandDelete.undo()}, multiple elements are created
* at once, so this property will contain all those shapes.
* @template
* @protected
*/
Canvas.prototype.onCreateElementHandler = function (updatedElement) {
};
/**
* @event createelement
* Handler for the custom event createelement, this event fires when an element
* has been created. It executes the hook #onCreateElementHandler
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onCreateElement = function (canvas) {
return function (e, ui) {
canvas.onCreateElementHandler(canvas.updatedElement);
};
};
/**
* This is a hook that will be executed after an element has been deleted in
* the canvas.
* This hook will be executed every time a shape, a connection, or an
* independent label is deleted
* @param {Object} updatedElement
* @param {string} [updatedElement.id] ID of the removed element
* @param {string} [updatedElement.type] Type of the removed element
* @param {PMUI.draw.Shape} [updatedElement.relatedObject] The removed element
* @param {Array} [updatedElement.relatedElements] An array with all the other elements removed
* e.g. When executing {@link PMUI.command.CommandDelete#execute PMUI.command.CommandDelete.execute()},
multiple elements are created
* at once, so this property will contain all those shapes.
* @template
* @protected
*/
Canvas.prototype.onRemoveElementHandler = function (updatedElement) {
return true;
};
/**
* @event removeelement
* Handler for the custom event removeelement, this event fires when an element
* has been deleted. It executes the hook #onRemoveElementHandler
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onRemoveElement = function (canvas) {
return function (e, ui) {
canvas.onRemoveElementHandler(canvas.updatedElement.relatedElements);
};
};
/**
* This is a hook that will be executed after an element has been changed in
* the canvas.
* This hook will be executed every time a shape, a connection, or an
* independent label is changed.
* `arguments[0]` is an array with all the elements that were updated,
* the structure of each element of the array is described below:
*
* {
* id: #, // the id of the updated element
* type: # // the type of the updated element
* fields: [
* {
* field: # // the field that was updated in this element
* oldVal: # // the old value of this shape
* newVal: # // the new value of this shape
* },
* ...
* ]
* }
*
* @param {Array} updatedElements Array with all the elements that were updated.
* @template
* @protected
*/
Canvas.prototype.onChangeElementHandler = function (updatedElements) {
};
/**
* @event changeelement
* Handler for the custom event changeeelement, this event fires when an element
* has been changed. It executes the hook #onChangeElementHandler
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onChangeElement = function (canvas) {
return function (e, ui) {
canvas.onChangeElementHandler(canvas.updatedElement);
};
};
/**
* This is a hook that will be executed after an element has been selected in
* the canvas.
* This hook will be executed every time a shape, a connection, or an
* independent label is selected
* `arguments[0]` is an array with all the elements that were selected,
* the structure of each element of the array is described below:
*
* {
* id: #, // the id of the selected element
* type: # // the type of the selected element
* relatedObject // the selected element
* }
* @param {Array} updatedElements Array with the selected elements
* @protected
* @template
*/
Canvas.prototype.onSelectElementHandler = function (updatedElements) {
};
/**
* @event selectelement
* Handler for the custom event selectelement, this event fires when an element
* has been selected. It executes the hook #onSelectElementHandler
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onSelectElement = function (canvas) {
return function (e, ui) {
canvas.onSelectElementHandler(canvas.updatedElement);
};
};
/**
* This is a hook that will be executed after an element has been right clicked
* in the canvas or the canvas's been right clicked itself.
* This hook will be executed every time a shape, a connection, an
* independent label or the canvas is right clicked
* @param {Object} updatedElement Reference to the last element that was
* right clicked in the canvas
* @param {Object} points x coordinate where the mouse was pressed
* @template
* @protected
*/
Canvas.prototype.onRightClickHandler = function (updatedElement, points) {
};
/**
* @event rightclick
* Handler for the custom event rightclick, this event fires when an element
* has been right clicked. It executes the hook #onRightClickHandler
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onRightClick = function (canvas) {
return function (event, e, element) {
var realPoint = canvas.relativePoint(e);
canvas.updatedElement = element;
canvas.onRightClickHandler(canvas.updatedElement, {
canvas: realPoint,
page: {
x: e.pageX,
y: e.pageY
}
});
};
};
/**
* @event click
* Click event handler, which makes `this.currentLabel` lose its focus.
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onClick = function (canvas) {
return function (e, ui) {
var currentLabel = canvas.currentLabel,
figure,
realPoint,
realPoint,
oldConnection;
if (currentLabel) {
currentLabel.loseFocus();
$(currentLabel.textField).focusout();
}
realPoint = canvas.relativePoint(e);
figure = canvas.getBestConnecion(realPoint);
if (figure !== null) {
oldConnection = canvas.currentConnection;
canvas.emptyCurrentSelection();
if (oldConnection) {
oldConnection.hidePortsAndHandlers();
}
figure.showPortsAndHandlers();
canvas.currentConnection = figure;
}
};
};
Canvas.prototype.getBestConnecion = function (point, lineToIgnore) {
var count = this.getConnections().asArray().length,
i,
line;
for (i = 0; i < count; i += 1) {
line = this.getConnections().get(i);
if (line.hitTest(point)) {
return line;
}
}
return null;
}
/**
* @event mousedown
* MouseDown Handler of the canvas. It does the following:
*
* - Trigger the {@link PMUI.draw.Canvas#event-rightclick Right Click event} if it detects a right click
* - Empties `canvas.currentSelection`
* - Hides `canvas.currentConnection` if there's one
* - Resets the position of `canvas.multipleSelectionContainer` making it visible and setting its
* `[x, y]` to the point where the user did mouse down in the `canvas`.
*
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onMouseDown = function (canvas) {
return function (e, ui) {
var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(),
y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY();
e.preventDefault();
if (e.which === 3) {
canvas.rightClick = true;
$(canvas.html).trigger("rightclick", [e, canvas]);
}
canvas.isMouseDown = true;
canvas.isMouseDownAndMove = false;
// do not create the rectangle selection if a segment handler
// is being dragged
if (canvas.draggingASegmentHandler) {
return;
}
// clear old selection
canvas.emptyCurrentSelection();
// hide the currentConnection if there's one
canvas.hideCurrentConnection();
canvas.multipleSelectionHelper.reset();
canvas.multipleSelectionHelper.setPosition(x / canvas.zoomFactor,
y / canvas.zoomFactor);
canvas.multipleSelectionHelper.oldX = x;
canvas.multipleSelectionHelper.oldY = y;
canvas.multipleSelectionHelper.setVisible(true);
canvas.multipleSelectionHelper.changeOpacity(0.2);
};
};
/**
* @event mousemove
* MouseMove handler of the canvas, it does the following:
*
* - Updates the position and dimension of `canvas.multipleSelectionContainer`
*
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onMouseMove = function (canvas) {
return function (e, ui) {
if (canvas.isMouseDown && !canvas.rightClick) {
canvas.isMouseDownAndMove = true;
var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(),
y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY(),
topLeftX,
topLeftY,
bottomRightX,
bottomRightY;
topLeftX = Math.min(x, canvas.multipleSelectionHelper.oldX);
topLeftY = Math.min(y, canvas.multipleSelectionHelper.oldY);
bottomRightX = Math.max(x, canvas.multipleSelectionHelper.oldX);
bottomRightY = Math.max(y, canvas.multipleSelectionHelper.oldY);
canvas.multipleSelectionHelper.setPosition(
topLeftX / canvas.zoomFactor,
topLeftY / canvas.zoomFactor
);
canvas.multipleSelectionHelper.setDimension(
(bottomRightX - topLeftX) / canvas.zoomFactor,
(bottomRightY - topLeftY) / canvas.zoomFactor
);
}
};
};
/**
* @event mouseup
* MouseUp handler of the canvas. It does the following:
*
* - Wraps the elements that are inside `canvas.multipleSelectionContainer`
* - Resets the state of `canvas.multipleSelectionContainer`
(see {@link PMUI.draw.MultipleSelectionContainer#reset})
*
* @param {PMUI.draw.Canvas} canvas
*/
Canvas.prototype.onMouseUp = function (canvas) {
return function (e, ui) {
var realPoint,
x,
y;
if (canvas.isMouseDownAndMove) {
realPoint = canvas.relativePoint(e);
x = realPoint.x;
y = realPoint.y;
canvas.multipleSelectionHelper.setPosition(
Math.min(x, canvas.multipleSelectionHelper.zoomX) / canvas.zoomFactor,
Math.min(y, canvas.multipleSelectionHelper.zoomY) / canvas.zoomFactor
);
if (canvas.multipleSelectionHelper) {
canvas.multipleSelectionHelper.wrapElements();
}
} else {
if (!canvas.multipleSelectionHelper.wasDragged) {
canvas.multipleSelectionHelper.reset().setVisible(false);
}
if (canvas.isMouseDown) {
canvas.onClickHandler(canvas, x, y);
}
}
canvas.isMouseDown = false;
canvas.isMouseDownAndMove = false;
canvas.rightClick = false;
};
};
/**
* click handler
* MouseClick handler of the canvas. It does the following:
*
* @param {Canvas} canvas
* @param {Number} x
* @param {Number} y
*/
Canvas.prototype.onClickHandler = function () {
};
/**
* @event scroll
* Handler for scrolling, sets the scroll values to the canvas
* @param {PMUI.draw.Canvas} canvas
* @param {Object} $canvasContainer jQuery element that is the container of the `canvas`
*/
Canvas.prototype.onScroll = function (canvas, $canvasContainer) {
return function (e, ui) {
canvas.setLeftScroll($canvasContainer.scrollLeft())
.setTopScroll($canvasContainer.scrollTop());
};
};
/**
* Fires the {@link PMUI.draw.Canvas#event-selectelement} event,
and elaborates the structure of the object that will
* be passed to the handlers.
* @param {Array} selection The `currentSelection` ArrayList of some canvas
* @chainable
*/
Canvas.prototype.triggerSelectEvent = function (selection) {
var i,
elements = [],
current;
for (i = 0; i < selection.length; i += 1) {
current = selection[i];
elements.push({
id: current.id,
type: current.type,
relatedObject: current
});
}
this.updatedElement = elements;
$(this.html).trigger('selectelement');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-rightclick} event and elaborates the structure
* of the object that will be passed to the event.
* @param {PMUI.draw.CustomShape} element The object that's been right clicked on.
* @chainable
*/
Canvas.prototype.triggerRightClickEvent = function (element) {
this.updatedElement = {
id: element.id,
type: element.type,
relatedObject: element
};
$(this.html).trigger('rightclick');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-createelement}
event, and elaborates the structure of the object that will
* be passed to the handlers.
* @param {Object} shape The shape created
* @param {Array} relatedElements The array with the other elements created
* @chainable
*/
Canvas.prototype.triggerCreateEvent = function (shape, relatedElements) {
this.updatedElement = {
id: (shape && shape.id) || null,
type: (shape && shape.type) || null,
relatedObject: shape,
relatedElements: relatedElements
};
if (!this.items.find("id", shape.id)) {
$(this.html).trigger('createelement');
}
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-removeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers.
* @param {PMUI.draw.CustomShape} shape The shape created
* @param {Array} relatedElements The array with the other elements created
* @chainable
*/
Canvas.prototype.triggerRemoveEvent = function (shape, relatedElements) {
this.updatedElement = {
id: (shape && shape.id) || null,
type: (shape && shape.type) || null,
relatedObject: shape,
relatedElements: relatedElements
};
$(this.html).trigger('removeelement');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers, the structure contains the following
fields (considering old values and new values):
*
* - width
* - height
*
* @param {PMUI.draw.CustomShape} shape The shape that updated its dimension
* @param {number} oldWidth The old width of `shape`
* @param {number} oldHeight The old height of `shape`
* @param {number} newWidth The new width of `shape`
* @param {number} newHeight The old height of `shape`
* @chainable
*/
Canvas.prototype.triggerDimensionChangeEvent = function (shape, oldWidth,
oldHeight, newWidth, newHeight) {
this.updatedElement = [{
id: shape.id,
type: shape.type,
fields: [
{
field: "width",
oldVal: oldWidth,
newVal: newWidth
},
{
field: "height",
oldVal: oldHeight,
newVal: newHeight
}
],
relatedObject: shape
}];
$(this.html).trigger('changeelement');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers, the structure contains the following fields
(considering old values and new values):
*
* - x
* - y
* - parent (the shape that is parent of this shape)
* - state (of the connection)
*
* @param {PMUI.draw.Port} port The port updated
* @chainable
*/
Canvas.prototype.triggerPortChangeEvent = function (port) {
this.updatedElement = [{
id: port.getID(),
type: port.type,
fields: [
{
field: 'x',
oldVal: port.getOldX(),
newVal: port.getX()
},
{
field: 'y',
oldVal: port.getOldY(),
newVal: port.getY()
},
{
field: 'parent',
oldVal: port.getOldParent().getID(),
newVal: port.getParent().getID()
},
{
field: 'state',
oldVal: port.connection.getOldPoints(),
newVal: port.connection.savePoints() &&
port.connection.getPoints()
}
],
relatedObject: port
}];
$(this.html).trigger('changeelement');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers, the structure contains the following
fields (considering old values and new values):
*
* - state (of the connection)
*
* @param {PMUI.draw.Connection} connection The connection updated
* @chainable
*/
Canvas.prototype.triggerConnectionStateChangeEvent = function (connection) {
var points = [],
Point = PMUI.util.Point,
point,
i;
connection.savePoints();
for (i = 0; i < connection.points.length; i += 1) {
point = connection.points[i];
points.push(new Point(point.x / this.zoomFactor, point.y / this.zoomFactor));
}
this.updatedElement = [{
id: connection.getID(),
type: connection.type,
fields: [
{
field: 'state',
oldVal: connection.getOldPoints(),
newVal: points
}
],
relatedObject: connection
}];
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers, the structure contains the following
fields (considering old values and new values):
*
* - x
* - y
*
* @param {Array} shapes The shapes that were updated
* @param {Array} before The state of the shapes before they were repositioned
* @param {Array} after The state of the shapes after they were repositioned
* @chainable
*/
Canvas.prototype.triggerPositionChangeEvent = function (shapes, before, after) {
var i,
elements = [];
for (i = 0; i < shapes.length; i += 1) {
elements.push({
id: shapes[i].getID(),
type: shapes[i].type,
fields: [
{
field: "x",
oldVal: before[i].x,
newVal: after[i].x
},
{
field: "y",
oldVal: before[i].y,
newVal: after[i].y
}
],
relatedObject: shapes[i]
});
}
this.updatedElement = elements;
$(this.html).trigger('changeelement');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers, the structure contains the following
fields (considering old values and new values):
*
* - message
*
* @param {PMUI.draw.CustomShape} element The shape that updated one of ots labels
* @param {string} oldText The old text of the label
* @param {string} newText The new text of the label
* @chainable
*/
Canvas.prototype.triggerTextChangeEvent = function (element, oldText, newText) {
this.updatedElement = [{
id: element.id,
type: element.type,
parent: element.parent,
fields: [
{
field: "message",
oldVal: oldText,
newVal: newText
}
],
relatedObject: element
}];
$(this.html).trigger('changeelement');
return this;
};
/**
* Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
and elaborates the structure of the object that will
* be passed to the handlers, the structure contains the following
fields (considering old values and new values):
*
* - parent
* - x
* - y
*
* @param {Array} shapes The shapes that were updated
* @param {Array} before The state of the shapes before they were repositioned
* @param {Array} after The state of the shapes after they were repositioned
* @chainable
*/
Canvas.prototype.triggerParentChangeEvent = function (shapes, before, after) {
var i,
elements = [];
for (i = 0; i < shapes.length; i += 1) {
elements.push({
id: shapes[i].getID(),
type: shapes[i].type,
fields: [
{
field: "parent",
oldParent: before[i].parent,
newVal: after[i].parent
},
{
field: "x",
oldVal: before[i].x,
newVal: after[i].x
},
{
field: "y",
oldVal: before[i].y,
newVal: after[i].y
}
],
relatedObject: shapes[i]
});
}
this.updatedElement = elements;
$(this.html).trigger('changeelement');
return this;
};
/**
* Sets the top scroll of this canvas.
* @param {number} newScroll
* @chainable
*/
Canvas.prototype.setTopScroll = function (newScroll) {
this.topScroll = newScroll;
return this;
};
/**
* Sets the left scroll of this canvas.
* @param {number} newScroll
* @chainable
*/
Canvas.prototype.setLeftScroll = function (newScroll) {
this.leftScroll = newScroll;
return this;
};
/**
* Sets the zoom Factor applied in the canvas
* @param {number} newZoom
* @chainable
*/
Canvas.prototype.setZoomFactor = function (newZoom) {
if (typeof newZoom === "number" && newZoom % 25 === 0 && newZoom > 0) {
this.zoomFactor = newZoom;
}
return this;
};
/**
* Sets the currentConnection of this canvas.
* @param {PMUI.draw.Connection} newConnection
* @chainable
*/
Canvas.prototype.setCurrentConnection = function (newConnection) {
if (newConnection.type === "Connection") {
this.currentConnection = newConnection;
}
return this;
};
/**
* Assigns `newFunction` as `Canvas.prototype.toolbarShapeFactory` so that
* the canvas has a reference to the shapes that will be created when they
* are dragged from the toolbar.
* @param {Function} newFunction
* @chainable
*/
Canvas.prototype.setShapeFactory = function (newFunction) {
Canvas.prototype.shapeFactory = newFunction;
return this;
};
/**
* Gets the current zoom factor applied in the canvas
* @return {number}
*/
Canvas.prototype.getZoomFactor = function () {
return this.zoomFactor;
};
/**
* Gets the index where the zoom properties are located for the current
* zoom factor.
* @return {number}
*/
Canvas.prototype.getZoomPropertiesIndex = function () {
return this.zoomPropertiesIndex;
};
/**
* Gets the segment used to make connections in the canvas.
* @return {PMUI.draw.Segment}
*/
Canvas.prototype.getConnectionSegment = function () {
return this.connectionSegment;
};
/**
* Gets the left scroll position of the canvas.
* @return {number}
*/
Canvas.prototype.getLeftScroll = function () {
return this.leftScroll;
};
/**
* Gets the top scroll position of the canvas.
* @return {number}
*/
Canvas.prototype.getTopScroll = function () {
return this.topScroll;
};
/**
* Gets the current connection stored in this canvas.
* @return {PMUI.draw.Connection}
*/
Canvas.prototype.getCurrentConnection = function () {
return this.currentConnection;
};
/**
* Gets the current selection of this canvas.
* @return {PMUI.util.ArrayList}
*/
Canvas.prototype.getCurrentSelection = function () {
return this.currentSelection;
};
/**
* Gets all the connections of this canvas.
* @return {PMUI.util.ArrayList}
*/
Canvas.prototype.getConnections = function () {
return this.connections;
};
/**
* Gets all the shared connections stored in this canvas.
* @return {PMUI.util.ArrayList}
*/
Canvas.prototype.getSharedConnections = function () {
return this.sharedConnections;
};
/**
* Gets all the custom shapes of the canvas.
* @return {PMUI.util.ArrayList}
*/
Canvas.prototype.getCustomShapes = function () {
return this.customShapes;
};
/**
* Gets all the regular shapes of the canvas.
* @return {PMUI.util.ArrayList}
*/
Canvas.prototype.getRegularShapes = function () {
return this.regularShapes;
};
/**
* Gets the multiple selection container instance.
* @return {PMUI.draw.MultipleSelectionContainer}
*/
Canvas.prototype.getMultipleSelectionHelper = function () {
return this.multipleSelectionHelper;
};
/**
* Gets the horizontal snapper of this canvas.
* @return {PMUI.draw.Snapper}
*/
Canvas.prototype.getHorizontalSnapper = function () {
return this.horizontalSnapper;
};
/**
* Gets the vertical snapper of this canvas.
* @return {PMUI.draw.Snapper}
*/
Canvas.prototype.getVerticalSnapper = function () {
return this.verticalSnapper;
};
/**
* Gets the last updated element in the canvas
* @return {Mixed}
*/
Canvas.prototype.getUpdatedElement = function () {
return this.updatedElement;
};
/**
* Any instance of the class Canvas is not resizable so this method
* will always return false.
* @return {boolean}
*/
Canvas.prototype.isResizable = function () {
return false;
};
/**
* Gets a reference to itself.
* @return {PMUI.draw.Canvas}
*/
Canvas.prototype.getCanvas = function () {
return this;
};
/**
* Undoes the last action in the canvas by calling `this.commandStack.undo`.
* @chainable
*/
Canvas.prototype.undo = function () {
this.commandStack.undo();
return this;
};
/**
* Redoes the last action in the canvas by calling `this.commandStack.redo`.
* @chainable
*/
Canvas.prototype.redo = function () {
this.commandStack.redo();
return this;
};
/**
* Serializes this canvas by serializing its customShapes, regularShapes and connections.
* @return {Object}
* @return {Object} return.customShapes See {@link PMUI.draw.CustomShape#stringify}
* @return {Object} return.regularShapes See {@link PMUI.draw.Shape#stringify}
* @return {Object} return.connections See {@link PMUI.draw.Connection#stringify}
*/
Canvas.prototype.stringify = function () {
var i,
customShapes = [],
regularShapes = [],
connections = [],
inheritedJSON,
thisJSON,
Shape;
// serialize custom shapes
for (i = 0; i < this.customShapes.getSize(); i += 1) {
customShapes.push(this.customShapes.get(i).stringify());
}
// serialize regular shapes
for (i = 0; i < this.regularShapes.getSize(); i += 1) {
regularShapes.push(this.regularShapes.get(i).stringify());
}
// serialize connections shapes
for (i = 0; i < this.connections.getSize(); i += 1) {
connections.push(this.connections.get(i).stringify());
}
inheritedJSON = Shape.prototype.stringify.call(this);
thisJSON = {
customShapes: customShapes,
regularShapes: regularShapes,
connections: connections
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
/**
* Adds shape and its children to `this.shapesToCopy` array so that later
* they can be pasted in the canvas.
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Canvas.prototype.addToShapesToCopy = function (shape) {
var i,
child,
shapeCustomized = shape.stringify();
shapeCustomized.extendedType = shape.extendedType;
this.shapesToCopy.push(shapeCustomized);
for (i = 0; i < shape.getChildren().getSize(); i += 1) {
child = shape.getChildren().get(i);
this.addToShapesToCopy(child);
}
return this;
};
/**
* Duplicates the `this.sharedConnection` array and the shapes stored in `this.currentSelection`
* array (saving them in `this.shapesToCopy` and `this.connectionsToCopy` respectively) so
* that they can be pasted later in the canvas.
* @chainable
*/
Canvas.prototype.copy = function () {
var i,
shape,
connection;
// duplicate shapes
this.shapesToCopy = [];
for (i = 0; i < this.getCurrentSelection().getSize(); i += 1) {
shape = this.getCurrentSelection().get(i);
this.addToShapesToCopy(shape);
}
// duplicate connections
this.connectionsToCopy = [];
for (i = 0; i < this.getSharedConnections().getSize(); i += 1) {
connection = this.getSharedConnections().get(i);
this.connectionsToCopy.push(connection.stringify());
}
return this;
};
/**
* Pastes the shapes saved in `this.shapesToCopy` and the connections saved in `this.connectionsToCopy`
* by calling the #parse method.
*
* Currently the parser is called with these arguments:
*
* {
* shapes: this.shapesToCopy,
* connections: this.connectionsToCopy,
* createCommand: true,
* uniqueID: true,
* selectAfterFinish: true,
* prependMessage: "Copy of ",
* diffX: 100,
* diffY: 100
* }
*
* @chainable
*/
Canvas.prototype.paste = function () {
this.parse({
shapes: this.shapesToCopy,
connections: this.connectionsToCopy,
createCommand: true,
uniqueID: true,
selectAfterFinish: true,
prependMessage: "Copy of ",
diffX: 100,
diffY: 100
});
return this;
};
/**
* Default copy paste factory which creates new instances of {@link PMUI.draw.CustomShape}, its main purpose
* is to create instances using `this.copyAndPasteReferences` (passed through the config options of the canvas)
* which are reference variables to the constructor of some class (a class declared outside the library).
*
* // let's assume that there's a class declared outside the library called BpmnActivity
* var BpmnActivity = function (options) {
* ...
* };
*
* // in the config options of this canvas, we passed a reference to the constructor like this
* var canvas = new PMUI.draw.Canvas({
* ...
* copyAndPasteReferences: {
* bpmnActivity: BpmnActivity
* }
* ...
* });
*
* // so the copyAndPasteFactory will create an instance of the class BpmnActivity
* // using that reference
* // e.g.
* // let's assume that options are the correct configuration options for BpmnActivity
* var bpmnActivityInstance = Canvas.prototype.copyAndPasteFactory('bpmnActivity', options);
*
*
* @param {string} type The type of the shape to be created
* @param {Object} options The config options to be passed to the constructor of the shape
* @return {PMUI.draw.CustomShape} A custom shape or shape created using the reference created before.
*/
Canvas.prototype.shapeFactory = function (type, options) {
if (this.copyAndPasteReferences[type]) {
return new this.copyAndPasteReferences[type](options);
}
return new PMUI.draw.CustomShape(options);
};
/**
* Factory to create connections
* @param {string} type
* @param {Object} options
* @return {Object}
*/
Canvas.prototype.connectionFactory = function (type, options) {
if (type && this.copyAndPasteReferences[type]) {
return new this.copyAndPasteReferences[type](options);
}
return new PMUI.draw.Connection(options);
};
/**
* Transforms an array of objects, each of the form `{id: #, parent: #}` to a tree like object.
* The structure of the returned object (which represents a tree) is:
*
* {
* id_1: [child_1_of_id1, child_2_of_id1, ...],
* id_2: [child_1_of_id2, child_2_of_id2, ...],
* ...
* }
*
* @param {Array} nodes
* @return {Object}
*/
Canvas.prototype.transformToTree = function (nodes) {
var tree = {},
node,
i;
for (i = 0; i < nodes.length; i += 1) {
// node = {id: #, parent: #, order: #}
node = nodes[i];
// create the children of node.id
if (!tree[node.id]) {
tree[node.id] = [];
}
// insert to the children of its parent
if (node.parent) {
// check if the node exists
if (!tree[node.parent]) {
tree[node.parent] = [];
}
// add node to the children of node's parent
tree[node.parent][node.order] = node.id;
}
}
return tree;
};
/**
* Given a tree (with the structure proposed in #transformToTree)
* and a pointer to the root node, perform a levelOrder traversal (BFS)
* of the tree and returning an array with the IDs of each node.
* @param {Object} tree
* @param {String} [root=canvas.getID()] The ID of the root node (might be canvas)
* @return {Array} An array with the IDs of the nodes of the tree in level order traversal
*/
Canvas.prototype.levelOrderTraversal = function (tree, root) {
var queue = [],
processed = [],
top,
realRoot = root || this.getID(),
i;
queue.push(realRoot);
while (queue.length > 0) {
top = queue.shift();
// push the json of the node
processed.push(top);
// push to the queue
for (i = 0; i < tree[top].length; i += 1) {
queue.push(tree[top][i]);
}
}
// return the IDs
return processed;
};
/**
* Parses `options` creating shapes and connections and placing them in this canvas.
* It does the following:
*
* - Creates each shape (in the same order as it is in the array `options.shapes`)
* - Creates each connection (in the same order as it is in the array `options.connections`)
* - Creates the an instance of {@link PMUI.command.CommandPaste} (if possible)
*
* @param {Object} options
* @param {Array} [options.shapes=[]] The config options of each shape to be placed in this canvas.
* @param {Array} [options.connections=[]] The config options of each connection to be placed in this canvas.
* @param {boolean} [options.uniqueID=false] If set to true, it'll assign a unique ID to each shape created.
* @param {boolean} [options.selectAfterFinish=false] If set to true, it'll add the shapes that are
* direct children of this canvas to `this.currentSelection` arrayList.
* @param {string} [options.prependMessage=""] The message to be prepended to each shape's label.
* @param {boolean} [options.createCommand=true] If set to true it'll create a command for each creation
* of a shape and connection (see {@link PMUI.command.CommandCreate},
{@link PMUI.command.CommandConnect}) and save them in
* a {@link PMUI.command.CommandPaste} (for undo-redo purposes).
* @param {number} [options.diffX=0] The number of pixels on the x-coordinate to move the shape on creation
* @param {number} [options.diffY=0] The number of pixels on the y-coordinate to move the shape on creation
* @chainable
*/
Canvas.prototype.parse = function (options) {
var defaults = {
shapes: [],
connections: [],
uniqueID: false,
selectAfterFinish: false,
prependMessage: "",
createCommand: true,
diffX: 0,
diffY: 0
},
i,
j,
id,
oldID,
shape,
points,
shapeOptions,
connection,
connectionOptions,
sourcePort,
sourcePortOptions,
sourceShape,
sourceBorder,
destPort,
destPortOptions,
destShape,
destBorder,
command,
diffX,
diffY,
stackCommandCreate = [],
stackCommandConnect = [],
canvasID = this.getID(),
mapOldId = {},
map = {};
$.extend(true, defaults, options);
// set the differentials (if the shapes are pasted in the canvas)
diffX = defaults.diffX;
diffY = defaults.diffY;
// map the canvas
map[canvasID] = this;
mapOldId[canvasID] = canvasID;
// empty the current selection and sharedConnections as a consequence
// (so that the copy is selected after)
if (defaults.selectAfterFinish) {
this.emptyCurrentSelection();
}
for (i = 0; i < defaults.shapes.length; i += 1) {
shapeOptions = {};
$.extend(true, shapeOptions, defaults.shapes[i]);
// set the canvas of
shapeOptions.canvas = this;
// create a map of the current id with a new id
oldID = shapeOptions.id;
// generate a unique id on user request
if (defaults.uniqueID) {
shapeOptions.id = PMUI.generateUniqueId();
}
mapOldId[oldID] = shapeOptions.id;
// change labels' messages (using prependMessage)
if (shapeOptions.labels) {
for (j = 0; j < shapeOptions.labels.length; j += 1) {
shapeOptions.labels[j].message = defaults.prependMessage +
shapeOptions.labels[j].message;
}
}
// create an instance of the shape based on its type
shape = this.shapeFactory(shapeOptions.extendedType, shapeOptions);
// map the instance with its id
map[shapeOptions.id] = shape;
// if the shapes don't have a valid parent then set the parent
// to be equal to the canvas
// TODO: ADD shapeOptions.topLeftOnCreation TO EACH SHAPE
if (!mapOldId[shapeOptions.parent]) {
this.addElement(shape,
shapeOptions.x + diffX, shapeOptions.y + diffY, true);
} else if (shapeOptions.parent !== canvasID) {
// get the parent of this shape
map[mapOldId[shapeOptions.parent]].addElement(shape, shapeOptions.x,
shapeOptions.y, true);
} else {
// move the shapes a little (so it can be seen that
// they were duplicated)
map[mapOldId[shapeOptions.parent]].addElement(shape,
shapeOptions.x + diffX, shapeOptions.y + diffY, true);
}
// perform some extra actions defined for each shape
shape.parseHook();
shape.attachListeners();
// execute command create but don't add it to the canvas.commandStack
command = new PMUI.command.CommandCreate(shape);
command.execute();
stackCommandCreate.push(command);
}
for (i = 0; i < defaults.connections.length; i += 1) {
connectionOptions = {};
$.extend(true, connectionOptions, defaults.connections[i]);
// state of the connection
points = connectionOptions.state || [];
// determine the shapes
sourcePortOptions = connectionOptions.srcPort;
sourceShape = map[mapOldId[sourcePortOptions.parent]];
sourceBorder = sourceShape.getBorderConsideringLayers();
destPortOptions = connectionOptions.destPort;
destShape = map[mapOldId[destPortOptions.parent]];
destBorder = destShape.getBorderConsideringLayers();
// populate points if points has no info (backwards compatibility,
// e.g. the flow state is null)
if (points.length === 0) {
points.push({
x: sourcePortOptions.x + sourceShape.getAbsoluteX(),
y: sourcePortOptions.y + sourceShape.getAbsoluteY()
});
points.push({
x: destPortOptions.x + destShape.getAbsoluteX(),
y: destPortOptions.y + destShape.getAbsoluteY()
});
}
//create the ports
sourcePort = new PMUI.draw.Port({
width: 8,
height: 8
});
destPort = new PMUI.draw.Port({
width: 8,
height: 8
});
// add the ports to the shapes
// LOGIC: points is an array of points relative to the canvas.
// CustomShape.addPort() requires that the point passed as an argument
// is respect to the shape, so transform the point's coordinates (also
// consider the border)
sourceShape.addPort(
sourcePort,
points[0].x + diffX + sourceBorder -
sourceShape.getAbsoluteX(),
points[0].y + diffX + sourceBorder -
sourceShape.getAbsoluteY()
);
destShape.addPort(
destPort,
points[points.length - 1].x + diffX + destBorder -
destShape.getAbsoluteX(),
points[points.length - 1].y + diffY + destBorder -
destShape.getAbsoluteY(),
false,
sourcePort
);
connection = this.connectionFactory(
connectionOptions.type,
{
srcPort: sourcePort,
destPort: destPort,
canvas: this,
segmentStyle: connectionOptions.segmentStyle
}
);
connection.id = connectionOptions.id || PMUI.generateUniqueId();
if (defaults.uniqueID) {
connection.id = PMUI.generateUniqueId();
}
//set its decorators
connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
width: 11,
height: 11,
canvas: this,
decoratorPrefix: connectionOptions.srcDecoratorPrefix,
decoratorType: "source",
parent: connection
}));
connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
width: 11,
height: 11,
canvas: this,
decoratorPrefix: connectionOptions.destDecoratorPrefix,
decoratorType: "target",
parent: connection
}));
command = new PMUI.command.CommandConnect(connection);
stackCommandConnect.push(command);
// connect the two ports
if (points.length >= 3) {
connection.connect({
algorithm: 'user',
points: connectionOptions.state,
dx: defaults.diffX,
dy: defaults.diffY
});
} else {
// use manhattan
// console.log("manhattan");
connection.connect();
}
connection.setSegmentMoveHandlers();
// add the connection to the canvas, that means insert its html to
// the DOM and adding it to the connections array
this.addConnection(connection);
// now that the connection was drawn try to create the intersections
connection.checkAndCreateIntersectionsWithAll();
//attaching port listeners
sourcePort.attachListeners(sourcePort);
destPort.attachListeners(destPort);
this.triggerCreateEvent(connection, []);
}
// finally add to currentSelection each shape if possible (this method is
// down here because of the zIndex problem with connections)
if (defaults.selectAfterFinish) {
for (id in map) {
if (map.hasOwnProperty(id)) {
if (map[id].family !== 'Canvas') {
this.addToSelection(map[id]);
}
}
}
}
// create command if possible
if (defaults.createCommand) {
this.commandStack.add(new PMUI.command.CommandPaste(this, {
stackCommandCreate: stackCommandCreate,
stackCommandConnect: stackCommandConnect
}));
}
return this;
};
Canvas.prototype.getRelativeX = function () {
return this.x + this.absoluteX;
};
Canvas.prototype.getRelativeY = function () {
return this.y + this.absoluteY;
};
Canvas.prototype.hideAllFocusLabels = function () {
var size = this.customShapes.getSize(),
i,
shape;
for (i = 0; i < size; i += 1) {
shape = this.customShapes.get(i);
shape.labels.get(0).loseFocus();
}
return true;
};
PMUI.extendNamespace('PMUI.draw.Canvas', Canvas);
if (typeof exports !== 'undefined') {
module.exports = Canvas;
}
}());
(function () {
/**
* @abstract
* @class PMUI.draw.Shape
* Represents a shape in the PMDraw framework, shapes can be:
*
* - **Regular shapes** (Ovals, rectangles, polygons)
* - **Custom shapes** (these kind of shapes can have sprites)
*
* A shape has the following characteristics:
*
* - It has a dragBehavior (inherited from {@link PMUI.draw.Core})
* - It has a dropBehavior (inherited from {@link PMUI.draw.BehavioralElement})
* - It has a containerBehavior (inherited from {@link PMUI.draw.BehavioralElement})
* - It has a resizeBehavior (instantiated in this class)
*
* This class cannot be instantiated.
*
* @extend PMUI.draw.BehavioralElement
* @constructor Creates an instance of the class ConnectionDecorator
* @param {Object} options Initialization options
* @cfg {boolean} [topLeft=false] If set to true then when this shape is dragged from the toolbar it'll be created
* and placed in its topLeft coordinate otherwise it'll use the center as its topLeft coordinate
* @cfg {string} [resizeBehavior="no"] Default resize behavior used to create the correct instance in the factory
* @cfg {Object} [resizeHandlers={
* type: "None",
* total: 4,
* resizableStyle: {
* cssProperties: {
* 'background-color': "rgb(0, 255, 0)",
* 'border': '1px solid black'
* }
* },
* nonResizableStyle: {
* cssProperties: {
* 'background-color': "white",
* 'border': '1px solid black'
* }
* }
* }] Default styles to create the instances of the class Style
* @cfg {string} [drag="disabled"] Default drag behavior used to create the correct instance in the factory
*/
var Shape = function (options) {
Shape.superclass.call(this, options);
/**
* Array built when setting the dimension of the shape to store the
* x coordinate of the div corners in clockwise order starting at top left
* @property {Array}
*/
this.xCorners = [0, 0, 0, 0];
/**
* Array built when setting the dimension of the shape to store the
* y coordinate of the div corners in clockwise order starting at top left
* @property {Array}
*/
this.yCorners = [0, 0, 0, 0];
/**
* Array built when setting the dimension of the shape to store the
* x coordinate of the midpoints of each div border in clockwise order
* starting at the top border
* @property {Array}
*/
this.xMidPoints = [0, 0, 0, 0];
/**
* Array built when setting the dimension of the shape to store the
* y coordinate of the midpoints of each div border in clockwise order
* starting at the top border
* @property {Array}
*/
this.yMidPoints = [0, 0, 0, 0];
/**
* List containing the resize Points located in the corner of a div
* @property {PMUI.util.ArrayList}
*/
this.cornerResizeHandlers = new PMUI.util.ArrayList();
/**
* List containing the resize Points located in the middle of a border
* @property {PMUI.util.ArrayList}
*/
this.midResizeHandlers = new PMUI.util.ArrayList();
/**
* Center point of the shape (in the case of a polygon).
* @property {PMUI.util.Point}
*/
this.center = null;
/**
* The parent of this shape.
* @property {PMUI.draw.Shape}
*/
this.parent = null;
/**
* Old parent of this shape (useful to check the previous
* container of this shape).
* @property {PMUI.draw.Shape}
*/
this.oldParent = null;
/**
* Default zOrder of the shape.
* @property {number} [defaultZOrder=1]
*/
this.defaultZOrder = 1;
/**
* Denotes whether this shape is being dragged.
* @property {boolean} [dragging=false]
*/
this.dragging = false;
/**
* Denotes whether this shape was dragged.
* @property {boolean} [wasDragged=false]
*/
this.wasDragged = false;
/**
* Denotes whether this shape was entered by a draggable element.
* @property {boolean} [entered=false]
*/
this.entered = false;
/**
* Determines the resizeBehavior that this object has.
* @property {PMUI.behavior.ResizeBehavior}
*/
this.resizeBehavior = null;
/**
* Determines whether the shape is being resized or not.
* @property {boolean} [resizing=false]
*/
this.resizing = false;
/**
* This shape was repainted.
* @property {boolean}
*/
this.repainted = false;
/**
* Determines whether a shape has fixed Dimensions or not
* @property boolean
*/
this.fixed = true;
/**
* Determines if the shape's been dropped to a different container
* @property {boolean}
*/
this.changedContainer = false;
/**
* Determines whether this shape will be created considering its top-left
* coordinates or its center
* @property {boolean}
*/
this.topLeftOnCreation = false;
/**
* Determines wether the events ["mousedown, mouseup, click"] will be assigned to component.
* @property {boolean}
*/
this.attachEvents = true
// set defaults
Shape.prototype.init.call(this, options);
};
// inherits from BehavioralElement
PMUI.inheritFrom('PMUI.draw.BehavioralElement', Shape);
/**
* Type of each instance of this class
* @property {String}
*/
Shape.prototype.type = "Shape";
/**
* Family of each instance of this class
* @property {String}
*/
Shape.prototype.family = "Shape";
/**
* Instance of RegularDragBehavior (avoiding the creation of multiple same instances)
* @property {PMUI.behavior.DragBehavior} [noDragBehavior=null]
*/
Shape.prototype.noDragBehavior = null;
/**
* Instance of RegularDragBehavior (avoiding the creation of multiple same instances)
* @property {PMUI.behavior.DragBehavior} [regularDragBehavior=null]
*/
Shape.prototype.regularDragBehavior = null;
/**
* Instance of ConnectionDragBehavior (avoiding the creation of multiple same instances)
* @property {PMUI.behavior.ConnectionDragBehavior} [connectionDragBehavior=null]
*/
Shape.prototype.connectionDragBehavior = null;
/**
* Instance of CustomShapeDragBehavior (avoiding the creation of multiple same instances)
* @property {PMUI.behavior.CustomShapeDragBehavior} [customShapeDragBehavior=null]
*/
Shape.prototype.customShapeDragBehavior = null;
/**
* Corner resize identifiers (for jQueryUI Resizable handles)
* @property {Array} [cornersIdentifiers=['nw', 'ne', 'se', 'sw']]
*/
Shape.prototype.cornersIdentifiers = ['nw', 'ne', 'se', 'sw'];
/**
* Mid resize identifiers (for jQueryUI Resizable handles)
* @property {Array} [midPointIdentifiers=['n', 'e', 's', 'w']]
*/
Shape.prototype.midPointIdentifiers = ['n', 'e', 's', 'w'];
/**
* Constant for the maximum z-index
* @property {number} [MAX_ZINDEX=100]
*/
Shape.prototype.MAX_ZINDEX = 100;
/**
* Constant for the default radius used in the class Arc
* @property {number} [DEFAULT_RADIUS=6]
*/
Shape.prototype.DEFAULT_RADIUS = 6;
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains old points and new points
* @private
*/
Shape.prototype.init = function (options) {
var defaults = {
topLeft: false,
resizeBehavior: "no",
resizeHandlers: {
type: "None",
total: 4,
resizableStyle: {
cssProperties: {
'background-color': "rgb(0, 255, 0)",
'border': '1px solid black'
}
},
nonResizableStyle: {
cssProperties: {
'background-color': "white",
'border': '1px solid black'
}
}
},
drag: "disabled",
attachEvents: true
};
$.extend(true, defaults, options);
if (defaults.drag !== "disabled") {
this.setDragBehavior(defaults.drag);
} else {
this.setDragBehavior("nodrag");
}
this.setResizeBehavior(defaults.resizeBehavior);
this.createHandlers(defaults.resizeHandlers.type,
defaults.resizeHandlers.total,
defaults.resizeHandlers.resizableStyle,
defaults.resizeHandlers.nonResizableStyle);
this.topLeftOnCreation = defaults.topLeft;
this.attachEvents = defaults.attachEvents;
};
/**
* Creates handlers according to the `number` of handlers, the `type` of handlers (currently only Rectangle
* is supported), the `resizableStyle` (created in `this.initObject`) and the `nonResizableStyle`
* (created in `this.initObject`).
* @param {string} type
* @param {number} number
* @param {Object} resizableStyle
* @param {Object} nonResizableStyle
* @chainable
*/
Shape.prototype.createHandlers = function (type, number, resizableStyle, nonResizableStyle) {
if (type === "Rectangle") {
var i;
//First determine how many ResizeHandlers we are to create
if (!number || (number !== 8 &&
number !== 4 && number !== 0)) {
number = 4;
}
//Then insert the corners first
for (i = 0; i < number && i < 4; i += 1) {
this.cornerResizeHandlers.insert(
new PMUI.draw.ResizeHandler({
parent: this,
zOrder: PMUI.util.Style.MAX_ZINDEX + 3,
representation: new PMUI.draw.Rectangle(),
orientation: this.cornersIdentifiers[i],
resizableStyle: resizableStyle,
nonResizableStyle: nonResizableStyle
})
);
}
//subtract 4 just added resize points to the total
number -= 4;
//add the rest to the mid list
for (i = 0; i < number; i += 1) {
this.midResizeHandlers.insert(
new PMUI.draw.ResizeHandler({
parent: this,
zOrder: PMUI.util.Style.MAX_ZINDEX + 3,
representation: new PMUI.draw.Rectangle(),
orientation: this.midPointIdentifiers[i],
resizableStyle: resizableStyle,
nonResizableStyle: nonResizableStyle
})
);
}
}
return this;
//console.log(this.cornerResizeHandlers.asArray());
//console.log(this.midResizeHandlers.asArray());
};
/**
* Updates the position of the handlers using `this.cornerResizeHandlers` and `this.midResizeHandlers`.
* NOTE: There's a prerequisite to call this method, `this.setDimensions` must be called first
* because it updated the arrays used by this method.
* @chainable
*/
Shape.prototype.updateHandlers = function () {
var handler,
i;
for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
handler = this.cornerResizeHandlers.get(i);
handler.setPosition(this.xCorners[i] -
Math.round(handler.width / 2) - 1,
this.yCorners[i] - Math.round(handler.height / 2) - 1);
}
for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
handler = this.midResizeHandlers.get(i);
handler.setPosition(this.xMidPoints[i] -
Math.round(handler.width / 2) - 1,
this.yMidPoints[i] - Math.round(handler.height / 2) - 1);
}
return this;
};
/**
* Sets the visibility of the resize handlers
* @param {boolean} visible
* @chainable
*/
Shape.prototype.showOrHideResizeHandlers = function (visible) {
var i;
if (!visible) {
visible = false;
}
for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
this.cornerResizeHandlers.get(i).setVisible(visible);
}
for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
this.midResizeHandlers.get(i).setVisible(visible);
}
return this;
};
/**
* Applies a predefined style to its handlers (which can be resizable style or non resizable style)
* @param {string} styleType
* @chainable
*/
Shape.prototype.applyStyleToHandlers = function (styleType) {
var i;
for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
this.cornerResizeHandlers.get(i)[styleType].applyStyle();
}
for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
this.midResizeHandlers.get(i)[styleType].applyStyle();
}
return this;
};
/**
* Attaches events to this shape (currently mousedown, mouseup and click events).
* This method also instantiates the behaviors defined in the configuration options of the object,
* the behaviors instantiated are:
*
* - drag behavior
* - drop behavior
* - resize behavior
*
* @chainable
*/
Shape.prototype.attachListeners = function () {
var $shape = $(this.html);
if (this.attachEvents && !this.canvas.readOnly) {
$shape.on("mousedown", this.onMouseDown(this));
$shape.on("mouseup", this.onMouseUp(this));
$shape.on("click", this.onClick(this));
this.updateBehaviors();
}
return this;
};
/**
* @event mousedown
* Moused down callback fired when the user mouse downs on the `shape`
* @param {PMUI.draw.Shape} shape
*/
Shape.prototype.onMouseDown = function (shape) {
return function (e, ui) {
};
};
/**
* @event mouseup
* Moused up callback fired when the user mouse ups on the `shape`
* @param {PMUI.draw.Shape} shape
*/
Shape.prototype.onMouseUp = function (shape) {
return function (e, ui) {
};
};
/**
* @event click
* Click callback fired when the user clicks on the `shape`
* @param {PMUI.draw.Shape} shape
*/
Shape.prototype.onClick = function (shape) {
return function (e, ui) {
};
};
/**
* Creates the HTML representation of the shape, besides calling the method `createHTML` of
* the method of its parent, it also adds the resize handlers to the DOM.
* @returns {HTMLElement}
*/
Shape.prototype.createHTML = function () {
var i;
// call the prototype's createHTML
Shape.superclass.prototype.createHTML.call(this);
// add the handlers
for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
this.addResizeHandler(this.cornerResizeHandlers.get(i),
this.xCorners[i], this.yCorners[i]);
}
for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
this.addResizeHandler(this.midResizeHandlers.get(i),
this.xMidPoints[i], this.yMidPoints[i]);
}
return this.html;
};
/**
* Returns true if this object is draggable
* @return {boolean}
*/
Shape.prototype.isDraggable = function () {
return this.drag &&
this.drag.type !== "NoDragBehavior";
};
/**
* Updates the behaviors of this shape (this method is called from `this.attachListeners`).
* This is the method that actually initializes jQueryUI's plugins (during the creation of the
* instance of this shapes, the shape's behaviors are initialized but the init that they do
* initialize jQuery's UI plugins is done through `[behavior].init`).
* @chainable
*/
Shape.prototype.updateBehaviors = function () {
Shape.superclass.prototype.updateBehaviors.call(this);
if (this.drag) {
this.drag.attachDragBehavior(this);
}
if (this.resizeBehavior) {
this.resizeBehavior.init(this);
}
return this;
};
/**
* Adds a `resizeHandler` to the shape at `[x, y]`
* @param {PMUI.draw.ResizeHandler} resizeHandler
* @param {number} x
* @param {number} y
* @chainable
*/
Shape.prototype.addResizeHandler = function (resizeHandler, x, y) {
if (!this.html) {
return;
}
//console.log(resizeHandler.getHTML());
this.html.appendChild(resizeHandler.getHTML());
resizeHandler.setPosition(x - Math.round(resizeHandler.width / 2) - 1,
y - Math.round(resizeHandler.height / 2) - 1);
//add resizable validations
if (this.isResizable()) {
resizeHandler.setCategory("resizable");
}
return this;
};
/**
* Paints the shape performing the following actions:
*
* - Paints its resize handlers
* - Applies the predefined style according to the resize behavior it has
*
* @chainable
*/
Shape.prototype.paint = function () {
var i,
styleToApply;
// // apply predefined style
// this.style.applyStyle();
for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
this.cornerResizeHandlers.get(i).paint();
}
for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
this.midResizeHandlers.get(i).paint();
}
// apply style to the handlers
if (this.resizeBehavior) {
styleToApply = this.resizeBehavior.type === "NoResizeBehavior" ?
"nonResizableStyle" : "resizableStyle";
this.applyStyleToHandlers(styleToApply);
}
return this;
};
Shape.prototype.updateHTML = function () {
return this;
};
/**
* Detaches `this` HTML from the DOM (also removing it from `canvas.customShapes` or `canvas.regularShapes`)
* @chainable
*/
Shape.prototype.saveAndDestroy = function () {
// save the html but detach it from the DOM
this.html = $(this.html).detach()[0];
this.canvas.removeFromList(this);
return this;
};
/**
* Updates the dimensions of this shape according to the dimensions and
* positions of its children
* @param {number} newMargin Padding to be added when a children is near the edge
* @chainable
*/
Shape.prototype.updateSize = function (newMargin) {
var children = this.children,
limits = children.getDimensionLimit(),
left = limits[3],
top = limits[0],
right = limits[1],
bottom = limits[2],
newLeft = this.getX(),
newTop = this.getY(),
newWidth = this.getWidth(),
newHeight = this.getHeight(),
margin,
diffX = 0,
diffY = 0,
positionShift = false,
dimensionIncrement = false;
if (newMargin !== "undefined") {
margin = newMargin;
} else {
margin = 15;
}
if (left < 0) {
diffX = margin - left;
positionShift = true;
this.oldX = this.x;
this.oldAbsoluteX = this.x;
this.oldY = this.y;
this.oldAbsoluteY = this.absoluteY;
}
if (top < 0) {
diffY = margin - top;
positionShift = true;
this.oldX = this.x;
this.oldAbsoluteX = this.x;
this.oldY = this.y;
this.oldAbsoluteY = this.absoluteY;
}
newLeft -= diffX;
newTop -= diffY;
newWidth += diffX;
newHeight += diffY;
if (right > this.width) {
newWidth += right - this.width + margin;
dimensionIncrement = true;
this.oldWidth = this.width;
}
if (bottom > this.height) {
newHeight += bottom - this.height + margin;
dimensionIncrement = true;
this.oldHeight = this.height;
}
// move the shape to the new coordinates
this.setPosition(newLeft, newTop);
// update the shape's dimension
this.setDimension(newWidth, newHeight);
// custom triggers
if (positionShift) {
this.changePosition(this.oldX, this.oldY,
this.absoluteX, this.absoluteY);
}
if (dimensionIncrement) {
this.changeSize(this.oldWidth, this.oldHeight);
}
// move the children
this.updateChildrenPosition(diffX, diffY);
return this;
};
/**
* Applies the actual zoom scale to the corresponding shape
* @chainable
*/
Shape.prototype.applyZoom = function () {
// var zoomFactor = this.canvas.getZoomFactor(),
// zoomIndex = this.canvas.getZoomPropertiesIndex();
this.refreshShape();
return this;
};
/**
* Sets the dimension of this shape, it also updates the arrays `this.xCorners, this.yCorners, this.xMidPoints
* and this.yMidPoints`
* @param {number} width
* @param {number} height
* @chainable
*/
Shape.prototype.setDimension = function (width, height) {
Shape.superclass.prototype.setDimension.call(this, width, height);
if (this.xCorners) {
this.xCorners = [0, Math.round(this.zoomWidth), Math.round(this.zoomWidth), 0];
this.yCorners = [0, 0, Math.round(this.zoomHeight), Math.round(this.zoomHeight)];
this.xMidPoints = [Math.round(this.zoomWidth / 2), Math.round(this.zoomWidth),
Math.round(this.zoomWidth / 2), 0];
this.yMidPoints = [0, Math.round(this.zoomHeight / 2), Math.round(this.zoomHeight),
Math.round(this.zoomHeight / 2)];
this.updateHandlers();
}
return this;
};
/**
* Sets some variables that store what changed during the process of changing the parent and also
* triggers `changeElement` using those variables.
*
* The variables saved in {@link PMUI.draw.Canvas#updatedElement} are:
*
* - x (old x and new x)
* - y (old y and new y)
* - absoluteX (old absoluteX and new absoluteX)
* - absoluteY (old absoluteY and new absoluteY)
* - parent (old parent and new parent)
*
* @param {number} oldX
* @param {number} oldY
* @param {number} oldAbsoluteX
* @param {number} oldAbsoluteY
* @param {Object} oldParent
* @param {PMUI.draw.Canvas} canvas
* @chainable
*/
Shape.prototype.changeParent = function (oldX, oldY, oldAbsoluteX, oldAbsoluteY, oldParent, canvas) {
var fields = [
{
"field": "x",
"oldVal": oldX,
"newVal": this.x
},
{
"field": "y",
"oldVal": oldY,
"newVal": this.y
},
{
"field": "absoluteX",
"oldVal": oldAbsoluteX,
"newVal": this.absoluteX
},
{
"field": "absoluteY",
"oldVal": oldAbsoluteY,
"newVal": this.absoluteY
},
{
"field": "parent",
"oldVal": oldParent,
"newVal": this.parent
}
];
canvas.updatedElement = {
"id": this.id,
"type": this.type,
"fields": fields,
"relatedObject": this
};
$(canvas.html).trigger("changeelement");
return this;
};
/**
* Sets some variables that store what changed during the process of resizing and also
* triggers `changeElement` using those variables.
*
* The variables saved in {@link PMUI.draw.Canvas#updatedElement} are:
*
* - width (old width and new width)
* - height (old height and new height)
*
* @param {number} oldWidth
* @param {number} oldHeight
* @chainable
*/
Shape.prototype.changeSize = function (oldWidth, oldHeight) {
var canvas = this.canvas,
fields = [
{
"field": "width",
"oldVal": oldWidth,
"newVal": this.width
},
{
"field": "height",
"oldVal": oldHeight,
"newVal": this.height
}
];
canvas.updatedElement = {
"id": this.id,
"type": this.type,
"fields": fields,
"relatedObject": this
};
$(canvas.html).trigger("changeelement");
return this;
};
/**
* Sets some variables that store what changed during the process of changing its position and also
* triggers `changeElement` using those variables.
*
* The variables saved in {@link PMUI.draw.Canvas#updatedElement} are:
*
* - x (old x and new x)
* - y (old y and new y)
* - absoluteX (old absoluteX and new absoluteX)
* - absoluteY (old absoluteY and new absoluteY)
*
* @param {number} oldX
* @param {number} oldY
* @param {number} oldAbsoluteX
* @param {number} oldAbsoluteY
* @chainable
*/
Shape.prototype.changePosition = function (oldX, oldY, oldAbsoluteX, oldAbsoluteY) {
var canvas = this.canvas,
fields = [
{
"field": "x",
"oldVal": oldX,
"newVal": this.x
},
{
"field": "y",
"oldVal": oldY,
"newVal": this.y
},
{
"field": "absoluteX",
"oldVal": oldAbsoluteX,
"newVal": this.absoluteX
},
{
"field": "absoluteY",
"oldVal": oldAbsoluteY,
"newVal": this.absoluteY
}
];
canvas.updatedElement = [{
"id": this.id,
"type": this.type,
"fields": fields,
"relatedObject": this
}];
$(canvas.html).trigger("changeelement");
return this;
};
/**
* Sets whether the dimensions are fixed or not
* @param {boolean} fixed
* @chainable
*/
Shape.prototype.setFixed = function (fixed) {
if (typeof fixed === "boolean") {
this.fixed = fixed;
}
return this;
};
/**
* Adds `value` to the z-index of the shape (considering the z-index of its parent), since a shape might have
* children, this method must increase the z-index of each child recursively.
* @param {PMUI.draw.Shape} shape
* @param {number} value
* @chainable
*/
Shape.prototype.fixZIndex = function (shape, value) {
var i,
anotherShape,
port,
srcShape,
destShape,
srcShapeZIndex,
destShapeZIndex,
parentZIndex;
parentZIndex = shape.parent.html.style.zIndex;
shape.setZOrder(
parseInt(parentZIndex, 10) + value + parseInt(shape.defaultZOrder, 10)
);
// fix children zIndex
for (i = 0; i < shape.children.getSize(); i += 1) {
anotherShape = shape.children.get(i);
anotherShape.fixZIndex(anotherShape, 0);
}
// fix connection zIndex
// only if it has ports
if (shape.ports) {
for (i = 0; i < shape.ports.getSize(); i += 1) {
port = shape.ports.get(i);
srcShape = port.connection.srcPort.parent;
destShape = port.connection.destPort.parent;
srcShapeZIndex = parseInt(srcShape.html.style.zIndex, 10);
destShapeZIndex = parseInt(destShape.html.style.zIndex, 10);
port.connection.style.addProperties({
zIndex: Math.max(srcShapeZIndex + 1, destShapeZIndex + 1)
});
}
}
return this;
};
/**
* Increases the zIndex of this shape by Style.MAX_ZINDEX
* @chainable
*/
Shape.prototype.increaseZIndex = function () {
this.fixZIndex(this, PMUI.util.Style.MAX_ZINDEX);
return this;
};
/**
* Decreases the zIndex of this shape back to normal
* @chainable
*/
Shape.prototype.decreaseZIndex = function () {
this.fixZIndex(this, 0);
return this;
};
/**
* Increases the z-index of `shapes`'s ancestors by one
* @param shape
* @chainable
*/
Shape.prototype.increaseParentZIndex = function (shape) {
if (shape.family !== "Canvas") {
shape.style.addProperties({
zIndex: parseInt(shape.html.style.zIndex, 10) + 1
});
shape.increaseParentZIndex(shape.parent);
}
return this;
};
/**
* Decreases the zIndex of `shapes`'s ancestors by one by one
* @param {PMUI.draw.Shape} shape
* @chainable
*/
Shape.prototype.decreaseParentZIndex = function (shape) {
if (shape && shape.family !== "Canvas") {
shape.style.addProperties({
zIndex: parseInt(shape.html.style.zIndex, 10) - 1
});
shape.decreaseParentZIndex(shape.parent);
}
return this;
};
/**
* Sets the determined resize behavior to `this` by calling `this.resizeBehaviorFactory` (which creates or returns
* the instance according to `behavior`) and attaches the drag events to `this`.
* @param {String} behavior
* @chainable
*/
Shape.prototype.setResizeBehavior = function (behavior) {
var factory = new PMUI.behavior.BehaviorFactory({
products: {
"regularresize": PMUI.behavior.RegularResizeBehavior,
"Resize": PMUI.behavior.RegularResizeBehavior,
"yes": PMUI.behavior.RegularResizeBehavior,
"resize": PMUI.behavior.RegularResizeBehavior,
"noresize": PMUI.behavior.NoResizeBehavior,
"NoResize": PMUI.behavior.NoResizeBehavior,
"no": PMUI.behavior.NoResizeBehavior
},
defaultProduct: "noresize"
});
this.resizeBehavior = factory.make(behavior);
if (this.html) {
this.resizeBehavior.init(this);
}
return this;
};
/**
* Returns whether the shape is resizable or not
* @return {boolean}
*/
Shape.prototype.isResizable = function () {
return this.resizeBehavior &&
this.resizeBehavior.type !== "NoResizeBehavior";
};
/**
* Updates the position and dimensions of the shape (useful when the parent of this shape
* has changed positions or dimensions).
* @chainable
*/
Shape.prototype.refreshShape = function () {
this.setPosition(this.x, this.y)
.setDimension(this.width, this.height);
return this;
};
/**
* Abstract method intended to refresh the connections of a shapes
* @abstract
* @chainable
*/
Shape.prototype.refreshConnections = function () {
return this;
};
/**
* Updates the positions of the children of this shape recursively
* @param {boolean} onCommand
* @chainable
*/
Shape.prototype.refreshChildrenPositions = function (onCommand) {
var i,
children = this.children,
child,
relatedShapes = [],
coordinates = [];
for (i = 0; i < children.getSize(); i += 1) {
child = children.get(i);
child.setPosition(child.getX(), child.getY());
if (onCommand) {
child.refreshConnections(false);
}
relatedShapes.push(child);
coordinates.push({
x: child.getX(),
y: child.getY()
});
child.refreshChildrenPositions(onCommand);
}
return this;
};
/**
* Fix connections ports on resize (a container must call this method on resize to reposition its
* ports on resize and the ports of its children)
* @param {boolean} resizing
* @param {boolean} root The currentShape is root?
* @chainable
*/
Shape.prototype.fixConnectionsOnResize = function (resizing, root) {
var i,
port,
child,
connection,
zoomFactor = this.canvas.zoomFactor;
if (root) {
if (this.ports) {
// connections
for (i = 0; i < this.ports.getSize(); i += 1) {
port = this.ports.get(i);
connection = port.connection;
this.recalculatePortPosition(port);
connection.disconnect().connect();
if (!this.resizing) {
connection.setSegmentMoveHandlers();
connection.checkAndCreateIntersectionsWithAll();
}
}
}
} else {
if (this.ports) {
// connections
for (i = 0; i < this.ports.getSize(); i += 1) {
// for each port update its absolute position and
// repaint its connections
port = this.ports.get(i);
connection = port.connection;
port.setPosition(port.x, port.y);
connection.disconnect().connect();
if (!this.resizing) {
connection.setSegmentMoveHandlers();
connection.checkAndCreateIntersectionsWithAll();
}
}
}
}
// children
for (i = 0; i < this.children.getSize(); i += 1) {
child = this.children.get(i);
child.setPosition(child.x, child.y);
child.fixConnectionsOnResize(child.resizing, false);
}
return this;
};
/**
* Serializes this object.
*
* This method adds the following to the object retrieved from {@link PMUI.draw.BehavioralElement#stringify}:
*
* - resizeBehavior
* - resizeHandlers (as defined in the config options)
*
* @return {Object}
*/
Shape.prototype.stringify = function () {
var inheritedJSON = Shape.superclass.prototype.stringify.call(this),
type = (this.savedOptions.resizeHandlers &&
this.savedOptions.resizeHandlers.type) || 'Rectangle',
total = (this.savedOptions.resizeHandlers &&
this.savedOptions.resizeHandlers.total) || 4,
thisJSON = {
resizeBehavior: this.savedOptions.resizeBehavior,
resizeHandlers: {
type: type,
total: total
}
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
/**
* Sets the center of the shape
* @param {number} newCenter
* @throws {Error} parameter newCenter is not an instance of points
* @chainable
*/
Shape.prototype.setCenter = function (newCenter) {
if (newCenter instanceof PMUI.util.Point) {
this.center = newCenter;
} else {
throw new Error("setCenter(): argument is not an instance of Point");
}
return this;
};
/**
* Sets the Parent of the shape (might also trigger the custom event change element if the parameter
* triggerChange is set to true)
* @chainable
* @param {PMUI.draw.Shape} newParent
* @param {boolean} triggerChange
*/
Shape.prototype.setParent = function (newParent, triggerChange) {
if (newParent) {
if (this.canvas && triggerChange) {
this.canvas.updatedElement = {
"id": this.id,
"type": this.type,
"fields": [
{
"field": "parent",
"oldVal": this.parent,
"newVal": newParent
}
]
};
$(this.canvas.html).trigger("changeelement");
}
this.parent = newParent;
}
return this;
};
/**
* Sets the oldParent of the shape.
* @chainable
* @param {PMUI.draw.Shape} oldParent
*/
Shape.prototype.setOldParent = function (oldParent) {
this.oldParent = oldParent;
return this;
};
/**
* Gets the center of the shape.
* @return {PMUI.util.Point}
*/
Shape.prototype.getCenter = function () {
return this.center;
};
/**
* Gets the parent of the shape.
* @return {PMUI.draw.Shape / PMUI.draw.Canvas}
*/
Shape.prototype.getParent = function () {
return this.parent;
};
/**
* Gets the oldParent of the shape
* @return {PMUI.draw.Shape / PMUI.draw.Canvas}
*/
Shape.prototype.getOldParent = function () {
return this.oldParent;
};
/**
* Gets the handles IDs used to initialize jQueryUI's resizable plugin
* @return {Object}
*/
Shape.prototype.getHandlesIDs = function () {
var handlesObject = {},
i;
for (i = 0; i < this.midPointIdentifiers.length; i += 1) {
handlesObject[this.midPointIdentifiers[i]] = '#' +
this.midPointIdentifiers[i] + this.id +
'resizehandler';
}
for (i = 0; i < this.cornersIdentifiers.length; i += 1) {
handlesObject[this.cornersIdentifiers[i]] = '#' +
this.cornersIdentifiers[i] + this.id +
'resizehandler';
}
return handlesObject;
};
/**
* Applies all behavior in a Shape
* @chainable
*/
Shape.prototype.applyBehaviors = function () {
if (this.html) {
if (this.drag) {
this.drag.attachDragBehavior(this);
}
if (this.drop) {
this.drop.attachDropBehavior(this);
}
if (this.resizeBehavior) {
this.resizeBehavior.init(this);
}
}
return this;
};
PMUI.extendNamespace('PMUI.draw.Shape', Shape);
if (typeof exports !== 'undefined') {
module.exports = Shape;
}
}());
(function () {
/**
* @class PMUI.draw.Label
* Creates a an object that can in order to illustrate text in the HTML it can
* be inside a shape or by its own directly in the canvas
*
* //e.g.
* var label = new PMUI.draw.Label({
* //message that the label will display
* message: "This is a label",
* //orientation of the text, can be vertical or horizontal
* orientation: "horizontal",
* //font-family
* fontFamily: "arial",
* //size of the label object not the text
* size: 80,
* //position where it will be located relative to its
* //container
* position: {
* //location can be center, top, bottom among others,
* //relative to its container
* location: "center",
* //How many pixels in the x coordinate and y coordinate
* //we want to move it from its location
* diffX: 2,
* diffY: -1
* },
* //option that determines if the label should update its
* //parent size when it grows
* updateParent: false,
* //label's parent
* parent: canvas
*
* });
* @extends PMUI.draw.Shape
*
* @constructor
* Creates an instance of the class
* @param {Object} options configuration options for the label object
* @cfg {String} [message=""] Message to be displayed
* @cfg {String} [orientation="horizontal"] Orientation of the text, can be
* vertical or horizontal
* @cfg {String} [fontFamily="arial"] Font family we want the message to be
* displayed with
* @cfg {number} [size=0] Size of the label object
* @cfg {Object} [position={
* location: "none",
* diffX: 0,
* diffY: 0
* }] Location where we want the label to be positioned relative to its parent
* @cfg {boolean} [updateParent=false] Determines whether the parent's size
* should be updated when the label increases its size
* @cfg {Object} [parent=null] Label's parent
*/
var Label = function (options) {
Label.superclass.call(this, options);
/**
* The percentage of this label respect to the width of the shape
* in the range(0, 1)
* @property {number}
*/
this.xPercentage = 0;
/**
* The percentage of this label respect to the height of the shape
* in the range(0, 1)
* @property {number}
*/
this.yPercentage = 0;
/**
* Message that the label will display
* @property {String}
*/
this.message = "";
/**
* Orientation of the label
* @property {String}
*/
this.orientation = "";
/**
* HTML span that holds the text display
* @property {HTMLElement}
*/
this.text = null;
/**
* Determines whether a label's parent should be updated when a label
* increases its size
* @property {boolean}
*/
this.updateParent = false;
/**
* Determines the type of overflow this label should have
* @property {boolean}
*/
this.overflow = false;
/**
* XXX
* @property {boolean}
*/
this.onFocus = false;
/**
* Determines the location relative to its parent where this label will be
* positioned
* @property {String}
*/
this.location = "";
/**
* x direction pixels that the label will be moved from its location
* @property {number}
*/
this.diffX = 0;
/**
* y direction pixels that the label will be moved from its location
* @property {number}
*/
this.diffY = 0;
/**
* Determines the font-size to be used in each zoom scale
* @property {Array}
*/
this.fontSizeOnZoom = [];
/**
* The font-size that this label will use to display the message
* @property {number}
*/
this.fontSize = 0;
/**
* html text field for text editing
* @property {HTMLElement}
*/
this.textField = null;
/**
* Initial width
* @property {number}
*/
this.initialWidth = null;
/**
* Min Height
* @property {number}
*/
this.minHeight = null;
Label.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Shape', Label);
/**
* Type of all label instances
* @property {String}
*/
Label.prototype.type = "Label";
/**
* Line height to be considered in the label's message
* @type {number}
*/
Label.prototype.lineHeight = 20;
/**
* Initializer of the object will all the given configuration options
* @param {Object} options
*/
Label.prototype.init = function (options) {
var defaults = {
message: "New Label",
orientation: "horizontal",
fontFamily: "arial",
size: 0,
position: {
location: "none",
diffX: 0,
diffY: 0
},
overflow: false,
updateParent: false,
parent: null
};
this.fontSizeOnZoom = [6, 8, 10, 13, 15];
$.extend(true, defaults, options);
this.setMessage(defaults.message)
.setOverflow(defaults.overflow)
.setMinHeight(defaults.minHeight)
.setUpdateParent(defaults.updateParent)
.setOrientation(defaults.orientation)
.setFontFamily(defaults.fontFamily)
.setFontSize(defaults.size)
.setParent(defaults.parent)
.setLabelPosition(defaults.position.location, defaults.position.diffX,
defaults.position.diffY);
};
/**
* Attach the corresponding listeners to this label
* @chainable
*/
Label.prototype.attachListeners = function () {
var $label = $(this.html),
$label = $(this.text);
var $textField = $(this.textField);
if (!this.html) {
return this;
}
Label.superclass.prototype.attachListeners.call(this);
if (!this.getCanvas().readOnly) {
$($textField).mousedown (function (e) {
e.stopPropagation();
});
$($textField).mouseup (function (e) {
e.stopPropagation();
});
$($textField).click (function (e) {
e.stopPropagation();
});
}
return this;
};
/**
* Creates the HTML of the label, the input text and the span for displaying the
* message
* @return {HTMLElement}
*/
Label.prototype.createHTML = function () {
Label.superclass.prototype.createHTML.call(this);
this.html.style.textAlign = "center";
this.html.style.align = "center";
this.html.style.fontFamily = this.fontFamily;
this.html.style.fontSize = this.fontSize + "pt";
this.textField = document.createElement("input");
this.textField.style.width = "200px";
this.textField.style.position = "absolute";
this.textField.style.display = "none";
this.text = document.createElement("span");
this.text.style.width = "auto";
this.text.style.height = "auto";
this.text.style.lineHeight = this.lineHeight * this.canvas.zoomFactor + "px";
this.text.innerHTML = this.message;
this.html.appendChild(this.text);
this.html.appendChild(this.textField);
this.html.style.zIndex = '120';
return this.html;
};
/**
* Displays the style of the label and adds the corresponding classes for
* rotation
* @chainable
*/
Label.prototype.paint = function () {
var $label = $(this.html);
this.text.style.lineHeight = this.lineHeight * this.canvas.zoomFactor + "px";
this.textField.value = this.message;
this.text.innerHTML = this.message;
this.html.style.verticalAlign = "middle";
if (this.overflow) {
this.html.style.overflow = "hidden";
} else {
this.html.style.overflow = "none";
}
this.displayText(true);
if (this.orientation === "vertical") {
$label.addClass('rotateText');
} else {
$label.removeClass('rotateText');
}
if (this.parent.onUpdateLabel) {
this.parent.onUpdateLabel();
}
return this;
};
/**
* Displays the label's message in its current orientation or the input text
* @param {boolean} display true if we want to display the label's message or
* false for the input text
* @chainable
*/
Label.prototype.displayText = function (display, e) {
var y1 = 0,
y2 = 0,
diffY;
if (display) {
this.text.style.display = "block";
this.textField.style.display = "none";
if (this.orientation === "vertical") {
this.textField.style.left = "0px";
}
} else {
this.textField.style.display = "block";
if (this.orientation === "vertical") {
this.textField.style.left = this.width / 2 + "px";
if (e) {
y1 = this.canvas.getTopScroll() + this.parent.getAbsoluteY() + this.parent.getZoomHeight() / 2;
y2 = e.pageY - this.canvas.getZoomY();
diffY = (y2 - y1);
this.textField.style.top = diffY + "px";
}
}
this.text.style.display = "none";
}
return this;
};
/**
* Sets the message of this label
* @param {String} newMessage
* @chainable
*/
Label.prototype.setMessage = function (newMessage) {
this.message = newMessage;
if (this.text) {
this.text.innerHTML = this.message;
this.textField.value = this.message;
}
return this;
};
/**
* Retrieves the message that this label is displaying
* @return {String}
*/
Label.prototype.getMessage = function () {
return this.message;
};
/**
* Retrieves the message that this label is displaying
* @return {String}
*/
Label.prototype.setMinHeight = function (minHeight) {
this.minHeight = minHeight;
return this;
};
/**
* Sets the orientation of the text
* @param {String} newOrientation It can be vertical or horizontal by default
* @chainable
*/
Label.prototype.setOrientation = function (newOrientation) {
var $label;
this.orientation = newOrientation;
if (!this.html) {
return this;
}
$label = $(this.html);
if (newOrientation === "vertical") {
$label.addClass("rotateText");
} else {
$label.removeClass("rotateText");
}
return this;
};
/**
* Retrieves the orientation of this label's text
* @return {String}
*/
Label.prototype.getOrientation = function () {
return this.orientation;
};
/**
* Sets the font family of this label's displayed text
* @param {String} newFontFamily
* @chainable
*/
Label.prototype.setFontFamily = function (newFontFamily) {
this.fontFamily = newFontFamily;
if (this.html) {
this.html.style.fontFamily = this.fontFamily;
}
return this;
};
/**
* Sets the font-size of this label's displayed text
* @param {String} newFontSize
* @chainable
*/
Label.prototype.setFontSize = function (newFontSize) {
if (newFontSize === 0) {
this.fontSize = this.getZoomFontSize();
} else {
this.fontSize = newFontSize;
}
if (this.html) {
this.html.style.fontSize = this.fontSize + "pt";
}
return this;
};
/**
* Sets the property to determine if a label should update its parent
* @param {boolean} newUpdateParent
* @chainable
*/
Label.prototype.setUpdateParent = function (newUpdateParent) {
this.updateParent = newUpdateParent;
return this;
};
/**
* Sets the overflow property of this label
* @param {boolean} newOverflow
* @chainable
*/
Label.prototype.setOverflow = function (newOverflow) {
this.overflow = newOverflow;
return this;
};
/**
* Sets the position of the label regarding its parent, considering the location
* and x and y differentials
* @param {String} position location where we want to put the label relative to,
* its parent, it can be top-left, top, top-right, center-left, center,
* center-right, bottom-left, bottom, bottom-right
* @param {number} diffX x coordinate pixels to move from its location
* @param {number} diffY y coordinate pixels to move from its location
* @chainable
*/
Label.prototype.setLabelPosition = function (position, diffX, diffY) {
var x,
y,
i,
width = this.zoomWidth,
height = this.zoomHeight,
parent = this.parent,
parentWidth,
parentHeight,
zoomFactor = this.canvas.zoomFactor,
bottomHeightFactor = 4 * zoomFactor,
positionString = [
'top-left',
'top',
'top-right',
'center-left',
'center',
'center-right',
'bottom-left',
'bottom',
'bottom-right'
],
orientation,
orientationIndex = (this.orientation === "vertical") ? 1 : 0,
positionCoordinates;
if (!position || position === "") {
position = "top-left";
}
if (diffX === undefined || diffX === null) {
diffX = 0;
}
if (diffY === undefined || diffY === null) {
diffY = 0;
}
if (parent && parent.family !== "Canvas") {
parentWidth = parent.getZoomWidth();
parentHeight = parent.getZoomHeight();
orientation = [
{x: width / 2, y: 0},
{x: 0, y: height / 2}
];
positionCoordinates = [
{
x: -width / 2,
y: 0
},
{
x: parentWidth / 2 - width / 2,
y: 0
},
{
x: parentWidth - width / 2,
y: 0
},
{
x: -width / 2,
y: parentHeight / 2 - height / 2
},
{
// Changed width by this.zoomWidth and Height for apply zoom by canvas
x: parentWidth / 2 - this.zoomWidth / 2,
y: parentHeight / 2 - this.zoomHeight / 2
},
{
x: parentWidth - width,
y: parentHeight / 2 - height / 2
},
{
x: -width / 2,
y: parentHeight - bottomHeightFactor
},
{
x: parentWidth / 2 - width / 2,
y: parentHeight - bottomHeightFactor
},
{
x: parentWidth - width / 2,
y: parentHeight - bottomHeightFactor
}
];
for (i = 0; i < 9; i += 1) {
if (position === positionString[i]) {
this.setPosition(
positionCoordinates[i].x / zoomFactor + diffX,
positionCoordinates[i].y / zoomFactor + diffY
);
break;
}
}
}
this.location = position;
this.diffX = diffX;
this.diffY = diffY;
return this;
};
/**
* Hides the span showing the label's message and display the input text ready
* to be edited
* @chainable
*/
Label.prototype.getFocus = function (e) {
var $textField = $(this.textField);
this.displayText(false, e);
this.canvas.currentLabel = this;
$($textField).select();
this.onFocus = true;
$label = $(this.html);
$label.removeClass("rotateText");
if (this.parent.corona) {
this.canvas.hideAllCoronas();
this.canvas.cancelConnect();
}
return this;
};
/**
* Hides the input text and display the label's message, and if the message's
* changed, then it executes the editlabel command
* @chainable
*/
Label.prototype.loseFocus = function () {
var command;
this.canvas.currentLabel = null;
if (this.textField.value !== this.message) {
command = new PMUI.command.CommandEditLabel(this, this.textField.value);
command.execute();
this.canvas.commandStack.add(command);
this.setLabelPosition(this.location, this.diffX, this.diffY);
}
this.paint();
this.onFocus = false;
if (this.orientation === "vertical") {
$label = $(this.html);
$label.addClass("rotateText");
}
return this;
};
/**
* On Click handler, used to stop propagation when a label is being edited or
* its parent is the canvas
* @param {PMUI.draw.Label} label
* @return {Function}
*/
Label.prototype.onClick = function (label) {
return function (e, ui) {
if (label.parent.family === "Canvas") {
e.stopPropagation();
}
if (label.onFocus) {
e.stopPropagation();
}
};
};
/**
* Double Click handler, used in order for this label to get focus and being
* edited
* @param {PMUI.draw.Label} label
* @return {Function}
*/
Label.prototype.onDblClick = function (label) {
return function (e, ui) {
var canvas = label.getCanvas(),
$label = $(label.html);
if (canvas.currentLabel) {
canvas.currentLabel.loseFocus();
}
label.getFocus(e);
e.stopPropagation();
//e.preventDefault();
};
};
/**
* Returns the font-size according to the current zoom scale
* @return {number}
*/
Label.prototype.getZoomFontSize = function () {
var canvas = this.canvas;
this.fontSize = this.fontSizeOnZoom[canvas.zoomPropertiesIndex];
return this.fontSize;
};
/**
* Parse the messages in words length.
* It returns an array with the length of all the words in the message
* @return {Array}
*/
Label.prototype.parseMessage = function () {
var i,
start = 0,
result = [],
word;
while (this.message.charAt(start) === ' ') {
start += 1;
}
word = 0;
for (i = start; i < this.message.length; i += 1) {
if (this.message.charAt(i) === ' ') {
result.push(word);
word = 0;
} else {
word += 1;
}
}
result.push(word);
return result;
};
/**
* Updates the dimension of the label, according to its message, and if the
* updateParent property is true then it will call the corresponding method to
* update its parent according to the label's size
* @chainable
*/
Label.prototype.updateDimension = function (firstTime) {
var divWidth = this.width || $(this.text).width(),
newWidth,
newHeight;
divWidth = (this.orientation === "vertical") ? this.parent.height : this.zoomWidth;
if (divWidth === this.parent.width) {
divWidth = divWidth * 0.9;
}
if (divWidth < 10) {
newWidth = this.parent.getWidth() * 0.9;
this.initialWidth = this.parent.getWidth() * 0.9;
} else {
newWidth = divWidth;
}
newHeight = $(this.text).height();
// Old code for this part
this.setDimension(newWidth / this.canvas.zoomFactor,
newHeight / this.canvas.zoomFactor);
if (this.updateParent) {
this.updateParentDimension();
}
this.setLabelPosition(this.location, this.diffX, this.diffY);
this.width = newWidth;
return this;
};
/**
* Apply all properties necessary for this label in a given zoom scale
* @chainable
*/
Label.prototype.applyZoom = function () {
var canvas = this.canvas;
this.setFontSize(0);
this.updateDimension();
return this;
};
/**
* Calls the method to update the label's parent dimension according to the
* label's orientation
* @chainable
*/
Label.prototype.updateParentDimension = function () {
if (this.orientation === "vertical") {
this.updateVertically();
} else {
this.updateHorizontally();
}
if (this.parent.html) {
this.parent.paint();
}
return this;
};
/**
* Updates its parent height according to the size of the label
* @chainable
*/
Label.prototype.updateVertically = function () {
var margin = 5,
parent = this.parent,
labelWidth = this.zoomWidth,
newHeight,
zoomFactor = this.canvas.zoomFactor;
if (this.minHeight || this.maxHeight) {
if (this.zoomHeight < this.maxHeight && this.zoomHeight > this.minHeight)
newHeight = this.zoomHeight;
if (this.zoomHeight > this.maxHeight)
newHeight = this.maxHeight;
if (this.zoomHeight < this.minHeight)
newHeight = this.minHeight;
if (this.zoomHeight > this.minHeight)
newHeight = this.zoomHeight;
}
else {
newHeight = this.zoomHeight;
}
parent.setDimension(parent.width, newHeight / zoomFactor);
parent.updateChildrenPosition(0, 0);
parent.refreshConnections();
this.setLabelPosition(this.location, this.diffX, this.diffY);
return this;
};
/**
* Updates its parent width and height according to the new label's dimension
* @chainable
*/
Label.prototype.updateHorizontally = function () {
var margin = 5,
parent = this.parent,
labelWidth = this.zoomWidth,
labelHeight = this.zoomHeight,
newWidth,
newHeight,
zoomFactor = this.canvas.zoomFactor;
if (labelWidth > parent.zoomWidth - margin * 2) {
newWidth = labelWidth + margin * 2;
} else {
newWidth = parent.zoomWidth;
}
if (labelHeight > parent.zoomHeight - margin * 2) {
newHeight = labelHeight + margin * 2;
} else {
newHeight = parent.zoomHeight;
}
parent.refreshConnections();
this.setLabelPosition(this.location, this.diffX, this.diffY);
return this;
};
/**
* Serializes this object
* @return {Object}
*/
Label.prototype.stringify = function () {
// TODO: USE CLASS STYLE IN THE METHODS OF THIS CLASS
// TODO: COMPLETE THE JSON
/**
* inheritedJSON = {
* id: #
* x: #,
* y: #,
* width: #,
* height: #
* }
* @property {Object}
*/
var inheritedJSON = {},
thisJSON = {
id: this.getID(),
message: this.getMessage(),
orientation: this.getOrientation(),
position: {
location: this.location,
diffX: this.diffX,
diffY: this.diffY
}
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
PMUI.extendNamespace('PMUI.draw.Label', Label);
if (typeof exports !== 'undefined') {
module.exports = Label;
}
}());
(function () {
/**
* @class PMUI.draw.CustomShape
* This is a custom shape, where there can be applied styles, sprites and
* decoration, it can have connections associated to it by ports, different
* layers and labels as well
*
* //e.g.
* var customShape = new PMUI.draw.CustomShape({
* //Determines whether the shape will be connected only in its
* //middle points
* connectAtMiddlePoints : true,
* //The layers that will be instantiated with this shape
* layers: [
* {
* {
* layerName : "first-layer",
* priority: 2,
* visible: true,
* style: {
* cssClasses: ['bpmn_zoom']
* },
* zoomSprites : ['img_50_start',
* 'img_75_start', 'img_100_start',
* 'img_125_start', 'img_150_start']
* }, {
* layerName: "second-layer",
* priority: 3,
* visible: true
* }
*
* ],
* //Labels that belong to this shape
* labels : [
* {
* message: "this is one label",
* position: {
* location : "bottom",
* diffX: 0,
* diffY: 5
* }
* }
* ],
* //The type of connections that are made with this shape,
* //Each type differs of one another for the type of lines
* //used in the connection
* connectionType: "regular"
*
* });
*
* @extends PMUI.draw.Shape
*
* @constructor
* Creates an instance of a CustomShape
* @param {Object} options configuration options used in a custom shape
* @cfg {Boolean} [connectAtMiddlePoints=true] Determines whether shape's,
* connections should be created only in the middle points of its sides
* @cfg {Array} [layers=[]] Configuration options of all layers that will be,
* instantiated with this shape
* @cfg {Array} [labels=[]] Configuration options of all labels that will be
* instantiated with this shape
* @cfg {String} [connectionType="regular"] Type of lines that will be used in
* all connections involving this shape
*/
var CustomShape = function (options) {
/**
* List of all the layers associated to this shape
* @property {PMUI.util.ArrayList}
*/
this.layers = new PMUI.util.ArrayList();
CustomShape.superclass.call(this, options);
/**
* List of all the ports associated to this shape
* @property {PMUI.util.ArrayList}
*/
this.ports = new PMUI.util.ArrayList();
/**
* List of all the labels associated to this shape
* @property {PMUI.util.ArrayList}
*/
this.labels = new PMUI.util.ArrayList();
/**
* List of all the zoom properties in different zoom scales
* @property {PMUI.util.ArrayList}
*/
this.zoomProperties = new PMUI.util.ArrayList();
/**
* Inner figure for drawing connection limits
* @property {Array}
*/
this.limits = [0, 0, 0, 0];
/**
* Border to be added to determine the new position of the port
* @property {Array}
*/
this.border = [
{x: 0, y: 0},
{x: 0, y: 0},
{x: 0, y: 0},
{x: 0, y: 0}
];
/**
* Determines which type of drag behavior should be assigned
* @property {number}
*/
this.dragType = this.CANCEL;
/**
* Reference to the point where a connection drag is being started
* @property {PMUI.util.Point}
*/
this.startConnectionPoint = null;
/**
* if set to true, a port will only be added at its middle points
* @property {Boolean}
*/
this.connectAtMiddlePoints = null;
/**
* Auxiliary property for saving the previous x coordinate in the dragging
* procedure for multiple drag
* @property {Number}
*/
this.previousXDragPosition = 0;
/**
* Auxiliary property for saving the previous y coordinate in the dragging
* procedure for multiple drag
* @property {Number}
*/
this.previousYDragPosition = 0;
/**
* The type of lines for connections made with this shape
* @property {String}
*/
this.connectionType = null;
// init the custom shape
CustomShape.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Shape', CustomShape);
/**
* Type the instances of this class
* @property {String}
*/
CustomShape.prototype.type = "CustomShape";
/**
* Family where this class and all its subclasses belong
* @property {String}
* @readonly
*/
CustomShape.prototype.family = "CustomShape";
/**
* Reference to a drop behaviors for containers
* @property {PMUI.behavior.ContainerDropBehavior}
*/
CustomShape.prototype.containerDropBehavior = null;
/**
* Reference to a drop behavior that allows us to make connections
* @property {PMUI.behavior.ConnectionDropBehavior}
*/
CustomShape.prototype.connectionDropBehavior = null;
/**
* Reference to a drop behavior that has no acceptable droppables
* @property {PMUI.behavior.NoDropBehavior}
*/
CustomShape.prototype.noDropBehavior = null;
/**
* Constant that represents that a drag behavior for making connections should
* be used
* @property {Number}
*/
CustomShape.prototype.CONNECT = 1;
/**
* Constant that represents that a drag behavior for moving the shape should be
* used
* @property {Number}
*/
CustomShape.prototype.DRAG = 2;
/**
* Constant that represents that no drag behavior should be used
* @property {Number}
*/
CustomShape.prototype.CANCEL = 0;
/**
* Initializes the basic attributes for the custom shape, and also the
* particular objects the shape needs to instantiate
* //TODO Base limits on zoom
* @param options
*/
CustomShape.prototype.init = function (options) {
var defaults = {
connectAtMiddlePoints: true,
layers: [],
labels: [],
connectionType: "regular",
drag: "customshapedrag"
},
i;
// init the object with NO configurable options
this.limits = [5, 5, 5, 5, 5];
this.setStartConnectionPoint(new PMUI.util.Point(0, 0));
// init the object with configurable options
$.extend(true, defaults, options);
for (i = 0; i < defaults.layers.length; i += 1) {
this.createLayer(defaults.layers[i]);
}
for (i = 0; i < defaults.labels.length; i += 1) {
this.createLabel(defaults.labels[i]);
}
this.setConnectAtMiddlePoints(defaults.connectAtMiddlePoints)
.setConnectionType(defaults.connectionType);
};
/**
* Creates a layer given its configuration options
* @param {Object} options configuration options
* @return {PMUI.draw.Layer}
*/
CustomShape.prototype.createLayer = function (options) {
var layer;
options.parent = this;
layer = new PMUI.draw.Layer(options);
this.addLayer(layer);
return layer;
};
/**
* Creates a label given its configuration options
* @param {Object} options configuration options for instantiating a label
* @return {PMUI.draw.Label}
*/
CustomShape.prototype.createLabel = function (options) {
var label;
options.canvas = this.canvas;
options.parent = this;
if (options.width === 0) {
options.width = this.width * 0.9;
}
label = new PMUI.draw.Label(options);
this.addLabel(label);
return label;
};
/**
* Adds a label to the array of labels and also appends its html
* @param {PMUI.draw.Label} label
*/
CustomShape.prototype.addLabel = function (label) {
if (this.html) {
//so we just append it to the parent
label.parent = this;
this.html.appendChild(label.getHTML());
}
if (!this.labels.contains(label)) {
this.labels.insert(label);
}
};
/**
* Creates the html for the shape, its layers and labels
* @returns {HTMLElement}
*/
CustomShape.prototype.createHTML = function () {
var i,
label;
CustomShape.superclass.prototype.createHTML.call(this);
this.style.addClasses(["custom_shape"]);
this.layers.sort(PMUI.draw.Layer.prototype.comparisonFunction);
for (i = 0; i < this.layers.getSize(); i += 1) {
this.html.appendChild(this.layers.get(i).getHTML());
}
for (i = 0; i < this.labels.getSize(); i += 1) {
label = this.labels.get(i);
this.addLabel(label);
label.attachListeners();
}
return this.html;
};
/**
* This function will attach all the listeners corresponding to the CustomShape
* @chainable
*/
CustomShape.prototype.attachListeners = function () {
if (this.html === null) {
return this;
}
if (!this.canvas.readOnly) {
var $customShape = $(this.html)
.click(this.onClick(this));
//drag options for the added shapes
$customShape.on("mousedown", this.onMouseDown(this));
$customShape.mousemove(this.onMouseMove(this));
$customShape.mouseup(this.onMouseUp(this));
$customShape.on("contextmenu", function (e) {
e.preventDefault();
});
this.updateBehaviors();
}
return this;
};
/**
* Apply the styles related to the shape, its layers and labels
* @chainable
*/
CustomShape.prototype.paint = function () {
var i,
label;
CustomShape.superclass.prototype.paint.call(this);
//TODO Apply the style of the given shape
for (i = 0; i < this.layers.getSize(); i += 1) {
this.layers.get(i).paint();
}
for (i = 0; i < this.ports.getSize(); i += 1) {
this.ports.get(i).paint();
}
for (i = 0; i < this.labels.getSize(); i += 1) {
label = this.labels.get(i);
label.paint();
}
return this;
};
/**
* Updates properties obtained when the HTML is on the DOM
* @chainable
*/
CustomShape.prototype.updateHTML = function () {
var i,
label;
this.setDimension(this.width, this.height);
for (i = 0; i < this.labels.getSize(); i += 1) {
label = this.labels.get(i);
label.paint();
label.updateDimension();
}
return this;
};
/**
* Verify if a shape is into a set of related shapes
* @param shape {CustmShape} item to test
* @param relatedShapes {Array} items
* @returns {boolean}
*/
CustomShape.prototype.containsElement = function (shape, relatedShapes) {
var i,
max;
if (jQuery.isArray(relatedShapes)) {
for (i = 0, max = relatedShapes.length; i < max; i += 1) {
if (relatedShapes[i].getID() === shape.getID()) {
return true;
}
}
}
return false
};
/**
* Repaints connections related to this shape
* @param {Boolean} inContainer Determines if the points of a connection should
* be saved for its reconstruction
* @chainable
*/
CustomShape.prototype.refreshConnections = function (inContainer,
relatedShapes,
delta) {
var i,
connection,
ports = this.ports,
port,
srcElem,
destElem;
for (i = 0; i < ports.getSize(); i += 1) {
port = ports.get(i);
port.setPosition(port.getX(), port.getY());
connection = port.connection;
srcElem = connection.getSrcPort().parent;
destElem = connection.getDestPort().parent;
if (!(this.containsElement(srcElem, relatedShapes)
&& this.containsElement(destElem, relatedShapes))) {
connection.reconnectManhattah(inContainer);
connection.setSegmentMoveHandlers()
.checkAndCreateIntersectionsWithAll();
this.canvas.triggerConnectionStateChangeEvent(connection);
} else if (delta) {
if (!this.canvas.refreshArray.contains(connection)) {
connection.canvas.refreshArray.insert(connection);
}
}
}
return this;
};
/**
* Updates the properties of this shape layers according to the shape itself
* @chainable
*/
CustomShape.prototype.updateLayers = function () {
var i, j,
layer;
for (i = 0; i < this.getLayers().getSize(); i += 1) {
layer = this.getLayers().get(i);
layer.setProperties();
}
return this;
};
/**
* Returns what it should be the next layer if there is such in the DOM tree
* or null otherwise
* @param {PMUI.draw.Layer} layer
* @returns {PMUI.draw.Layer}
*/
CustomShape.prototype.findLayerPosition = function (layer) {
var nextLayer = null, //holds the next layer regarding the position where
// the new layer should be inserted
minVal = 10000000, //holds the minimum value of all the values greater
// than the newLayer priority
i,
currLayer,
currPriority;
// iterate through all the layers and find the minimum priority of all
// the priorities that are greater than the priority of the current layer
for (i = 0; i < this.layers.getSize(); i += 1) {
currLayer = this.layers.get(i);
currPriority = currLayer.getPriority();
if (currPriority > layer.getPriority()) {
if (minVal > currPriority) {
minVal = currPriority;
nextLayer = currLayer;
}
}
}
return nextLayer;
};
/**
* Adds a new layer to the corresponding shape
* @param {PMUI.draw.Layer} newLayer
* @chainable
*/
CustomShape.prototype.addLayer = function (newLayer) {
// gets the layer that would come next the new one
var nextLayer = this.findLayerPosition(newLayer);
// if there is none it means that the new layer has the highest priority
// of all
if (this.html) {
if (!nextLayer) {
//so we just append it to the parent
this.html.appendChild(newLayer.getHTML());
} else {
//otherwise we append it before nextLayer
this.html.insertBefore(newLayer.getHTML(), nextLayer.getHTML());
}
newLayer.paint();
}
this.layers.insert(newLayer);
return this;
};
/**
* Finds a given layer by ID or null of it doesn't exist
* @param {String} layerID
* @returns {PMUI.draw.Layer}
*/
CustomShape.prototype.findLayer = function (layerID) {
return this.layers.find('id', layerID);
};
/**
* Set the dimension of the customShape
* @param {Number} newWidth
* @param {Number} newHeight
*/
CustomShape.prototype.setDimension = function (newWidth, newHeight) {
CustomShape.superclass.prototype.setDimension.call(this, newWidth, newHeight);
this.updateLabels();
this.updateLayers();
return this;
};
/**
* Updates the labels properties if necessary
* @abstract
* @template
* @protected
*/
CustomShape.prototype.updateLabels = function () {
};
/**
* Makes a layer non-visible
* @param {String} layerID
* @returns {PMUI.draw.CustomShape}
*/
CustomShape.prototype.hideLayer = function (layerID) {
var currLayer;
if (!layerID || typeof layerID !== "string") {
return this;
}
currLayer = this.findLayer(layerID);
if (!currLayer) {
return this;
}
currLayer.setVisible(false);
return this;
};
/**
* Makes a layer visible
* @param {String} layerID
* @returns {PMUI.draw.CustomShape}
*/
CustomShape.prototype.showLayer = function (layerID) {
var currLayer;
if (!layerID || typeof layerID !== "string") {
return this;
}
currLayer = this.findLayer(layerID);
if (!currLayer) {
return this;
}
currLayer.setVisible(true);
return this;
};
/**
* Adds a port to the Shape
* @param {PMUI.draw.Port} port
* @param {Number} xPortCoord
* @param {Number} yPortCoord
* @chainable
*/
CustomShape.prototype.addPort = function (port, xPortCoord, yPortCoord, triggerChange, sourcePort) {
//where the user is attempting to create the port
//TODO Fix trowing custom events by using properties of the objects
var position = new PMUI.util.Point(xPortCoord, yPortCoord);
//set the corresponding shape where the port would be created
port.setParent(this);
port.setCanvas(this.canvas);
this.definePortPosition(port, position, sourcePort);
//append the html to the DOM and paint the port
this.html.appendChild(port.getHTML());
port.paint();
//insert the port to the ports array of the shape
this.ports.insert(port);
return this;
};
/**
*
* Removes a port from the Shape
* @param {PMUI.draw.Port} port
* @chainable
*/
CustomShape.prototype.removePort = function (port) {
this.ports.remove(port);
return this;
};
/**
* Determines the position where the port will be located
* @param {PMUI.draw.Port} port
* @param {PMUI.util.Point} point
* @param {PMUI.draw.Port} sourcePort
* @chainable
*/
CustomShape.prototype.definePortPosition = function (port, point, sourcePort) {
var canvas = this.canvas,
directionArray = [this.TOP, this.RIGHT, this.BOTTOM, this.LEFT],
// midPointArray is used when connectAtMiddlePoints is set to TRUE
midPointArray = [
new PMUI.util.Point(Math.round(this.zoomWidth / 2), 0), // TOP
new PMUI.util.Point(this.zoomWidth, Math.round(this.zoomHeight / 2)), // RIGHT
new PMUI.util.Point(Math.round(this.zoomWidth / 2), this.zoomHeight), // BOTTOM
new PMUI.util.Point(0, Math.round(this.zoomHeight / 2)) // LEFT
],
// sideArray is used when connectAtMiddlePoints is set to FALSE
sideArray = [
new PMUI.util.Point(point.x, 0), // TOP
new PMUI.util.Point(this.getZoomWidth(), point.y), // RIGHT
new PMUI.util.Point(point.x, this.getZoomHeight()), // BOTTOM
new PMUI.util.Point(0, point.y) // LEFT
],
usedArray, // selects either the midPointArray or the side array
direction,
i,
candidateDistance,
minDistance,
option,
border,
directionBorderMultiplier = [-1, 1, 1, -1],
rightBorderMultiplier = [0, 1, 0, 0],
bottomBorderMultiplier = [0, -2, 0, 1];
// if the shape has the connectAtMiddlePoints flag on then use the midPoints
usedArray = this.connectAtMiddlePoints ? midPointArray : sideArray;
//console.log(this.connectAtMiddlePoints, usedArray, point, sourcePort);
// if the shape has a source port available then use manhattan distance
// instead of squaredDistance
option = "getSquaredDistance";
if (sourcePort && this.connectAtMiddlePoints) {
option = "getManhattanDistance";
}
direction = undefined; //obtain location of the port
minDistance = Infinity;
// get the minimum distance between 2 points;
for (i = 0; i < usedArray.length; i += 1) {
candidateDistance = point[option](usedArray[i]);
if (minDistance > candidateDistance) {
minDistance = candidateDistance;
direction = directionArray[i];
}
}
border = this.getBorderConsideringLayers();
for (i = 0; i < 4; i += 1) {
this.border[i].x =
(border * directionBorderMultiplier[i] +
border * rightBorderMultiplier[i]);
this.border[i].y =
(border * directionBorderMultiplier[i] +
border * bottomBorderMultiplier[i]);
}
// because of the zIndex problem move the ports towards the center
// of the shape (this is done when the destDecorator is selected)
port.setDirection(direction);
// setPosition logic:
// since the port must face the border of the shape (or the shape if it
// doesn't have a border) first let's move the port according to the
// direction of the port (up -> -1 * border, right -> 1 * border, bottom ->
// 1 * border, left -> -1 * border)
// after the port will be right in the edge of the shape but now the
// multiplier has also affected the positioning of the port if it's located
// in the right or in the bottom (the port will move 2 * border in the
// y-axis or x-axis) so let's reverse that movement using another array
port.setPosition(
(
usedArray[direction].x
- port.getWidth() / 2
),
(
usedArray[direction].y
- port.getHeight() / 2
)
);
// determines the percentage of port in relation with the shape's width or
// height (useful to determine the new position of the port while resizing)
port.determinePercentage();
return this;
};
/**
* Returns the border of this shape or the border of its layers (max)
* @return {Number}
*/
CustomShape.prototype.getBorderConsideringLayers = function () {
var border = parseInt(this.style.getProperty('borderTopWidth') || 0, 10),
layer,
i;
for (i = 0; i < this.getLayers().getSize(); i += 1) {
layer = this.getLayers().get(i);
border = Math.max(border, parseInt(
layer.style.getProperty('borderTopWidth') || 0,
10
));
}
return border;
};
/**
* Show all the ports of the Shape
* @chainable
*/
CustomShape.prototype.showPorts = function () {
var i;
for (i = 0; i < this.ports.getSize(); i += 1) {
this.ports.get(i).show();
}
return this;
};
/**
* hide all the ports of the Shape
* @chainable
*/
CustomShape.prototype.hidePorts = function () {
var i;
for (i = 0; i < this.ports.getSize(); i += 1) {
this.ports.get(i).hide();
}
return this;
};
/**
* Updates the position of the ports regarding the CustomShape and two
* differentials
* TODO Improve triggering of events with ports own properties
* @param {Number} xDiff
* @param {Number} yDiff
* @chainable
*/
CustomShape.prototype.updatePortsPosition = function (xDiff, yDiff) {
var i,
port,
ports = this.ports;
for (i = 0; i < ports.getSize(); i += 1) {
port = ports.get(i);
if (port.direction === this.RIGHT || port.direction === this.BOTTOM) {
port.oldX = port.x;
port.oldY = port.y;
port.oldAbsoluteX = port.absoluteX;
port.oldAbsoluteY = port.absoluteY;
port.setPosition(port.x + xDiff, port.y + yDiff, true);
port.changePosition(port.oldX, port.oldY, port.oldAbsoluteX,
port.oldAbsoluteY);
} else {
port.setPosition(port.x, port.y, true);
}
port.connection.disconnect().connect();
port.connection.setSegmentMoveHandlers();
}
return this;
};
/**
* Recalculates a port position given the port
* TODO Determine if this method is necessary
* @param {PMUI.draw.Port} port
* @chainable
*/
CustomShape.prototype.recalculatePortPosition = function (port) {
var x = 0,
y = 0;
switch (port.getDirection()) {
case 2:
y = this.getZoomHeight();
case 0:
x = this.getZoomWidth() / 2;
break;
case 1:
x = this.getZoomWidth();
case 3:
y = this.getZoomHeight() / 2;
break;
}
port.setPosition(
Math.round(x - (port.getWidth() / 2)),
Math.round(y - (port.getHeight() /2)));
return this;
};
/**
* Initializes properties to to save the current position of the ports
* @chainable
*/
CustomShape.prototype.initPortsChange = function () {
var i,
ports = this.ports,
port;
for (i = 0; i < ports.getSize(); i += 1) {
port = ports.get(i);
port.oldX = port.x;
port.oldY = port.y;
port.oldAbsoluteX = port.absoluteX;
port.oldAbsoluteY = port.absoluteY;
}
return this;
};
/**
* Trigger to save the port changes
* @chainable
*/
CustomShape.prototype.firePortsChange = function () {
var i,
ports = this.ports,
port;
for (i = 0; i < ports.getSize(); i += 1) {
port = ports.get(i);
// port is not a shape so use call
CustomShape.superclass.prototype.changePosition.call(this, port.oldX, port.oldY,
port.oldAbsoluteX, port.oldAbsoluteY);
}
return this;
};
/**
* Updates ports and connections of the current shape
* @chainable
*/
CustomShape.prototype.refreshShape = function () {
CustomShape.superclass.prototype.refreshShape.call(this);
this.updatePortsOnZoom()
.refreshConnections(false);
this.paint();
return this;
};
/**
* Updates the position of the ports after applying a zoom scale
* @chainable
*/
CustomShape.prototype.updatePortsOnZoom = function () {
var i,
ports = this.ports,
port,
zoomFactor = this.canvas.zoomFactor,
prevZoomFactor = (this.canvas.prevZoom * 25 + 50) / 100,
portFactor = (ports.getSize() > 0) ? ports.get(0).width / 2 : 0,
srcDecorator,
destDecorator,
xCoords = [
this.zoomWidth / 2 - portFactor,
this.zoomWidth - portFactor,
this.zoomWidth / 2 - portFactor,
-portFactor
],
yCoords = [
-portFactor,
this.zoomHeight / 2 - portFactor,
this.zoomHeight - portFactor,
this.zoomHeight / 2 - portFactor
];
for (i = 0; i < ports.getSize(); i += 1) {
port = ports.get(i);
if (this.connectAtMiddlePoints) {
port.setPosition(xCoords[port.direction], yCoords[port.direction]);
} else {
port.setPosition(port.x / prevZoomFactor * zoomFactor,
port.y / prevZoomFactor * zoomFactor);
}
srcDecorator = port.connection.srcDecorator;
destDecorator = port.connection.destDecorator;
if (srcDecorator) {
srcDecorator.applyZoom();
}
if (destDecorator) {
destDecorator.applyZoom();
}
}
return this;
};
/**
* TODO Determine if this method is necessary
*/
CustomShape.prototype.calculateLabelsPercentage = function () {
var i, label;
for (i = 0; i < this.labels.getSize(); i += 1) {
label = this.labels.get(i);
label.xPercentage = label.getX() / this.getWidth();
label.yPercentage = label.getY() / this.getHeight();
}
};
/**
* Updates the labels position according to its configuration properties
* @chainable
*/
CustomShape.prototype.updateLabelsPosition = function () {
var i,
label;
for (i = 0; i < this.labels.getSize(); i += 1) {
label = this.labels.get(i);
label.setLabelPosition(label.location, label.diffX, label.diffY);
}
return this;
};
/**
* Returns the respective drag behavior according to a given point
* @return {Number}
*/
CustomShape.prototype.determineDragBehavior = function (point) {
// limit to consider inside the shape
var Point = PMUI.util.Point,
Geometry = PMUI.draw.Geometry,
limit = this.limits[this.canvas.zoomPropertiesIndex],
border = parseInt(this.style.getProperty('border') || 0, 10);
// if the point is inside the rectangle determine the behavior
// (drag or connect)
if (Geometry.pointInRectangle(point, new Point(0, 0),
new Point(this.zoomWidth + 2 * border,
this.zoomHeight + 2 * border))) {
// if the shape is inside the inner rectangle then drag
if (Geometry.pointInRectangle(point,
new Point(border + limit, border + limit),
new Point(this.zoomWidth + border - limit,
this.zoomHeight + border - limit))) {
return this.DRAG;
}
return this.CONNECT;
}
// if the mouse pointer is outside then return cancel
return this.CANCEL;
};
/**
* Creates a drag helper for drag and drop operations for the helper property
* in jquery ui draggable
* TODO Create a singleton object for this purpose
* @returns {String} html
*/
CustomShape.prototype.createDragHelper = function () {
var html = document.createElement("div");
// can't use class style here
html.style.width = 8 + "px";
html.style.height = 8 + "px";
html.style.backgroundColor = "black";
html.style.zIndex = 2 * PMUI.draw.Shape.prototype.MAX_ZINDEX;
html.id = "drag-helper";
html.className = "drag-helper";
return html;
};
/**
* Handler for the onmousedown event, changes the draggable properties
* according to the drag behavior that is being applied
* @param {PMUI.draw.CustomShape} CustomShape
* @returns {Function}
*/
CustomShape.prototype.onMouseDown = function (customShape) {
return function (e, ui) {
var canvas = customShape.canvas;
if (e.which === 3) {
$(canvas.html).trigger("rightclick", [e, customShape]);
} else {
if (customShape.dragType === customShape.DRAG) {
customShape.setDragBehavior("customshapedrag");
} else if (customShape.dragType === customShape.CONNECT) {
customShape.setDragBehavior("connection");
} else {
customShape.setDragBehavior("nodrag");
}
customShape.dragging = true;
}
e.stopPropagation();
};
};
/**
* On Mouse Up handler it allows the shape to recalculate drag behavior
* whenever there was a mouse down event but no drag involved
* @param {PMUI.draw.CustomShape} customShape
* @return {Function}
*/
CustomShape.prototype.onMouseUp = function (customShape) {
return function (e, ui) {
customShape.dragging = false;
};
};
/**
* Handler for the onmousemove event, determines the drag behavior that is
* being applied, the coordinates where the mouse is currently located and
* changes the mouse cursor
* @param {PMUI.draw.CustomShape} customShape
* @returns {Function}
*/
CustomShape.prototype.onMouseMove = function (customShape) {
return function (e, ui) {
var $customShape,
canvas,
realPoint,
auxPoint;
if (customShape.dragging || customShape.entered) {
return;
}
//TODO ADD TO UTILS A FUNCTION TO RETRIEVE A POINT RESPECTING THE SHAPE
$customShape = $(customShape.html);
canvas = customShape.getCanvas();
realPoint = canvas.relativePoint(e);
customShape.startConnectionPoint.x = realPoint.x - customShape.absoluteX;
customShape.startConnectionPoint.y = realPoint.y - customShape.absoluteY;
//console.log(customShape.startConnectionPoint);
auxPoint = new PMUI.util.Point(e.pageX - canvas.getX() -
customShape.absoluteX + canvas.getLeftScroll(),
e.pageY - canvas.getY() -
customShape.absoluteY + canvas.getTopScroll());
customShape.dragType = customShape
.determineDragBehavior(auxPoint);
//customShape.startConnectionPoint = auxPoint;
if (customShape.dragType === customShape.DRAG) {
$customShape.css('cursor', 'move');
} else if (customShape.dragType === customShape.CONNECT) {
$customShape.css('cursor', 'crosshair');
} else {
$customShape.css('cursor', 'default');
}
//e.stopPropagation();
};
};
/**
* Handler of the onClick Event hides the selected ports and resize Handlers if
* any and show its corresponding resize handler
* @param {PMUI.draw.CustomShape} customShape
* @returns {Function}
*/
CustomShape.prototype.onClick = function (customShape) {
return function (e, ui) {
var isCtrl = false,
canvas = customShape.canvas,
currentSelection = canvas.currentSelection,
currentLabel = canvas.currentLabel;
if (e.ctrlKey) { // Ctrl is also pressed
isCtrl = true;
}
// hide the current connection if there was one
customShape.canvas.hideCurrentConnection();
if (e.which === 3) { // right click
e.preventDefault();
// trigger right click
customShape.canvas.triggerRightClickEvent(customShape);
} else {
if (!customShape.wasDragged) {
// if the custom shape was not dragged (this var is set to true
// in custom_shape_drag_behavior >> onDragEnd)
if (isCtrl) {
if (currentSelection.contains(customShape)) {
// remove from the current selection
canvas.removeFromSelection(customShape);
} else {
// add to the current selection
canvas.addToSelection(customShape);
}
} else {
canvas.emptyCurrentSelection();
canvas.addToSelection(customShape);
}
}
if (!currentSelection.isEmpty()) {
canvas.triggerSelectEvent(currentSelection.asArray());
}
}
if (this.helper) {
$(this.helper.html).remove();
}
// if (currentLabel) {
// currentLabel.loseFocus();
// $(currentLabel.textField).focusout();
// }
customShape.wasDragged = false;
// customShape.canvas.setCurrentShape(customShape);
e.stopPropagation();
};
};
/**
* Empty function to perform some actions when parsing a diagram (called
* from Canvas.parse)
* @template
* @protected
*/
CustomShape.prototype.parseHook = function () {
};
/**
* Returns a list of ports related to the shape
* @returns {PMUI.util.ArrayList}
*/
CustomShape.prototype.getPorts = function () {
return this.ports;
};
/**
* Returns a list of Layers related to the shape
* @returns {PMUI.util.ArrayList}
*/
CustomShape.prototype.getLayers = function () {
return this.layers;
};
/**
* Returns the labels associated to the current shape
* @return {PMUI.util.ArrayList}
*/
CustomShape.prototype.getLabels = function () {
return this.labels;
};
/**
* Applies the current zoom to the corresponding shape its layers and labels
* @chainable
*/
CustomShape.prototype.applyZoom = function () {
var i,
label;
CustomShape.superclass.prototype.applyZoom.call(this);
for (i = 0; i < this.layers.getSize(); i += 1) {
this.layers.get(i).applyZoom();
}
for (i = 0; i < this.labels.getSize(); i += 1) {
label = this.labels.get(i);
label.applyZoom();
label.setLabelPosition(label.location, label.diffX, label.diffY);
//label.setPosition(label.x, label.y);
}
return this;
};
/**
* Sets the start point of a connection corresponding to this shape
* @param {PMUI.util.Point} point
* @chainable
*/
CustomShape.prototype.setStartConnectionPoint = function (point) {
this.startConnectionPoint = point;
return this;
};
/**
* Sets the connectAtMiddlePoints property
* @param {Boolean} connect
* @chainable
*/
CustomShape.prototype.setConnectAtMiddlePoints = function (connect) {
this.connectAtMiddlePoints = connect;
return this;
};
/**
* Returns whether a shape connections will be done only in the middle points of
* its sides or not
* @return {Boolean}
*/
CustomShape.prototype.getConnectAtMiddlePoints = function () {
return this.connectAtMiddlePoints;
};
/**
* Sets the connection type of the shape
* @param {String} newConnType
* @chainable
*/
CustomShape.prototype.setConnectionType = function (newConnType) {
this.connectionType = newConnType;
return this;
};
/**
* Returns the connection type of the shape
* @return {String}
*/
CustomShape.prototype.getConnectionType = function () {
return this.connectionType;
};
/**
* Serializes this object
* @return {Object}
*/
CustomShape.prototype.stringify = function () {
/**
* inheritedJSON = {
* id: #
* x: #,
* y: #,
* width: #,
* height: #
* }
* @property {Object}
*/
var sLayers = [],
labels = [],
i,
inheritedJSON,
thisJSON;
// serialize layers
for (i = 0; i < this.layers.getSize(); i += 1) {
sLayers.push(this.layers.get(i).stringify());
}
// serialize labels
for (i = 0; i < this.labels.getSize(); i += 1) {
labels.push(this.labels.get(i).stringify());
}
inheritedJSON = CustomShape.superclass.prototype.stringify.call(this);
thisJSON = {
canvas: this.canvas.getID(),
layers: sLayers,
labels: labels,
connectAtMiddlePoints: this.getConnectAtMiddlePoints(),
connectionType: this.getConnectionType(),
parent: this.parent.getID()
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
/**
* Builds a custom shape based on the parameter 'json'
* @param {String} json
* @chainable
*/
CustomShape.prototype.parseJSON = function (json) {
this.initObject(json);
return this;
};
PMUI.extendNamespace('PMUI.draw.CustomShape', CustomShape);
if (typeof exports !== 'undefined') {
module.exports = CustomShape;
}
}());
(function () {
/**
* @abstract
* @class PMUI.draw.RegularShape
* @extend PMUI.draw.Shape
* The class RegularShape represents all
* regular shapes created in the canvas such as, rectangles, ovals, ports, and
* handlers
*
* This class will hold all the common behavior of regular shapes
* like rectangles or ovals
*
* @constructor
* Initializes a regular shape
*/
var RegularShape = function (options) {
RegularShape.superclass.call(this, options);
/**
* color of the shape
* @property {PMUI.util.Color}
*/
this.color = new PMUI.util.Color();
/**
* Graphics for this regular shape
*/
this.graphics = null;
};
PMUI.inheritFrom('PMUI.draw.Shape', RegularShape);
/**
* Type of the shape
* @property {String}
*/
RegularShape.prototype.type = "RegularShape";
/**
* Returns the color of the shape
* @returns {PMUI.util.Color}
*/
RegularShape.prototype.getColor = function () {
return this.color;
};
/**
* Sets the color of the shape
* @returns {PMUI.draw.RegularShape}
* @param {PMUI.util.Color} newColor
*/
RegularShape.prototype.setColor = function (newColor) {
if (newColor.type && newColor.type === "Color") {
this.color = newColor;
}
return this;
};
PMUI.extendNamespace('PMUI.draw.RegularShape', RegularShape);
if (typeof exports !== 'undefined') {
module.exports = RegularShape;
}
}());
(function () {
/**
* @class PMUI.draw.Arc
* @extend PMUI.draw.RegularShape
* Class arc that draws arcs using the HTMLElement draw engine.
* Since the canvas is inverted in the y-axis the angles are:
*
* - TOP 270
* - RIGHT 0
* - BOTTOM 90
* - LEFT 180
*
* An arc can be defined with the following elements:
*
* - *startAngle* start angle `0 <= startAngle < 360`.
* - *endAngle* end angle `0 <= endAngle < 360`.
* - *radius* the radius of the circle.
*
* Besides, the HTMLElement draw engine needs another parameter called *step*, this field tells the engine to
* draw only at '*step*' steps
*
* e.g.
* startAngle = 90
* endAngle = 150
* engine.paint() with step = 10:
* 90, 100, 110, 120, 130, 140, 150
* engine.paint() with step = 20:
* 90, 110, 130, 150
*
* As a consequence of the y-axis being inverted,we start drawing from the end angle towards the start angle,
* therefore to draw an arc from 0 deg to 90 deg we must invert the parameters
*
* var a = new PMUI.draw.Arc({
* center: new PMUI.util.Point(10, 10),
* radius: 200,
* startAngle: 270,
* endAngle: 0
* });
*
* @param {Object} options Initialization options
* @cfg {PMUI.util.Point} [center=new PMUI.util.Points(0, 0)] Point representing the center of the arc
* @cfg {number} [radius=Shape.prototype.DEFAULT_RADIUS] radius of the arc
* @cfg {number} [startAngle=270] start angle of the arc
* @cfg {number} [endAngle=90] end angle of the arc
* @cfg {number} [step=10] steps to jump from end angle (to get to start angle)
* @constructor Creates a new instance of arc
*/
var Arc = function (options) {
Arc.superclass.call(this);
/**
* Start angle of this arc
* @property {number}
*/
this.startAngle = null;
/**
* End angle of this arc
* @property {number}
*/
this.endAngle = null;
/**
* Radius of the arc
* @property {number}
*/
this.radius = null;
/**
* Steps to draw the arc
* @property {number}
*/
this.step = null;
// set defaults
Arc.prototype.init.call(this, options);
};
// prototype chaining
PMUI.inheritFrom('PMUI.draw.RegularShape', Arc);
/**
* Type of this shape
* @property {String}
*/
Arc.prototype.type = "Arc";
/**
* Instance initializer which uses options to extend the config options to
* initialize the instance
* @private
* @param {Object} options
*/
Arc.prototype.init = function (options) {
// Default options for the object
var defaults = {
center: new PMUI.util.Point(0, 0),
radius: PMUI.draw.Shape.prototype.DEFAULT_RADIUS,
startAngle: 270,
endAngle: 90,
step: 10
};
// extend recursively defaults with the given options
$.extend(true, defaults, options);
// call setters using the defaults object
this.setCenter(defaults.center)
.setStartAngle(defaults.startAngle)
.setEndAngle(defaults.endAngle)
.setRadius(defaults.radius)
.setStep(defaults.step);
// change the id (to debug easier)
this.id += "-ARC";
};
/**
* In charge of the painting / positioning of the figure on
* the DOM and setting the styles
* @chainable
*/
Arc.prototype.paint = function () {
this.setVisible(this.visible);
if (this.html) {
this.style.applyStyle();
// this.graphic is inherited from RegularShape
this.graphics = new PMUI.draw.Graphics(this.id);
this.graphics.setColor("black");
this.graphics.drawArc(this.center.x, this.center.y, this.radius,
this.startAngle, this.endAngle, this.step);
this.graphics.graphics.paint();
}
return this;
};
/**
* Creates the HTML representation of the Arc
* @returns {HTMLElement}
*/
Arc.prototype.createHTML = function () {
Arc.superclass.prototype.createHTML.call(this);
return this.html;
};
/**
* Returns the startAngle of the arc
* @returns {number}
*/
Arc.prototype.getStartAngle = function () {
return this.startAngle;
};
/**
* Sets the startAngle of the arc
* @param {number} newAngle
* @chainable
*/
Arc.prototype.setStartAngle = function (newAngle) {
this.startAngle = newAngle;
return this;
};
/**
* Returns the endAngle of the arc
* @returns {number}
*/
Arc.prototype.getEndAngle = function () {
return this.endAngle;
};
/**
* Sets the endAngle of the arc
* @param {number} newAngle
* @chainable
*/
Arc.prototype.setEndAngle = function (newAngle) {
this.endAngle = newAngle;
return this;
};
/**
* Returns the radius of the arc
* @returns {number}
*/
Arc.prototype.getRadius = function () {
return this.radius;
};
/**
* Sets the radius of the arc
* @param {number} newRadius
* @chainable
*/
Arc.prototype.setRadius = function (newRadius) {
this.radius = newRadius;
return this;
};
/**
* Returns the radius of the arc
* @returns {number}
*/
Arc.prototype.getStep = function () {
return this.step;
};
/**
* Sets the step to draw the arc (steps jumped from startAngle to endAngle)
* @param {number} newStep
* @chainable
*/
Arc.prototype.setStep = function (newStep) {
this.step = newStep;
return this;
};
PMUI.extendNamespace('PMUI.draw.Arc', Arc);
if (typeof exports !== 'undefined') {
module.exports = Arc;
}
}());
(function () {
/**
* @class PMUI.draw.Oval
* A regular shape that represents an oval, in the PMDraw framework instances of the class Oval
* are used to represent a Port.
*
* Some examples of use:
*
* var oval = new PMUI.draw.Oval({
* width: 8,
* height: 8,
* center: new PMUI.util.Point(100, 100)
* });
*
* @extend PMUI.draw.RegularShape
*
* @constructor Creates an instance of the class Oval
* @param {Object} options Initialization options
* @cfg {number} [width=4] The width of this oval
* @cfg {number} [height=4] The height of this oval
* @cfg {number} [center=new Center(0, 0)] The center of this oval
*/
var Oval = function (options) {
Oval.superclass.call(this, options);
Oval.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.RegularShape', Oval);
/**
* The type of each instance of this class
* @property {String} [type=Oval]
*/
Oval.prototype.type = "Oval";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains the config
* @private
*/
Oval.prototype.init = function (options) {
/**
* Default options for the object
* @property {Object}
*/
var defaults = {
center: new PMUI.util.Point(0, 0),
width: 4,
height: 4
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// call setters using the defaults object
this.setCenter(defaults.center)
.setWidth(defaults.width)
.setHeight(defaults.height);
};
/**
* Paints a **red** oval using the configuration options (the HTML is not appended to
* the DOM)
* @chainable
*/
Oval.prototype.paint = function () {
// show or hide the oval
this.setVisible(this.visible);
if (this.html) {
this.style.applyStyle();
this.graphic = new JSGraphics(this.id);
this.graphic.setColor("red");
this.graphic.fillOval(0, 0, this.getWidth(), this.getHeight());
this.graphic.paint();
}
return this;
};
/**
* Creates the HTML representation of the Oval
* @returns {HTMLElement}
*/
Oval.prototype.createHTML = function () {
Oval.superclass.prototype.createHTML.call(this);
return this.html;
};
PMUI.extendNamespace('PMUI.draw.Oval', Oval);
if (typeof exports !== 'undefined') {
module.exports = Oval;
}
}());
(function () {
/**
* @class PMUI.draw.Polygon
* Abstract class polygon to draw simple poly-lines
*
* An example of usage:
*
* var polygon = new PMUI.draw.Polygon({
* points: []
* });
*
* @extend PMUI.draw.RegularShape
*
* @constructor Creates an instance of the class Polygon
* @param {Object} options Initialization options
* @cfg {Array} [points=[]] The points that make the polygon
*/
var Polygon = function (options) {
Polygon.superclass.call(this, options);
/**
* The points representing this polygon
* @property {Array}
*/
this.points = null;
Polygon.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.RegularShape', Polygon);
/**
* The type of each instance of this class
* @property {String}
*/
Polygon.prototype.type = "Polygon";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains the config
* @private
*/
Polygon.prototype.init = function (options) {
var defaults = {
points: []
};
$.extend(true, defaults, options);
this.setPoints(defaults.points);
};
/**
* Sets the points of this polygon
* @param {Array} newPoints
* @chainable
*/
Polygon.prototype.setPoints = function (newPoints) {
var i,
point;
this.points = [];
for (i = 0; i < newPoints.length; i += 1) {
point = newPoints[i];
this.points.push(new PMUI.util.Point(point.getX(), point.getY()));
}
};
/**
* Gets the points of this polygon
* @return {Array}
*/
Polygon.prototype.getPoints = function () {
return this.points;
};
PMUI.extendNamespace('PMUI.draw.Polygon', Polygon);
if (typeof exports !== 'undefined') {
module.exports = Polygon;
}
}());
(function () {
/**
* @class PMUI.draw.Intersection
* An intersection in the designer is defined as an arc which has only one additional property: `idOtherConnection`
* and it's the ID of the other connection this segment has intersection with.
*
* All the intersection of a segment are stored in `segment.intersections`.
*
* An example of instantiation:
*
* // let's assume that 'segment' is an instance of the class Segment
* // let's assume that 'otherSegment' is an instance of the class Segment
* // let's assume that 'segment' has an intersection with 'otherSegment' at 'ip' (intersection point)
* var intersection = new PMUI.draw.Intersection(
* ip,
* otherSegment.parent,getID(),
* segment
* );
*
* @extends PMUI.draw.Arc
*
* @constructor Creates an instance of the class Intersection
* @param {PMUI.util.Point} center
* @param {String} idOtherConnection
* @param {PMUI.draw.Segment} parent
*/
var Intersection = function (center, idOtherConnection, parent) {
Intersection.superclass.call(this);
/**
* The center of the arc
* @property {PMUI.util.Point} [center=null]
*/
this.center = (!center) ? null : center;
/**
* Visibility of this arc
* @property {boolean}
*/
this.visible = true;
/**
* Parent of this intersection is a segment
* @property {PMUI.draw.Segment}
*/
this.parent = parent;
/**
* Id of the other connection
* @property {String}
*/
this.idOtherConnection = idOtherConnection;
};
PMUI.inheritFrom('PMUI.draw.Arc', Intersection);
/**
* The type of each instance of the class Intersection
* @property {String}
*/
Intersection.prototype.type = "Intersection";
/**
* Paints this intersection (calling `Arc.paint()`) considering the orientation of its parent.
*
* It overwrites the properties `startAngle` and `endAngle` inherited from {@link PMUI.draw.Arc}
* according to the orientation of its parent (segment), the calculation is as follows:
*
* - Segment is vertical
* - startAngle = 270
* - endAngle = 90
* - Segment is horizontal
* - startAngle = 180
* - endAngle = 0
*
* @chainable
*/
Intersection.prototype.paint = function () {
// NOTE: it's always visible so do not call setVisible()
if (this.parent.orientation === this.VERTICAL) {
this.startAngle = 270;
this.endAngle = 90;
} else {
this.startAngle = 180;
this.endAngle = 0;
}
// call the representation (always an arc)
Intersection.superclass.prototype.paint.call(this);
// apply predefined style
this.style.applyStyle();
return this;
};
/**
* Destroys the intersection by removing its HTML from the DOM.
* @chainable
*/
Intersection.prototype.destroy = function () {
$(this.html).remove();
return this;
};
/**
* Creates the HTML representation of the Intersection.
* @returns {HTMLElement}
*/
Intersection.prototype.createHTML = function () {
PMUI.draw.Shape.prototype.createHTML.call(this);
return this.html;
};
PMUI.extendNamespace('PMUI.draw.Intersection', Intersection);
if (typeof exports !== 'undefined') {
module.exports = Intersection;
}
}());
(function () {
/**
* @class PMUI.draw.Rectangle
* A regular shape that represents a rectangle, in the PMDraw framework instances of the class Rectangle
* are used to represent a resize handler and a segment move handler.
*
* Some examples of use:
*
* var rectangle = new PMUI.draw.Rectangle();
*
* @extend PMUI.draw.Polygon
*
* @constructor Creates an instance of the class Rectangle
* @param {Object} options Initialization options (currently there are no initialization options)
*/
var Rectangle = function (options) {
Rectangle.superclass.call(this, options);
Rectangle.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Polygon', Rectangle);
/**
* The type of each instance of this class
* @property {String}
*/
Rectangle.prototype.type = "Rectangle";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains the config
* @private
*/
Rectangle.prototype.init = function (options) {
};
/**
* Paints the rectangle applying the predefined style and adding a background color if
* it's possible (it's possible if this rectangle has an instance of the class Color).
* @chainable
*/
Rectangle.prototype.paint = function () {
if (this.html) {
this.style.applyStyle();
if (this.color) {
this.style.addProperties({
backgroundColor: this.color.getCSS()
});
}
}
return this;
};
/**
* Creates the HTML representation of the Rectangle
* @returns {HTMLElement}
*/
Rectangle.prototype.createHTML = function () {
PMUI.draw.Shape.prototype.createHTML.call(this);
return this.html;
};
PMUI.extendNamespace('PMUI.draw.Rectangle', Rectangle);
if (typeof exports !== 'undefined') {
module.exports = Rectangle;
}
}());
(function () {
/**
* @class PMUI.draw.MultipleSelectionContainer
* Represents the rectangle created to do multiple selection after firing
{@link PMUI.draw.Canvas#event-mousedown} and
* dragging a rectangle over the desired elements in the canvas and firing
{@link PMUI.draw.Canvas#event-mouseup},
* currently it can only select shapes that are direct children of the canvas.
*
* An example of use:
*
* // let's assume that canvas is an instance of the class Canvas
* var multipleSelectionContainer = new PMUI.draw.MultipleSelectionContainer(canvas);
*
* @extend PMUI.draw.Rectangle
*
* @constructor Creates an instance of the class MultipleSelectionContainer
* @param {PMUI.draw.Canvas} canvas
*/
var MultipleSelectionContainer = function (options) {
MultipleSelectionContainer.superclass.call(this);
/**
* The background color of this element
* @property {PMUI.util.Color}
*/
this.backgroundColor = null;
/**
* Reference to the canvas
* @property {PMUI.draw.Canvas}
*/
this.canvas = null;
// init object
MultipleSelectionContainer.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Rectangle', MultipleSelectionContainer);
/**
* Type of each instance of this class
* @property {String}
*/
MultipleSelectionContainer.prototype.type = "MultipleSelectionContainer";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {PMUI.draw.Canvas} canvas The canvas of this multiple selection container
* @private
*/
MultipleSelectionContainer.prototype.init = function (options) {
var defaults = {
canvas: null,
x: 0,
y: 0,
color: new PMUI.util.Color(0, 128, 255, 0.1)
};
jQuery.extend(true, defaults, options);
this.backgroundColor = defaults.color;
// light blue
this.canvas = defaults.canvas;
this.absoluteX = this.canvas.absoluteX;
this.absoluteY = this.canvas.absoluteY;
};
/**
* Paints the multiple selection container with the color *light blue* (defined in initObject)
* @chainable
*/
MultipleSelectionContainer.prototype.paint = function () {
this.style.addProperties({
backgroundColor: this.backgroundColor.getCSS()
});
return this;
};
/**
* Changes the opacity of this multiple selection container (and repaints it later).
* @param {number} value
* @chainable
*/
MultipleSelectionContainer.prototype.changeOpacity = function (value) {
this.backgroundColor.setOpacity(value);
this.paint();
return this;
};
/**
* Wraps the direct children of the canvas. To call this method it's assumed that this instance has a position
* and a dimension in the canvas (this method is called from {@link PMUI.draw.Canvas#event-mouseup}).
* It executes the following actions:
*
* 1. Gathers the currentSelection
* 2. Checks which direct children of the canvas are inside the selection
* (done through {@link PMUI.draw.MultipleSelectionContainer#intersectElements}).
* 3. Fires {@link PMUI.draw.Canvas#triggerSelectEvent} using the new `this.canvas.currentSelection`
* 4. Resets the state of this instance
*
* @chainable
*/
MultipleSelectionContainer.prototype.wrapElements = function () {
var currentSelection = this.canvas.currentSelection,
selection = [];
this.intersectElements();
if (!currentSelection.isEmpty()) {
selection = currentSelection.asArray();
this.canvas.triggerSelectEvent(selection);
}
this.reset();
this.setVisible(false);
return this;
};
/**
* Checks which direct children of the canvas are inside `this` (represented as a rectangle in the canvas).
* The steps are:
*
* 1. Empty `this.canvas.currentSelection` by calling {@link PMUI.draw.Canvas#emptyCurrentSelection}.
* 2. If a child is inside this rectangle then it's added to `this.canvas.currentSelection`.
*
* @chainable
*/
MultipleSelectionContainer.prototype.intersectElements = function () {
var i,
shape,
children;
//empty the current selection
this.canvas.emptyCurrentSelection();
// get all the customShapes
children = this.canvas.customShapes;
for (i = 0; i < children.getSize(); i += 1) {
shape = children.get(i);
if (shape.parent.family === "Canvas" && this.checkIntersection(shape)) {
this.canvas.addToSelection(shape);
}
}
return this;
};
/**
* Resets the position and dimensions of this selection container.
* @chainable
*/
MultipleSelectionContainer.prototype.reset = function () {
this.setPosition(0, 0);
this.setDimension(0, 0);
return this;
};
/**
* Alias for {@link PMUI.draw.Core#getAbsoluteX}.
* @property {Function} getLeft
*/
MultipleSelectionContainer.prototype.getLeft = PMUI.draw.Shape.prototype.getAbsoluteX;
/**
* Alias for {@link PMUI.draw.Core#getAbsoluteY}.
* @property {Function} getTop
*/
MultipleSelectionContainer.prototype.getTop = PMUI.draw.Shape.prototype.getAbsoluteY;
/**
* Checks if `shape` is inside `this`, `shape` is inside `this` if one of its corners
* its inside `this`.
* @param {PMUI.draw.Shape} shape
* @return {boolean}
*/
MultipleSelectionContainer.prototype.checkIntersection = function (shape) {
var Point = PMUI.util.Point,
Geometry = PMUI.draw.Geometry,
topLeft = new PMUI.util.Point(this.zoomX, this.zoomY),
bottomRight = new PMUI.util.Point(this.zoomX + this.zoomWidth,
this.zoomY + this.zoomHeight);
return Geometry.pointInRectangle(new PMUI.util.Point(shape.getZoomX(), shape.getZoomY()),
topLeft, bottomRight) ||
Geometry.pointInRectangle(new PMUI.util.Point(shape.zoomX +
shape.zoomWidth, shape.zoomY), topLeft, bottomRight) ||
Geometry.pointInRectangle(new PMUI.util.Point(shape.zoomX, shape.zoomY +
shape.zoomHeight), topLeft, bottomRight) ||
Geometry.pointInRectangle(new PMUI.util.Point(shape.zoomX +
shape.zoomWidth, shape.zoomY + shape.zoomHeight), topLeft, bottomRight);
};
PMUI.extendNamespace('PMUI.draw.MultipleSelectionContainer', MultipleSelectionContainer);
if (typeof exports !== 'undefined') {
module.exports = MultipleSelectionContainer;
}
}());
(function () {
/**
* @class PMUI.draw.Snapper
* Class snapper represents the helper shown while moving shapes.
* @extend PMUI.draw.Core
*
* @constructor Creates an instance of the class Snapper
* @param {Object} options Initialization options
* @cfg {PMUI.util.Point} [orientation="horizontal"] The default orientation of this snapper
*/
var Snapper = function (options) {
Snapper.superclass.call(this, options);
/**
* Orientation of this snapper, it can be either "horizontal" or "vertical".
* @property {string} [orientation=null]
*/
this.orientation = null;
/**
* Data saved to define the positioning of this snapper in the canvas.
* @property {Array} [data=[]]
*/
this.data = [];
/**
* The visibility of this snapper.
* @property {boolean} [visible=false]
*/
this.visible = false;
Snapper.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', Snapper);
/**
* The type of each instance of this class
* @property {String}
*/
Snapper.prototype.type = "Snapper";
/**
* Instance initializer which uses options to extend the config options to initialize the instance.
* @param {Object} options The object that contains the config
* @private
*/
Snapper.prototype.init = function (options) {
var defaults = {
orientation: "horizontal"
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// call setters using the defaults object
this.setOrientation(defaults.orientation);
};
/**
* Creates the HTML representation of the snapper.
* @returns {HTMLElement}
*/
Snapper.prototype.createHTML = function () {
Snapper.superclass.prototype.createHTML.call(this);
return this.html;
};
Snapper.prototype.enable = function () {
if (this.html) {
this.graphic = new JSGraphics(this.id);
this.graphic.setColor("#81DAF5");
if (this.orientation === 'horizontal') {
this.graphic.drawLine(0, 0, 4000, 0);
} else {
this.graphic.drawLine(0, 0, 0, 4000);
}
this.graphic.paint();
}
return this;
};
/**
* Hides the snapper.
* @chainable
*/
Snapper.prototype.hide = function () {
this.visible = false;
this.setVisible(this.visible);
return this;
};
/**
* Shows the snapper.
* @chainable
*/
Snapper.prototype.show = function () {
this.visible = true;
this.setVisible(this.visible);
return this;
};
/**
* Fills the data for the snapper (using customShapes and regularShapes).
* The data considered for each shape is:
*
* - Its absoluteX
* - Its absoluteY
* - Its absoluteX + width
* - Its absoluteY + height
*
* @chainable
*/
Snapper.prototype.createSnapData = function () {
var i,
index = 0,
shape,
border = 0;
// clear the data before populating it
this.data = [];
// populate the data array using the customShapes
for (i = 0; i < this.canvas.customShapes.getSize(); i += 1) {
shape = this.canvas.customShapes.get(i);
border = parseInt($(shape.getHTML()).css('borderTopWidth'), 10);
if (this.orientation === 'horizontal') {
this.data[index * 2] = shape.getAbsoluteY() - border;
this.data[index * 2 + 1] = shape.getAbsoluteY() + shape.getZoomHeight();
} else {
this.data[index * 2] = shape.getAbsoluteX() - border;
this.data[index * 2 + 1] = shape.getAbsoluteX() + shape.getZoomWidth();
}
index += 1;
}
// populate the data array using the regularShapes
for (i = 0; i < this.canvas.regularShapes.getSize(); i += 1) {
shape = this.canvas.regularShapes.get(i);
border = parseInt($(shape.getHTML()).css('borderTopWidth'), 10);
if (this.orientation === 'horizontal') {
this.data[index * 2] = shape.getAbsoluteY() - border;
this.data[index * 2 + 1] = shape.getAbsoluteY() +
shape.getZoomHeight();
} else {
this.data[index * 2] = shape.getAbsoluteX() - border;
this.data[index * 2 + 1] = shape.getAbsoluteX() +
shape.getZoomWidth();
}
index += 1;
}
return this;
};
/**
* Sorts the data using the builtin `sort()` function, so that there's an strictly increasing order.
* @chainable
*/
Snapper.prototype.sortData = function () {
this.data.sort();
return this;
};
/**
* Performs a binary search for `value` in `this.data`, return true if `value` was found in the data.
* @param {number} value
* @return {boolean}
*/
Snapper.prototype.binarySearch = function (value) {
var low = 0,
up = this.data.length - 1,
mid;
while (low <= up) {
mid = parseInt((low + up) / 2, 10);
if (this.data[mid] === value) {
return value;
}
if (this.data[mid] > value) {
up = mid - 1;
} else {
low = mid + 1;
}
}
return false;
};
/**
* Attaches listeners to this snapper, currently it only has the
* mouseMove event which hides the snapper.
* @param {PMUI.draw.Snapper} snapper
* @chainable
*/
Snapper.prototype.attachListeners = function (snapper) {
var $snapper = $(snapper.html).mousemove(
function () {
snapper.hide();
}
);
return this;
};
/**
* Sets the orientation of this snapper.
* @param {string} orientation
* @chainable
*/
Snapper.prototype.setOrientation = function (orientation) {
if (orientation === "horizontal" || orientation === "vertical") {
this.orientation = orientation;
} else {
throw new Error("setOrientation(): parameter is not valid");
}
return this;
};
/**
* Gets the orientation of this snapper.
* @return {string}
*/
Snapper.prototype.getOrientation = function () {
return this.orientation;
};
PMUI.extendNamespace('PMUI.draw.Snapper', Snapper);
if (typeof exports !== 'undefined') {
module.exports = Snapper;
}
}());
(function () {
/**
* @abstract
* @class PMUI.draw.Router
* Represents the router used to define the points for a connection.
* @extend PMUI.draw.Core
*
* @constructor Creates an instance of the class Router
*/
var Router = function () {
Router.superclass.call(this);
};
PMUI.inheritFrom('PMUI.draw.Core', Router);
/**
* The type of each instance of this class
* @property {String}
*/
Router.prototype.type = "Router";
/**
* @abstract Abstract method to create a route (defined in other inherited classes)
* @returns {boolean}
*/
Router.prototype.createRoute = function () {
return true;
};
PMUI.extendNamespace('PMUI.draw.Router', Router);
if (typeof exports !== 'undefined') {
module.exports = Router;
}
}());
(function () {
/**
* @class PMUI.draw.ManhattanConnectionRouter
* Class ManhattanConnectionRouter uses the 'ManhattanRouter' algorithm to define the points of the connection.
* @extends PMUI.draw.Router
*
* @constructor Creates an instance of the class ManhattanConnectionRouter
*/
var ManhattanConnectionRouter = function () {
ManhattanConnectionRouter.superclass.call(this);
/**
* Minimum distance used in the algorithm
* @property {number} [mindist=20]
*/
this.mindist = 20;
};
PMUI.inheritFrom('PMUI.draw.Router', ManhattanConnectionRouter);
/**
* The type of each instance of this class
* @property {String}
*/
ManhattanConnectionRouter.prototype.type = "ManhattanConnectionRouter";
/**
* Creates the points of `connection` by calling the #route method and using
* `connection.srcPort` and `connection.destPort` as the start and end points
* @param {PMUI.draw.Connection} connection
* @return {Array} An array of points that define the connection.
*/
ManhattanConnectionRouter.prototype.createRoute = function (connection) {
var fromPt, fromDir, toPt, toDir, points = [];
fromPt = connection.srcPort.getPoint(false);
fromDir = connection.srcPort.direction;
toPt = connection.destPort.getPoint(false);
toDir = connection.destPort.direction;
// draw a line between the two points.
this.route(connection, toPt, toDir, fromPt, fromDir, points);
return points;
};
/**
* Implementation of the 'MahattanRouter' algorithm
* @param {PMUI.draw.Connection} conn Instance of the class Connection
* @param {PMUI.util.Point} fromPt initial Point
* @param {number} fromDir route using to begin line
* UP = 0; RIGHT= 1; DOWN = 2; LEFT = 3;
* @param {PMUI.util.Point} toPt final Point
* @param {number} toDir route using to end line
* UP = 0; RIGHT= 1; DOWN = 2; LEFT = 3;
* @param {Array} points array where points are saved
*/
ManhattanConnectionRouter.prototype.route = function (conn, fromPt, fromDir, toPt, toDir, points) {
var TOL,
TOLxTOL,
UP,
RIGHT,
DOWN,
LEFT,
xDiff,
yDiff,
nPoint,
dir,
pos;
TOL = 0.1;
TOLxTOL = 0.01;
// fromPt is an x,y to start from.
// fromDir is an angle that the first link must
UP = 0;
RIGHT = 1;
DOWN = 2;
LEFT = 3;
xDiff = fromPt.x - toPt.x;
yDiff = fromPt.y - toPt.y;
if (((xDiff * xDiff) < (TOLxTOL)) && ((yDiff * yDiff) < (TOLxTOL))) {
points.push(toPt);
return;
}
if (fromDir === LEFT) {
if ((xDiff > 0) && ((yDiff * yDiff) < TOL) && (toDir === RIGHT)) {
nPoint = toPt;
dir = toDir;
} else {
if (xDiff < 0) {
nPoint = new PMUI.util.Point(fromPt.x - this.mindist, fromPt.y);
} else if (((yDiff > 0) && (toDir === DOWN)) || ((yDiff < 0) &&
(toDir === UP))) {
nPoint = new PMUI.util.Point(toPt.x, fromPt.y);
} else if (fromDir === toDir) {
pos = Math.min(fromPt.x, toPt.x) - this.mindist;
nPoint = new PMUI.util.Point(pos, fromPt.y);
} else {
nPoint = new PMUI.util.Point(fromPt.x - (xDiff / 2), fromPt.y);
}
if (yDiff > 0) {
dir = UP;
} else {
dir = DOWN;
}
}
} else if (fromDir === RIGHT) {
if ((xDiff < 0) && ((yDiff * yDiff) < TOL) && (toDir === LEFT)) {
nPoint = toPt;
dir = toDir;
} else {
if (xDiff > 0) {
nPoint = new PMUI.util.Point(fromPt.x + this.mindist, fromPt.y);
} else if (((yDiff > 0) && (toDir === DOWN)) || ((yDiff < 0) &&
(toDir === UP))) {
nPoint = new PMUI.util.Point(toPt.x, fromPt.y);
} else if (fromDir === toDir) {
pos = Math.max(fromPt.x, toPt.x) + this.mindist;
nPoint = new PMUI.util.Point(pos, fromPt.y);
} else {
nPoint = new PMUI.util.Point(fromPt.x - (xDiff / 2), fromPt.y);
}
if (yDiff > 0) {
dir = UP;
} else {
dir = DOWN;
}
}
} else if (fromDir === DOWN) {
if (((xDiff * xDiff) < TOL) && (yDiff < 0) && (toDir === UP)) {
nPoint = toPt;
dir = toDir;
} else {
if (yDiff > 0) {
nPoint = new PMUI.util.Point(fromPt.x, fromPt.y + this.mindist);
} else if (((xDiff > 0) && (toDir === RIGHT)) || ((xDiff < 0) &&
(toDir === LEFT))) {
nPoint = new PMUI.util.Point(fromPt.x, toPt.y);
} else if (fromDir === toDir) {
pos = Math.max(fromPt.y, toPt.y) + this.mindist;
nPoint = new PMUI.util.Point(fromPt.x, pos);
} else {
nPoint = new PMUI.util.Point(fromPt.x, fromPt.y - (yDiff / 2));
}
if (xDiff > 0) {
dir = LEFT;
} else {
dir = RIGHT;
}
}
} else if (fromDir === UP) {
if (((xDiff * xDiff) < TOL) && (yDiff > 0) && (toDir === DOWN)) {
nPoint = toPt;
dir = toDir;
} else {
if (yDiff < 0) {
nPoint = new PMUI.util.Point(fromPt.x, fromPt.y - this.mindist);
} else if (((xDiff > 0) && (toDir === RIGHT)) || ((xDiff < 0) &&
(toDir === LEFT))) {
nPoint = new PMUI.util.Point(fromPt.x, toPt.y);
} else if (fromDir === toDir) {
pos = Math.min(fromPt.y, toPt.y) - this.mindist;
nPoint = new PMUI.util.Point(fromPt.x, pos);
} else {
nPoint = new PMUI.util.Point(fromPt.x, fromPt.y - (yDiff / 2));
}
if (xDiff > 0) {
dir = LEFT;
} else {
dir = RIGHT;
}
}
}
this.route(conn, nPoint, dir, toPt, toDir, points);
points.push(fromPt);
};
PMUI.extendNamespace('PMUI.draw.ManhattanConnectionRouter', ManhattanConnectionRouter);
if (typeof exports !== 'undefined') {
module.exports = ManhattanConnectionRouter;
}
}());
(function () {
/**
* @class PMUI.draw.Connection
* Class that represents a connection between two elements in the diagram.
*
* A connection is defined with a set of points, there's a segment between two points e.g. `point[i]`
* and `point[i + 1]` with `i >= 0` and `i < points.length - 1`, there are two ways to paint a connection:
*
* - given *2* {@link PMUI.draw.Port Ports}, use the algorithm 'ManhattanConnection' to define the points
* - given *n* points, make the segments with the rule defined above (but first let's use the first and
* the last points to make them {@link PMUI.draw.Port Ports}).
*
* Some characteristics of the connection:
*
* - The connection has references to its source port and end port.
* - The `state` of the connection is the set of points that define that connection.
* - The connections can have a color, the color is an instance of the class Color.
*
* The connections can have the following types of segments:
*
* - regular (a complete segment)
* - dotted
* - segmented
* - segmented and dotted (mixed)
*
* Some examples of the configuration:
*
* // e.g.
* // let's assume that there are two shapes (sourceShape and destShape)
* // let's assume that srcPort is a port that is stored in sourceShape
* // let's assume that destPort is a port that is stored in destShape
* // to create an instance of Connection with regular light green segments
* var connectionGreen = new PMUI.draw.Connection({
* srcPort: srcPort,
* destPort: destPort,
* segmentColor: new Color(0, 200, 0),
* segmentStyle: "regular"
* });
* // to create an instance of Connection with dotted red segments
* var connectionRed = new PMUI.draw.Connection({
* srcPort: srcPort,
* destPort: destPort,
* segmentColor: new Color(255, 0, 0),
* segmentStyle: "dotted"
* });
*
* @extend PMUI.draw.Core
*
* @constructor Creates an instance of the class Connection
* @param {Object} options Initialization options
* @cfg {PMUI.util.Point} [srcPort=new Port()] Source port of the connection
* @cfg {PMUI.util.Point} [destPort=new Port()] Destination port of the connection
* @cfg {PMUI.util.Color} [segmentColor=new Color(0, 0, 0)] Color of the connection (by default it's black)
* @cfg {string} [segmentStyle="regular"] Type of segments as defined above
*/
var Connection = function (options) {
Connection.superclass.call(this, options);
/**
* The source port of the connection
* @property {PMUI.draw.Port}
*/
this.srcPort = null;
/**
* The end port of the connection
* @property {PMUI.draw.Port}
*/
this.destPort = null;
/**
* The decorator of the source of the connection
* @property {PMUI.draw.ConnectionDecorator}
*/
this.srcDecorator = null;
/**
* The decorator of the target of the connection
* @property {PMUI.draw.ConnectionDecorator}
*/
this.destDecorator = null;
/**
* List of the lines that forms the connection
* @property {PMUI.util.ArrayList}
*/
this.lineSegments = new PMUI.util.ArrayList();
/**
* Saves a copy of the line segments' points when a flag is passed to the
* disconnect method (NOTE: this array is used in the
* userDefinedRoute method)
* @property {PMUI.util.ArrayList}
*/
this.points = [];
this.zoomPoints = [];
/**
* Saves a copy of the line segments' points when a flag is passed to the
* disconnect method (NOTE: this array is used while creating the object
* updatedElement in Canvas.triggerConnectionStateChangeEvent)
* @property {PMUI.util.ArrayList}
*/
this.oldPoints = [];
/**
* Current segment style
* @property {"dotted" / "regular" / "segmented" / "segmentdot"}
*/
this.segmentStyle = null;
/**
* This segment style ej. "dotted", "segmented", "segmentdot" (it's the
* original style set in `this.initObject()`)
* @property {"dotted" / "regular" / "segmented" / "segmentdot"}
*/
this.originalSegmentStyle = null;
/**
* Actual color of all the segment in this connection
* @property {PMUI.util.Color}
*/
this.segmentColor = null;
/**
* Original color of all the segments in this connection (set in `this.initObject()`)
* @property {PMUI.util.Color}
*/
this.originalSegmentColor = null;
/**
* default zIndex of the connection
* @property {number}
*/
this.defaultZOrder = 2;
/**
* Current zIndex of the connection
* @property {number}
*/
this.zOrder = 2;
/**
* ArrayList which contains the ids of the Connections it has an
* intersection with:
*
* // e.g.
* // let's assume that there's an instance of the class Connection called anotherConnection
* intersectionWith = new PMUI.util.ArrayList();
* intersectionWith.insert(anotherConnection);
*
* @property {PMUI.util.ArrayList}
*/
this.intersectionWith = new PMUI.util.ArrayList();
this.corona = 10;
Connection.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', Connection);
/**
* The type of each instance of this class
* @property {String}
*/
Connection.prototype.type = "Connection";
/**
* The family of each instance of this class
* @property {String}
*/
Connection.prototype.family = "Connection";
/**
* Router associated with the connection
* @property {PMUI.draw.Router}
*/
Connection.prototype.router = new PMUI.draw.ManhattanConnectionRouter();
/**
* Instance initializer which uses options to extend the config options to initialize the instance.
* @param {Object} options The object that contains the config
* @private
*/
Connection.prototype.init = function (options) {
var defaultOptions = {
srcPort: new PMUI.draw.Port(),
destPort: new PMUI.draw.Port(),
segmentColor: new PMUI.util.Color(0, 0, 0),
segmentStyle: "regular"
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaultOptions, options);
// init
this.setSrcPort(defaultOptions.srcPort)
.setDestPort(defaultOptions.destPort)
.setSegmentStyle(defaultOptions.segmentStyle, false)
.setSegmentColor(defaultOptions.segmentColor, false);
// init originals
this.originalSegmentStyle = defaultOptions.segmentStyle;
this.originalSegmentColor = defaultOptions.segmentColor;
// set the connections for each port as this
this.getSrcPort().setConnection(this);
this.getDestPort().setConnection(this);
};
/**
* Creates the HTML Representation of the Connection.
* @returns {HTMLElement}
*/
Connection.prototype.createHTML = function () {
this.html = document.createElement('div');
this.html.id = this.id;
this.style.addProperties({
position: "absolute",
left: 0,
top: 0,
height: 0,
width: 0,
zIndex: this.zOrder
});
return this.html;
};
Connection.prototype.attachListeners = function () {
if (!this.canvas.readOnly) {
$(this.html).children().click(this.onClick(this));
}
};
/**
* @event click
* Click callback fired when the decorator is clicked on.
* It hides the currentSelection if any and shows the ports and handlers of `decorator` parent
* (which is a connection).
* @param {PMUI.draw.Connection} decorator
*/
Connection.prototype.onClick = function (conn) {
return function (e, ui) {
var connection = conn,
oldConnection = conn.canvas.currentConnection,
canvas = connection.canvas;
// HIDE
// if there were some shapes in the current selection then
// empty the current selection
canvas.emptyCurrentSelection();
// if there was a connection previously select hide its ports
// and its handlers
if (oldConnection) {
oldConnection.hidePortsAndHandlers();
}
// SHOW
// show the ports and the handlers of the new connection
connection.showPortsAndHandlers();
// set the old connection as this connection
canvas.currentConnection = connection;
// TODO: zIndex
e.stopPropagation();
};
};
/**
* Sets the handlers for each segment.
* This method sets the handler for each segment, also sets a variable called hasMoveHandler on each segment to
* either true or false (it'll be false if the current segment is either the first or the last segment of
* the connection)
* @chainable
*/
Connection.prototype.setSegmentMoveHandlers = function () {
var i,
currentSegment,
orientationOptions = [this.HORIZONTAL, this.VERTICAL],
segmentOrientation = (this.destPort.direction === this.TOP ||
this.destPort.direction === this.BOTTOM) ? 1 : 0;
for (i = this.lineSegments.getSize() - 1; i >= 0; i -= 1) {
currentSegment = this.lineSegments.get(i);
currentSegment.orientation =
orientationOptions[segmentOrientation];
currentSegment.hasMoveHandler = false;
// set prev and next segments
if (i < this.lineSegments.getSize() - 1 && i > 0) {
currentSegment.nextNeighbor = this.lineSegments.get(i + 1);
currentSegment.previousNeighbor = this.lineSegments.get(i - 1);
currentSegment.hasMoveHandler = true;
currentSegment.addSegmentMoveHandler();
}
segmentOrientation = 1 - segmentOrientation;
}
return this;
};
/**
* Remove all the segmentHandlers of this connection
* (removing its html)
* @chainable
*/
Connection.prototype.removeAllSegmentHandlers = function () {
var segment,
i;
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
segment = this.lineSegments.get(i);
if (segment.hasMoveHandler) {
$(segment.moveHandler.html).remove();
}
}
return this;
};
/**
* Show the moveHandlers of the connections
* @chainable
*/
Connection.prototype.showMoveHandlers = function () {
var i,
currentHandler;
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
currentHandler = this.lineSegments.get(i).moveHandler;
if (currentHandler) {
currentHandler.setVisible(true);
}
}
return this;
};
/**
* Hide the moveHandlers of the connection
* @chainable
*/
Connection.prototype.hideMoveHandlers = function () {
var i,
currentHandler;
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
currentHandler = this.lineSegments.get(i).moveHandler;
if (currentHandler) {
currentHandler.setVisible(false);
}
}
return this;
};
/**
* Hides the ports and handlers of this connection.
* @chainable
*/
Connection.prototype.hidePortsAndHandlers = function () {
this.hidePorts();
this.hideMoveHandlers();
return this;
};
/**
* Shows the ports and handlers of this connection.
* @chainable
*/
Connection.prototype.showPortsAndHandlers = function () {
this.showPorts();
this.showMoveHandlers();
return this;
};
/**
* Paints the connection according to the parameters given as config options, this method `paint()`
* unlike other similar `paint()` does not append the HTML to the DOM, this is done with a call
* to `canvas.addConnection(connection)`.
* @param {Object} options Configuration options
* @param {string} [options.algorithm="manhattan"] The algorithm used to draw the connection
* @param {Array} [options.points=[]] Points to be used if the algorithm is "user"
* @param {number} [options.dx=0] Move the points dx
* @param {number} [options.dy=0] Move the points dy
* @chainable
*/
Connection.prototype.paint = function (options) {
var defaults = {
algorithm: 'manhattan',
points: [],
dx: 0,
dy: 0
};
$.extend(true, defaults, options);
try {
if (this.html === null) {
this.createHTML();
}
$(this.html).empty();
this.oldPoint = null;
switch (defaults.algorithm) {
case 'manhattan':
this.createManhattanRoute();
break;
case 'user':
this.createUserDefinedRoute(defaults.points,
defaults.dx, defaults.dy);
break;
default:
throw new Error('Connection.paint(): the algorithm provided ' +
'is not correct');
}
if (options && options.updateZoomPoints !== undefined) {
this.savePoints({updateZoomPoints: options.updateZoomPoints});
} else {
this.savePoints();
}
this.style.applyStyle();
// the inline style might have changed in this.move()
// so restore the style to the original setting
this.style.addProperties({
top: 0,
left: 0
});
// paint the decorator if any exists
if (this.destDecorator !== null) {
this.destDecorator.paint();
if (!this.canvas.readOnly) {
this.destDecorator.attachListeners();
}
}
if (this.srcDecorator !== null) {
this.srcDecorator.paint();
}
this.oldPoint = null;
} catch (e) {
}
return this;
};
/**
* Hides the connection and its intersections
* @param {boolean} [savePoints] If set to true, the connection state will be saved in `this.points
* (see the definition of {@link PMUI.draw.Connection#property-points} in the definition of the class).
* @chainable
*/
Connection.prototype.disconnect = function (savePoints) {
this.clearAllIntersections();
// hide the segment handlers
this.hideMoveHandlers();
// save the line segments and use them in the createCustomRoute method
if (savePoints) {
this.savePoints();
}
this.lineSegments.clear();
// empty the contents
$(this.html).empty();
return this;
};
/**
* Connects two elements using options as a parameter (alias for `this.paint`)
* @param {Object} options Configuration options
* @param {string} [options.algorithm="manhattan"] The algorithm used to draw the connection
* @param {Array} [options.points=[]] Points to be used if the algorithm is "user"
* @param {number} [options.dx=0] Move the points dx
* @param {number} [options.dy=0] Move the points dy
* @chainable
*/
Connection.prototype.connect = function (options) {
var i,
max,
srcPortPosition,
destPortPosition,
lineLength,
lastPoint,
currentOrientation,
newPoint,
referencePoints,
x,
y;
if (options && options.algorithm === 'user' && options.points) {
referencePoints = this.zoomPoints.length ? this.zoomPoints : options.points;
srcPortPosition = {
x: Math.round(this.srcPort.getAbsoluteX() + (this.srcPort.getWidth() / 2)),
y: Math.round(this.srcPort.getAbsoluteY() + (this.srcPort.getHeight() / 2))
};
destPortPosition = {
x: Math.round(this.destPort.getAbsoluteX() + (this.destPort.getWidth() / 2)),
y: Math.round(this.destPort.getAbsoluteY() + (this.destPort.getHeight() / 2))
};
this.points = [];
for (i = 0, max = options.points.length; i < max; i += 1) {
if (i === 0) {
x = srcPortPosition.x;
y = srcPortPosition.y;
currentOrientation = (this.srcPort.getDirection() === this.srcPort.LEFT
|| this.srcPort.getDirection() === this.srcPort.RIGHT) ? this.srcPort.HORIZONTAL
: this.srcPort.VERTICAL;
} else if (i === max - 2 || i === max - 1) {
if (currentOrientation === this.srcPort.HORIZONTAL) {
x = destPortPosition.x;
} else {
y = destPortPosition.y;
}
currentOrientation = currentOrientation === this.srcPort.HORIZONTAL ? this.srcPort.VERTICAL
: this.srcPort.HORIZONTAL;
} else {
if (currentOrientation === this.srcPort.HORIZONTAL) {
lineLength = (referencePoints[i].x - referencePoints[i - 1].x) * this.canvas.zoomFactor;
x = lastPoint.x + lineLength;
} else {
lineLength = (referencePoints[i].y - referencePoints[i - 1].y) * this.canvas.zoomFactor;
y = lastPoint.y + lineLength;
}
currentOrientation = currentOrientation === this.srcPort.HORIZONTAL ? this.srcPort.VERTICAL
: this.srcPort.HORIZONTAL;
}
newPoint = new PMUI.util.Point(x, y);
this.points.push(newPoint);
lastPoint = newPoint;
}
options.points = this.points;
}
this.paint(options);
this.attachListeners();
return this;
};
/**
* Hides the ports of the connection
* @chainable
*/
Connection.prototype.hidePorts = function () {
this.srcPort.hide();
this.destPort.hide();
return this;
};
/**
* Shows the ports of the connection
* @chainable
*/
Connection.prototype.showPorts = function () {
this.srcPort.show();
this.destPort.show();
return this;
};
/**
* Saves the state of the connection.
* @param {Object} options
* @param {boolean} [options.saveToOldPoints=false] If set to true then it will save the state
* to `this.oldPoints` array
* @chainable
*/
Connection.prototype.savePoints = function (options) {
var i,
segment,
arrayChosen = 'points',
defaults = {
saveToOldPoints: false,
updateZoomPoints: true
};
$.extend(true, defaults, options);
if (defaults.saveToOldPoints) {
arrayChosen = "oldPoints";
}
this[arrayChosen] = [];
if (defaults.updateZoomPoints) {
this.zoomPoints = [];
}
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
segment = this.lineSegments.get(i);
if (i === 0) {
// insert the startPoint only for the first segment
this[arrayChosen].push(new PMUI.util.Point(
segment.startPoint.x,
segment.startPoint.y
));
if (defaults.updateZoomPoints) {
this.zoomPoints.push(new PMUI.util.Point(
segment.startPoint.x / this.canvas.zoomFactor,
segment.startPoint.y / this.canvas.zoomFactor
));
}
}
this[arrayChosen].push(new PMUI.util.Point(
segment.endPoint.x,
segment.endPoint.y
));
if (defaults.updateZoomPoints) {
this.zoomPoints.push(new PMUI.util.Point(
segment.endPoint.x / this.canvas.zoomFactor,
segment.endPoint.y / this.canvas.zoomFactor
));
}
}
return this;
};
/**
* Creates the segments of the connection using points and moving the segments dx and dy
* @param {Array} points
* @param {number} dx
* @param {number} dy
* @chainable
*/
Connection.prototype.createUserDefinedRoute = function (points, dx, dy) {
var i,
segment,
diffPoint = new PMUI.util.Point(dx, dy);
for (i = 1; i < points.length; i += 1) {
segment = new PMUI.draw.Segment({
startPoint: new PMUI.util.Point(
parseInt(points[i - 1].x, 10),
parseInt(points[i - 1].y, 10)
).add(diffPoint),
endPoint: new PMUI.util.Point(
parseInt(points[i].x, 10),
parseInt(points[i].y, 10)
).add(diffPoint),
parent: this,
canvas: this.canvas,
color: this.segmentColor
});
this.addSegment(segment);
}
return this;
};
/**
* Create the segments of the connection using the points defined by the algorithm "ManhattanConnection"
* @chainable
*/
Connection.prototype.createManhattanRoute = function () {
var points = this.router.createRoute(this),
i,
segment;
// create the segments now that we have the points
for (i = 1; i < points.length; i += 1) {
segment = new PMUI.draw.Segment({
startPoint: new PMUI.util.Point(
parseInt(points[i - 1].x - this.canvas.absoluteX, 10),
parseInt(points[i - 1].y - this.canvas.absoluteY, 10)
),
endPoint: new PMUI.util.Point(
parseInt(points[i].x - this.canvas.absoluteX, 10),
parseInt(points[i].y - this.canvas.absoluteY, 10)
),
parent: this,
canvas: this.canvas,
color: this.segmentColor
});
this.addSegment(segment);
}
return this;
};
/**
* Add a segment to the line segments arrayList (painting it first)
* @param {PMUI.draw.Segment} segment
* @chainable
*/
Connection.prototype.addSegment = function (segment) {
segment.setStyle(this.segmentStyle);
segment.paint();
this.lineSegments.insert(segment);
return this;
};
/**
* Destroys the connection but saving its HTML first
* @chainable
*/
Connection.prototype.saveAndDestroy = function () {
if (this.canvas.currentConnection) {
this.hidePortsAndHandlers();
this.canvas.currentConnection = null;
}
// remove this from the canvas connections arrayList
this.canvas.removeConnection(this);
//this.canvas.removeFromList(this);
this.srcPort.saveAndDestroy(); //destroy srcPort
this.destPort.saveAndDestroy(); //destroy destPort
// save the html but detach it from the DOM
this.html = $(this.html).detach()[0];
return this;
};
/**
* Fixes the zIndex of the connection based on the parents of the connection ports (which are
* shapes), the zIndex is defined as the maximum zIndex the ports parents + 2
* @chainable
*/
Connection.prototype.fixZIndex = function () {
var sourceShape = this.srcPort.parent,
destShape = this.destPort.parent,
sourceShapeParent,
destShapeParent,
sourceShapeParentZIndex,
destShapeParentZIndex;
if (sourceShape.parent) {
sourceShapeParent = sourceShape.parent;
} else {
sourceShapeParent = sourceShape.canvas;
}
sourceShapeParentZIndex = Math.min(sourceShapeParent.getZOrder(),
sourceShape.getZOrder() - 1);
if (destShape.parent) {
destShapeParent = destShape.parent;
} else {
destShapeParent = destShape.canvas;
}
destShapeParentZIndex = Math.min(destShapeParent.getZOrder(),
destShape.getZOrder() - 1);
this.setZOrder(Math.max(sourceShapeParentZIndex, destShapeParentZIndex) +
2);
return this;
};
/**
* Checks and creates intersections of `this` connection with the `otherConnection`
* @param {PMUI.draw.Connection} otherConnection
* @return {boolean} True if there is at least one intersection
*/
Connection.prototype.checkAndCreateIntersections = function (otherConnection) {
// iterate over all the segments of this connection
var i,
j,
segment,
testingSegment,
hasAtLeastOneIntersection = false,
ip; // intersectionPoint
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
segment = this.lineSegments.get(i);
for (j = 0; j < otherConnection.lineSegments.getSize(); j += 1) {
testingSegment = otherConnection.lineSegments.get(j);
// create the intersection of the segments if possible
ip = PMUI.draw.Geometry.perpendicularSegmentIntersection(segment.startPoint,
segment.endPoint, testingSegment.startPoint,
testingSegment.endPoint);
if (ip) {
hasAtLeastOneIntersection = true;
segment.createIntersectionWith(testingSegment, ip);
}
}
}
if (hasAtLeastOneIntersection) {
if (!this.intersectionWith.find('id', otherConnection.getID())) {
this.intersectionWith.insert(otherConnection);
}
if (!otherConnection.intersectionWith.find('id', this.getID())) {
otherConnection.intersectionWith.insert(this);
}
}
return hasAtLeastOneIntersection;
};
/**
* Checks and creates intersections with all the other connections found in this canvas.
* This method also repaints the segments that have intersections.
* @chainable
*/
Connection.prototype.checkAndCreateIntersectionsWithAll = function () {
var i,
otherConnection,
segment;
// create the intersections of this connection
// each segment of this connection saves the intersections it has with
// other segments as an ArrayList of Intersections
// console.log(this.canvas.connections.getSize());
for (i = 0; i < this.canvas.connections.getSize(); i += 1) {
otherConnection = this.canvas.connections.get(i);
if (otherConnection.getID() !== this.getID()) {
this.checkAndCreateIntersections(otherConnection);
}
}
// after we've got all the intersections
// paint the segments with their intersections
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
segment = this.lineSegments.get(i);
if (segment.intersections.getSize()) {
segment.paintWithIntersections();
}
}
return this;
};
/**
* Clears all the intersections with the otherConnection that exist in this connection
* @param {PMUI.draw.Connection} otherConnection
* @chainable
*/
Connection.prototype.clearIntersectionsWith = function (otherConnection) {
var i,
segment,
intersectionObject,
intersectionWasErased;
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
intersectionWasErased = false;
segment = this.lineSegments.get(i);
while (true) {
intersectionObject = segment.
intersections.find('idOtherConnection',
otherConnection.getID());
if (intersectionObject) {
segment.intersections.remove(intersectionObject);
intersectionObject.destroy();
} else {
break;
}
intersectionWasErased = true;
}
if (intersectionWasErased) {
segment.paintWithIntersections();
}
}
// remove other connection from this connection intersectionWith ArrayList
this.intersectionWith.remove(otherConnection);
otherConnection.intersectionWith.remove(this);
return this;
};
/**
* Clear all the intersections of this connection calling clearIntersectionsWith
* many times (one for each connection that exists in the canvas)
* @chainable
*/
Connection.prototype.clearAllIntersections = function () {
var otherIntersection;
while (this.intersectionWith.getSize() > 0) {
otherIntersection = this.intersectionWith.get(0);
otherIntersection.clearIntersectionsWith(this);
}
return this;
};
/**
* Moves the connection [dx, dy]
* @param {number} dx
* @param {number} dy
* @chainable
*/
Connection.prototype.move = function (dx, dy) {
var top,
left;
// moving with inline style
top = parseFloat(this.html.style.top);
left = parseFloat(this.html.style.left);
$(this.html).css({
'top': top + dy,
'left': left + dx
});
return this;
};
/**
* Serializes this object (as a JavaScript object)
* @return {Object}
* @return {string} return.segmentStyle The style of each segment of this connection
* @return {Object} return.srcPort The serialization of `this.srcPort`
* @return {Object} return.destPort The serialization of `this.destPort`
* @return {Array} return.state The array of points that represent this connection a.k.a. state
* @return {string} return.srcDecoratorPrefix The source decorator prefix
* @return {string} return.destDecoratorPrefix The destination decorator prefix
*/
Connection.prototype.stringify = function () {
return {
segmentStyle: this.getSegmentStyle(),
srcPort: this.getSrcPort().stringify(),
destPort: this.getDestPort().stringify(),
state: this.savePoints() && this.points,
srcDecoratorPrefix: this.getSrcDecorator().getDecoratorPrefix(),
destDecoratorPrefix: this.getDestDecorator().getDecoratorPrefix()
};
};
/**
* Sets the color of the segments of this connection
* @param {PMUI.util.Color} newColor
* @param {boolean} [repaint] True if the segment are to be painted immediately
* @chainable
*/
Connection.prototype.setSegmentColor = function (newColor, repaint) {
var i,
segment;
this.segmentColor = newColor;
if (this.html && repaint) {
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
segment = this.lineSegments.get(i);
segment.setColor(this.segmentColor);
segment.paint();
}
}
return this;
};
/**
* Get the segment color of this connection
* @return {PMUI.util.Color}
*/
Connection.prototype.getSegmentColor = function () {
return this.segmentColor;
};
/**
* Sets the style of each segment of this connection
* @param {string} newStyle
* @param {boolean} [repaint] True if the segment are to be painted immediately
* @chainable
*/
Connection.prototype.setSegmentStyle = function (newStyle, repaint) {
var i,
segment;
this.segmentStyle = newStyle;
if (this.html && repaint) {
for (i = 0; i < this.lineSegments.getSize(); i += 1) {
segment = this.lineSegments.get(i);
segment.setStyle(this.segmentStyle);
segment.paint();
}
}
return this;
};
/**
* Get the segment style of this connection
* @return {string}
*/
Connection.prototype.getSegmentStyle = function () {
return this.segmentStyle;
};
/**
* Sets the source port
* @param {PMUI.draw.Port} newSrcPort
* @chainable
*/
Connection.prototype.setSrcPort = function (newSrcPort) {
this.srcPort = newSrcPort;
return this;
};
/**
* Gets the source port
* @return {PMUI.draw.Port}
*/
Connection.prototype.getSrcPort = function () {
return this.srcPort;
};
/**
* Sets the destination port
* @param {PMUI.draw.Port} newDestPort
* @chainable
*/
Connection.prototype.setDestPort = function (newDestPort) {
this.destPort = newDestPort;
return this;
};
/**
* Gets the destination port
* @return {PMUI.draw.Port}
*/
Connection.prototype.getDestPort = function () {
return this.destPort;
};
/**
* Returns the source decorator of the connection
* @returns {PMUI.draw.ConnectionDecorator}
*/
Connection.prototype.getSrcDecorator = function () {
return this.srcDecorator;
};
/**
* Returns the target decorator of the connection
* @returns {PMUI.draw.ConnectionDecorator}
*/
Connection.prototype.getDestDecorator = function () {
return this.destDecorator;
};
/**
* Returns a list of the lines associated with this connection
* @returns {PMUI.util.ArrayList}
*/
Connection.prototype.getLineSegments = function () {
return this.lineSegments;
};
/**
* Sets the source decorator of the connection
* @param {PMUI.draw.ConnectionDecorator} newDecorator
* @chainable
*/
Connection.prototype.setSrcDecorator = function (newDecorator) {
if (newDecorator.type === 'ConnectionDecorator') {
this.srcDecorator = newDecorator;
}
return this;
};
/**
* Sets the destination decorator of the connection
* @param {PMUI.draw.ConnectionDecorator} newDecorator
* @chainable
*/
Connection.prototype.setDestDecorator = function (newDecorator) {
if (newDecorator.type === 'ConnectionDecorator') {
this.destDecorator = newDecorator;
}
return this;
};
/**
* Gets the zOrder of the connection
* @return {number}
*/
Connection.prototype.getZOrder = function () {
return PMUI.draw.Shape.prototype.getZOrder.call(this);
};
/**
* Gets the oldPoints of the connection
* @return {Array}
*/
Connection.prototype.getOldPoints = function () {
return this.oldPoints;
};
/**
* Gets the points of the connection
* @return {Array}
*/
Connection.prototype.getPoints = function () {
return this.points;
};
Connection.prototype.hitTest = function (point) {
var count = this.getLineSegments().asArray().length,
i,
segment,
response;
for (i = 0; i < count; i += 1) {
segment = this.getLineSegments().get(i);
response = segment.hit(this.corona, point);
if (response) {
return true;
}
}
return false;
};
Connection.prototype.applyZoom = function () {
this.disconnect(false);
this.connect({
algorithm: 'user',
points: this.points,
updateZoomPoints: false
});
this.canvas.refreshArray.insert(this);
this.setSegmentMoveHandlers();
this.checkAndCreateIntersectionsWithAll();
};
Connection.prototype.reconnectUser = function (delta, inContainer) {
var j;
if (delta) {
for (j = 0; j < this.points.length; j += 1) {
this.points[j].x += delta.dx * this.canvas.zoomFactor;
this.points[j].y += delta.dy * this.canvas.zoomFactor;
}
this.disconnect(inContainer);
this.connect({
algorithm: 'user',
points: this.points
});
}
};
Connection.prototype.reconnectManhattah = function (inContainer) {
this.disconnect(inContainer);
this.connect();
};
/**
* Increases z-index to bring to front a connection
* @returns {PMUI.draw.Connection}
*/
Connection.prototype.increaseZIndex = function () {
this.setZOrder(PMUI.util.Style.MAX_ZINDEX);
return this;
};
PMUI.extendNamespace('PMUI.draw.Connection', Connection);
if (typeof exports !== 'undefined') {
module.exports = Connection;
}
}());
(function () {
/**
* @class PMUI.draw.ConnectionDecorator
* Represents the decorator on each endpoint of a connection (represented as srcDecorator and destDecorator in
* the class Connection).
* The connection will be painted as follows:
*
* 1. Each connection decorator is painted with a CSS sprite
* 2. The CSS class is built concatenating (with underscores) the following:
*
* 1. The prefix (passed as an argument in the config options)
* 2. The zoom factor multiplied by 100
* 3. The decorator type (passed as an argument in the config options)
* 4. The direction of the decorator (which is the same as the direction of the port it corresponds to)
*
* Some examples:
*
* // e.g.
* // let's assume that the zoom factor is 1
* // let's assume that connection is an instance of the class Connection
*
* // To create a target decorator
* var connectionDecorator = new PMUI.draw.ConnectionDecorator({
* decoratorPrefix: 'con',
* decoratorType: 'target',
* style: {
* cssClasses: [],
* cssProperties: {}
* },
* parent: connection
* });
*
* // assuming that the direction of the port is (1) TOP
* // paint() will build the class like this:
* // CSSClass = decoratorPrefix + "_" + zoomFactor * 100 + "_" + decoratorType + "_" + direction
* // CSSClass = "con_100_target_TOP"
*
* // To create a source decorator
* var connectionDecorator = new PMUI.draw.ConnectionDecorator({
* decoratorPrefix: 'con',
* decoratorType: 'source',
* style: {
* cssClasses: [],
* cssProperties: {}
* },
* parent: connection
* });
*
* // assuming that the direction of the port is (3) LEFT
* // paint() will build the class like this:
* // CSSClass = decoratorPrefix + "_" + zoomFactor * 100 + "_" + decoratorType + "_" + direction
* // CSSClass = "con_100_source_LEFT"
*
* @extends PMUI.draw.Core
*
* @constructor Creates an instance of the class ConnectionDecorator
* @param {Object} options Initialization options
* @cfg {PMUI.util.Point} [decoratorPrefix=''] Decorator prefix used to
reconstruct the css class for the sprite
* @cfg {PMUI.draw.Connection} [parent=null] The parent of this decorator
(must be an instance of the class Connection)
* @cfg {Object} [style={cssClasses: [], cssProperties: {}}] CSS classes and properties
*/
var ConnectionDecorator = function (options) {
ConnectionDecorator.superclass.call(this, options);
/**
* Parent of this decorator (must be a Connection)
* @property {PMUI.draw.Connection}
*/
this.parent = null;
/**
* The type of this decorator (either "source" or "target")
* @property {"source" / "target"}
*/
this.decoratorType = null;
/**
* Decorator prefix of this decorator to build the CSS class for the sprite
* @property {String}
*/
this.decoratorPrefix = null;
/**
* This parameter is an array to see if the end point
* is UP, RIGHT, BOTTOM and LEFT
* @property {Object} spriteDirection
* @property {string} [spriteDirection.0="up"] Enum for "up"
* @property {string} [spriteDirection.1="right"] Enum for "right"
* @property {string} [spriteDirection.2="bottom"] Enum for "bottom"
* @property {string} [spriteDirection.3="left"] Enum for "left"
*/
this.spriteDirection = {
'0': 'top', '1': 'right',
'2': 'bottom', '3': 'left'
};
/**
* Height of this decorator
* @property {number} [height=11]
*/
this.height = 11;
/**
* Width of this decorator
* @property {number} [width=11]
*/
this.width = 11;
/**
* Separator used to build the class
* @type {String}
*/
this.separator = null;
/**
* Sprite used to build the class
* @type {String}
*/
this.sprite = null;
/**
* The class that will be constructed using the parameters given in
* the options object
* @type {string}
*/
this.cssClass = null;
ConnectionDecorator.prototype.initObject.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', ConnectionDecorator);
/**
* Type of this connection decorator
* @property {String}
*/
ConnectionDecorator.prototype.type = "ConnectionDecorator";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains the config
* @private
*/
ConnectionDecorator.prototype.initObject = function (options) {
var defaults = {
width: 11,
height: 11,
sprite: 'bpmn_zoom',
decoratorPrefix: '',
separator: '_',
decoratorType: 'target',
parent: null
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// init
this.setDecoratorType(defaults.decoratorType)
.setDecoratorPrefix(defaults.decoratorPrefix)
.setSeparator(defaults.separator)
.setSprite(defaults.sprite)
.setParent(defaults.parent)
.setDimension(defaults.width, defaults.height)
.setCssClass(''); // cssClass defaults to empty
};
/**
* Paints the connectionDecorator according to the parameters saved in `this.initObject`.
* The steps to paint the decorator are:
*
* 1. Determine if this decorator belongs to the source or destination port
* 2. Determine the direction of the decorator
* 3. Build the class using the direction, decorator prefix, decorator type and zoom
* 4. Determine the position of this decorator
*
* @chainable
*/
ConnectionDecorator.prototype.paint = function (options) {
var linePoint,
portPoint,
canvas,
direction,
lineSegment,
port,
topStyle,
leftStyle;
if (this.decoratorType === "source") {
port = this.parent.getSrcPort();
lineSegment = this.parent.lineSegments.get(0);
linePoint = lineSegment.startPoint;
} else {
port = this.parent.getDestPort();
lineSegment = this.parent.lineSegments.get(this.parent.lineSegments.getSize() - 1);
linePoint = lineSegment.endPoint;
}
portPoint = port.getPoint();
portPoint.x = Math.round(portPoint.x - this.canvas.absoluteX);
portPoint.y = Math.round(portPoint.y - this.canvas.absoluteY);
direction = port.getDirection();
canvas = port.canvas;
switch (direction) {
case 0:
case 2:
if (this.zoomWidth % 2 === 0) {
leftStyle = (this.zoomWidth / 2) - 1;
} else {
leftStyle = Math.floor(this.zoomWidth / 2);
}
leftStyle = linePoint.x - leftStyle;
topStyle = portPoint.y - (direction === 0 ? this.zoomHeight : 0);
break;
case 1:
case 3:
if (this.zoomHeight % 2 === 0) {
topStyle = (this.zoomHeight / 2) - 1;
} else {
topStyle = Math.floor(this.zoomHeight / 2);
}
topStyle = linePoint.y - topStyle;
leftStyle = portPoint.x - ((direction === 1) ? 0 : this.zoomWidth);
break;
default:
throw new Error("paint(): invalid direction.");
}
if (this.getHTML() === null) {
this.createHTML();
}
if (this.decoratorType === null) {
this.html = null;
return this;
}
// remove the last class if possible
this.style.removeClasses([this.cssClass]);
// construct the new class to be applied
this.setCssClass([this.prefix, parseInt(canvas.zoomFactor * 100, 10),
this.decoratorType, this.spriteDirection[direction]].join(this.separator));
this.style.addClasses([
this.sprite,
this.getCssClass()
]);
// top and left position
this.style.addProperties({
top: topStyle,
left: leftStyle
});
this.parent.html.appendChild(this.html);
return this;
};
/**
* Creates the HTML Representation of the SourceSpriteConnectionDecorator
* @returns {HTMLElement}
*/
ConnectionDecorator.prototype.createHTML = function () {
this.html = document.createElement('div');
this.html.id = this.id;
this.style.applyStyle();
this.style.addProperties({
position: "absolute",
left: 0,
top: 0,
height: this.zoomHeight,
width: this.zoomWidth,
zIndex: PMUI.util.Style.MAX_ZINDEX
});
return this.html;
};
/**
* Attaches listeners to the connectionDecorator (currently it has click and mouseDown events)
* @chainable
*/
ConnectionDecorator.prototype.attachListeners = function () {
var $connectionDecorator;
$connectionDecorator = $(this.getHTML()).click(this.onClick(this));
$connectionDecorator.on("mousedown", this.onMouseDown(this));
return this;
};
/**
* Refresh the dimension and position of the decorator to apply the current
* zoom scale
* @chainable
*/
ConnectionDecorator.prototype.applyZoom = function () {
this.setDimension(this.width, this.height);
return this;
};
/**
* @event mousedown
* ConnectionDecorator mouse down callback fired when the mouse is down on it.
* @param {PMUI.draw.ConnectionDecorator} decorator
*/
ConnectionDecorator.prototype.onMouseDown = function (decorator) {
return function (e, ui) {
e.preventDefault();
if (e.which === 3) {
decorator.parent.canvas.updatedElement = decorator.parent;
$(decorator.parent.canvas.html).trigger("rightclick");
}
e.stopPropagation();
};
};
/**
* @event click
* Click callback fired when the decorator is clicked on.
* It hides the currentSelection if any and shows the ports and handlers of `decorator` parent
* (which is a connection).
* @param {PMUI.draw.ConnectionDecorator} decorator
*/
ConnectionDecorator.prototype.onClick = function (decorator) {
return function (e, ui) {
var connection = decorator.parent,
oldConnection = decorator.parent.canvas.currentConnection,
canvas = connection.canvas;
// SHOW
// show the ports and the handlers of the new connection
connection.showPortsAndHandlers();
// HIDE
// if there were some shapes in the current selection then
// empty the current selection
canvas.emptyCurrentSelection();
// if there was a connection previously select hide its ports
// and its handlers
if (oldConnection) {
oldConnection.hidePortsAndHandlers();
}
// set the old connection as this connection
canvas.currentConnection = connection;
// TODO: zIndex
e.stopPropagation();
};
};
/**
* Serializes this connection decorator.
* @return {Object}
* @return {"source" / "target"} return.decoratorType The decorator type to build the CSS class for the sprite
* @return {string} return.decoratorPrefix The decorator prefix to build the CSS class for the sprite
*/
ConnectionDecorator.prototype.stringify = function () {
var inheritedJSON = {},
thisJSON = {
decoratorType: this.getDecoratorType(),
decoratorPrefix: this.getDecoratorPrefix()
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
/**
* Returns the decorator type
* @returns {String}
*/
ConnectionDecorator.prototype.getDecoratorType = function () {
return this.decoratorType;
};
/**
* Sets the decoration type
* @param {String} newType
* @chainable
*/
ConnectionDecorator.prototype.setDecoratorType = function (newType) {
this.decoratorType = newType;
return this;
};
/**
* Returns the decorator type
* @returns {String}
*/
ConnectionDecorator.prototype.getDecoratorPrefix = function () {
return this.prefix;
};
/**
* Sets the decoration prefix
* @param {String} newType
* @chainable
*/
ConnectionDecorator.prototype.setDecoratorPrefix = function (newType) {
this.prefix = newType;
return this;
};
/**
* Sets the parent of this connectionDecorator
* @param {PMUI.draw.Connection} newParent
* @chainable
*/
ConnectionDecorator.prototype.setParent = function (newParent) {
this.parent = newParent;
return this;
};
/**
* Gets the parent of this connectionDecorator
* @return {PMUI.draw.Connection}
*/
ConnectionDecorator.prototype.getParent = function () {
return this.parent;
};
/**
* Sets the separator of this connectionDecorator
* @param {String} newSeparator
* @chainable
*/
ConnectionDecorator.prototype.setSeparator = function (newSeparator) {
this.separator = newSeparator;
return this;
};
/**
* Sets the sprite of this connectionDecorator
* @param {String} newSprite
* @chainable
*/
ConnectionDecorator.prototype.setSprite = function (newSprite) {
this.sprite = newSprite;
return this;
};
/**
* Sets the cssClass of this connectionDecorator
* @param {string} newCssClass
* @chainable
*/
ConnectionDecorator.prototype.setCssClass = function (newCssClass) {
this.cssClass = newCssClass;
return this;
};
/**
* Gets the cssClass of this connectionDecorator
* @return {string}
*/
ConnectionDecorator.prototype.getCssClass = function () {
return this.cssClass;
};
PMUI.extendNamespace('PMUI.draw.ConnectionDecorator', ConnectionDecorator);
if (typeof exports !== 'undefined') {
module.exports = ConnectionDecorator;
}
}());
(function () {
/**
*
* @class PMUI.draw.Layer
* Class that contains the properties of a layer for a shape we need
* to have a shape already instantiated and added to the canvas in order for
* this class to be effective
*
* //e.g.
* var layer = new PMUI.draw.Layer({
* //Determine the layer's parent
* parent: customShape,
* layerName: "first layer",
* //the order in which the layers will be added in increasing
* //order
* priority: 0,
* //determines if the layer will be hidden or visible
* visible: true,
* //sprites to be applied on the layers according to a zoom
* //scale
* zoomSprites : ["class50, class75, class100, class125,
* class 150"]
* });
* @extend PMUI.draw.Core
*
* @constructor
* Initializes a layer, the constructor must be called with all its parameter
* for the object to be meaningful, its is important to denote that the css
* class must follow this structure
* any word_zoomScale_anythingYouWantHere
* @param {Object} options
* @cfg {Object} parent, Parent of a corresponding layer, a layer may not exist
* without a parent
* @cfg {String} [layerName="defaultLayerName"] A name we want to label a layer
* with
* @cfg {number} [priority=0] The orders in which the layers will be added in
* increasing order
* @cfg {boolean} [visible=true] Determines whether a layer wll be visible or
* hidden
* @cfg {Array} [zoomSprites=["","","","",""]] Sprites to be applied to the
* layer according to a zoom scale
*/
var Layer = function (options) {
// TODO: check elementClass and bpmnClass removal impact on the layers
//Layer = function (parent, name, elementClass, priority, bpmnClass, visible) {
Layer.superclass.call(this, options);
/**
* The name of the layer
* @property {String}
*/
this.layerName = null;
/**
* The priority of the layer, determines which layer should be on top
* @property {number}
*/
this.priority = null;
/**
* The bpmnShape that this layer belongs too.
* Extremely important since some data will be strictly drawn by its parent
* @property {Object}
*/
this.parent = null;
/**
* Determines when a layer is visible or not
* @property boolean
*/
this.visible = null;
/**
* Determines when a layer is resizable or not
* @property boolean
*/
this.resizable = null;
/**
* The current Sprite applied in the zoom scale
* @property {String}
*/
this.currentZoomClass = "";
/**
* Sprites for the layer in each zoom scale
* @property {Array}
*/
this.zoomSprites = [];
Layer.prototype.initObject.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', Layer);
/**
* Type of an instance of this class
* @property {String}
*/
Layer.prototype.type = "Layer";
/**
* Object init method (internal)
* @param {Object} options
*/
Layer.prototype.initObject = function (options) {
/**
* Default options for the object
* @property {Object}
*/
var defaults = {
x: 0,
y: 0,
parent: null,
layerName: "defaultLayerName",
resizable: true,
priority: 0,
visible: true,
zoomSprites: ["", "", "", "", ""]
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// call setters using the defaults object
this.setParent(defaults.parent)
.setPosition(defaults.x, defaults.y)
.setLayerName(defaults.layerName)
.setPriority(defaults.priority)
.setVisible(defaults.visible)
.setZoomSprites(defaults.zoomSprites)
.setProperties()
.setResizable(defaults.resizable);
};
/**
* Updates the properties in order to change zoom scales
*/
Layer.prototype.applyZoom = function () {
this.setProperties();
};
/**
* Updates the property resizable
*/
Layer.prototype.setResizable = function (option) {
this.resizable = option;
};
/**
* Comparison function for ordering layers according to priority
* @param {PMUI.draw.Layer} layer1
* @param {PMUI.draw.Layer} layer2
* @returns {boolean}
*/
Layer.prototype.comparisonFunction = function (layer1, layer2) {
if (layer1.priority > layer2.priority) {
return -1;
} else if (layer1.priority > layer2.priority) {
return 1;
}
return 0;
};
/**
* Creates the HTML representation of the layer
* @returns {HTMLElement}
*/
Layer.prototype.createHTML = function (modifying) {
this.setProperties();
Layer.superclass.prototype.createHTML.call(this, modifying);
return this.html;
};
/**
* Paints the corresponding layer, in this case adds the
* corresponding css classes
* @chainable
*/
Layer.prototype.paint = function () {
var $layer = $(this.html),
newSprite;
this.style.removeClasses([this.currentZoomClass]);
newSprite = this.zoomSprites[this.canvas.zoomPropertiesIndex];
this.style.addClasses([newSprite]);
this.currentZoomClass = newSprite;
this.style.applyStyle();
return this;
};
/**
* This method will set the parent necessary properties for the layer to work
* @chainable
*/
Layer.prototype.setProperties = function () {
if (!this.parent) {
return this;
}
//generates an id for the layer
this.id = this.parent.getID() + "Layer-" + this.layerName;
if ((typeof this.width !== "number") && (typeof this.height !== "number")) {
this.setDimension(this.parent.getWidth(), this.parent.getHeight());
} else {
this.setDimension(this.width, this.height);
}
if (this.resizable)
this.setDimension(this.parent.getWidth(), this.parent.getHeight());
// DO NOT ASSUME THAT THE POSITION OF THE LAYER IS 0,0 BECAUSE OF THE
// BORDERS IT MAY HAVE
this.canvas = this.parent.canvas;
return this;
};
/**
* Returns the layer name
* @returns {String}
*/
Layer.prototype.getLayerName = function () {
return this.layerName;
};
/**
* Returns the priority of the layer
* @returns {number}
*/
Layer.prototype.getPriority = function () {
return this.priority;
};
/**
* Sets the layer name
* @param {String} newLayerName
* @chainable
*/
Layer.prototype.setLayerName = function (newLayerName) {
if (typeof newLayerName === "string" && newLayerName !== "") {
this.layerName = newLayerName;
}
return this;
};
/**
* Sets the priority of the layer
* @param {number} newPriority
* @chainable
*/
Layer.prototype.setPriority = function (newPriority) {
if (typeof newPriority === "number") {
this.priority = newPriority;
}
return this;
};
/**
* Sets the parent of this layer
* @param {PMUI.draw.CustomShape} newParent
* @chainable
*/
Layer.prototype.setParent = function (newParent) {
if (newParent) {
this.parent = newParent;
}
return this;
};
/**
* Gets the parent of this layer
* @return {PMUI.draw.Shape}
*/
Layer.prototype.getParent = function () {
return this.parent;
};
/**
* Sets the css classes for the zoom scales
* @param {Array} zoomSprites
* @chainable
*/
Layer.prototype.setZoomSprites = function (zoomSprites) {
var i;
this.zoomSprites = ["", "", "", "", ""];
for (i = 0; i < zoomSprites.length; i += 1) {
this.zoomSprites[i] = zoomSprites[i];
}
return this;
};
/**
* Serializes this object
* @return {Object}
*/
Layer.prototype.stringify = function () {
/**
* inheritedJSON = {
* id: #
* x: #,
* y: #,
* width: #,
* height: #
* }
* @property {Object}
*/
var inheritedJSON = {},
thisJSON = {
id: this.getID(),
x: this.getX(),
y: this.getY(),
layerName: this.getLayerName(),
priority: this.getPriority(),
style: {
cssClasses: this.style.getClasses()
},
zoomSprites: this.zoomSprites
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
PMUI.extendNamespace('PMUI.draw.Layer', Layer);
if (typeof exports !== 'undefined') {
module.exports = Layer;
}
}());
(function () {
/**
* @abstract
* @class PMUI.draw.Handler
* Abstract class which provides methods to represent a handler.
* @extends PMUI.draw.Core
*
* @constructor Creates an instance of the class Handler (for inheritance purposes only).
* @param {Object} options Initialization options.
*/
var Handler = function (options) {
Handler.superclass.call(this, options);
/**
* Representation of this handler.
* @property {Object}
*/
this.representation = null;
/**
* The parent of this handler.
* @property {PMUI.draw.Shape}
*/
this.parent = null;
/**
* Color of this handler.
* @property {PMUI.util.Color}
*/
this.color = null;
/**
* The orientation of this handler.
* @property {string}
*/
this.orientation = null;
};
PMUI.inheritFrom('PMUI.draw.Core', Handler);
/**
* Sets the parent of this handler
* @param newParent
* @chainable
*/
Handler.prototype.setParent = function (newParent) {
this.parent = newParent;
return this;
};
/**
* Gets the parent of this handler
* @return {PMUI.draw.Shape}
*/
Handler.prototype.getParent = function () {
return this.parent;
};
/**
* Sets the representation of this handler
* @param representation
* @chainable
*/
Handler.prototype.setRepresentation = function (representation) {
this.representation = representation;
return this;
};
/**
* Gets the representation of this handler
* @return {Object}
*/
Handler.prototype.getRepresentation = function () {
return this.representation;
};
/**
* Sets the orientation of this handler
* @param newOrientation
* @chainable
*/
Handler.prototype.setOrientation = function (newOrientation) {
this.orientation = newOrientation;
return this;
};
/**
* Gets the orientation of this handler
* @return {string}
*/
Handler.prototype.getOrientation = function () {
return this.orientation;
};
/**
* Paint the handler method which will call `this.representation.paint()`
* @chainable
*/
Handler.prototype.paint = function () {
this.representation.paint.call(this);
this.style.applyStyle();
return this;
};
/**
* The color representation of this object
* @param {PMUI.util.Color} newColor
* @chainable
*/
Handler.prototype.setColor = function (newColor) {
this.color = newColor;
return this;
};
/**
* Get the color representation of this object
* @return {PMUI.util.Color}
*/
Handler.prototype.getColor = function () {
return this.color;
};
PMUI.extendNamespace('PMUI.draw.Handler', Handler);
if (typeof exports !== 'undefined') {
module.exports = Handler;
}
}());
(function () {
/**
* @class PMUI.draw.ResizeHandler
* Defines a class resize handler to represent handlers used with jQueryUI' resizable plugin, currently
* it has only support for rectangle resize handler (oval resize handlers were implemented but apparently
* jQueryUI won't accept a child of the designated HTMLElement to be used as the resize handler).
*
* An example of use:
*
* // e.g.
* // let's assume that shape is an instance of the class Shape
* // let's assume that rectangle is an instance of the class Rectangle
*
* var resizableStyle = {
* cssProperties: {
* 'background-color': "rgb(0, 255, 0)",
* 'border': '1px solid black'
* }
* },
* nonResizableStyle = {
* cssProperties: {
* 'background-color': "white",
* 'border': '1px solid black'
* }
* },
* resizeHandler;
*
* resizeHandler = new PMUI.draw.ResizeHandler({
* width: 8,
* height: 8,
* parent: shape,
* orientation: 'nw' // see jQueryUI's resizable plugin 'handles' option
* representation: rectangle,
* resizableStyle: resizableStyle,
* nonResizableStyle: nonResizableStyle,
* zOrder: 2
* });
*
* @extend PMUI.draw.Handler
* @constructor Creates an instance of resize handler.
* @param {Object} options
* @cfg {number} [width=4] The width of this resize handler.
* @cfg {number} [height=4] The height of this resize handler.
* @cfg {PMUI.draw.Shape} [parent=null] The parent of this resize handler.
* @cfg {string} [orientation=null] The orientation of this resize handler.
* @cfg {string} [representation=null] The representation of this resize handler.
* @cfg {Object} [resizableStyle={}] The parameters to create an instance of the class Style used
* when the object is resizable.
* @cfg {Object} [nonResizableStyle={}] The parameters to create an instance of the class Style used
* when the object is not resizable.
* @cfg {number} [zOrder=2] The z-index of this resize handler.
*/
var ResizeHandler = function (options) {
ResizeHandler.superclass.call(this, options);
/**
* Category of this resize handler
* @type {"resizable"/"nonresizable"}
*/
this.category = null;
/**
* Denotes whether the resize handle is visible or not.
* @property boolean
*/
this.visible = false;
/**
* JSON used to create an instance of the class Style used when the object is resizable.
* @property {Object}
*/
this.resizableStyle = null;
/**
* JSON used to create an instance of the class Style used when the object is not resizable.
* @property {Object}
*/
this.nonResizableStyle = null;
// set defaults
ResizeHandler.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Handler', ResizeHandler);
/**
* The type of each instance of this class.
* @property {String}
*/
ResizeHandler.prototype.type = "ResizeHandler";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains the config
* @private
*/
ResizeHandler.prototype.init = function (options) {
var defaults = {
width: 4,
height: 4,
parent: null,
orientation: null,
representation: null,
resizableStyle: {},
nonResizableStyle: {},
zOrder: 2
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// add default zIndex to this handler
if (defaults.resizableStyle.cssProperties) {
defaults.resizableStyle.cssProperties.zIndex = defaults.zOrder;
}
if (defaults.nonResizableStyle.cssProperties) {
defaults.nonResizableStyle.cssProperties.zIndex = defaults.zOrder;
}
// init
this.setParent(defaults.parent)
.setWidth(defaults.width)
.setHeight(defaults.height)
.setOrientation(defaults.orientation)
.setRepresentation(defaults.representation)
.setResizableStyle(defaults.resizableStyle)
.setNonResizableStyle(defaults.nonResizableStyle);
// create the id
this.id = defaults.orientation + defaults.parent.id + "resizehandler";
};
/**
* Sets the parent of this handler
* @param {PMUI.draw.Shape} newParent
* @chainable
*/
ResizeHandler.prototype.setParent = function (newParent) {
this.parent = newParent;
return this;
};
/**
* Gets the parent of this handler.
* @return {PMUI.draw.Shape}
*/
ResizeHandler.prototype.getParent = function () {
return this.parent;
};
/**
* Paints this resize handler by calling it's parent's `paint` and setting
* the visibility of this resize handler
* @chainable
*/
ResizeHandler.prototype.paint = function () {
if (!this.html) {
throw new Error("paint(): This handler has no html");
}
// this line paints the representation (by default a rectangle)
ResizeHandler.superclass.prototype.paint.call(this);
this.setVisible(this.visible);
return this;
};
/**
* Sets the category of the resizeHandler (also adds the needed class to
* make the element resizable)
* @param newCategory
* @chainable
*/
ResizeHandler.prototype.setCategory = function (newCategory) {
if (typeof newCategory === "string") {
this.category = newCategory;
}
if (this.category === "resizable") {
this.color = new PMUI.util.Color(0, 255, 0);
this.style.addClasses([
"ui-resizable-handle", "ui-resizable-" + this.orientation
]);
} else {
this.color = new PMUI.util.Color(255, 255, 255);
this.style.removeClasses([
"ui-resizable-handle", "ui-resizable-" + this.orientation
]);
}
return this;
};
/**
* Sets the resizable style of this shape by creating an instance of the class Style
* @param {Object} style
* @chainable
*/
ResizeHandler.prototype.setResizableStyle = function (style) {
this.resizableStyle = new PMUI.util.Style({
belongsTo: this,
cssProperties: style.cssProperties,
cssClasses: style.cssClasses
});
return this;
};
/**
* Sets the non resizable style for this shape by creating an instance of the class Style
* @param {Object} style
* @chainable
*/
ResizeHandler.prototype.setNonResizableStyle = function (style) {
this.nonResizableStyle = new PMUI.util.Style({
belongsTo: this,
cssProperties: style.cssProperties,
cssClasses: style.cssClasses
});
return this;
};
PMUI.extendNamespace('PMUI.draw.ResizeHandler', ResizeHandler);
if (typeof exports !== 'undefined') {
module.exports = ResizeHandler;
}
}());
(function () {
/**
* @class PMUI.draw.SegmentMoveHandler
* Represents the handler to move a segment (the handlers are visible when a decorator of the parent of this
* segment is clicked on)
*
* An example of use:
*
* // e.g.
* // let's assume that segment is an instance of the class Segment
* // let's assume that rectangle is an instance of the class Rectangle
*
* segmentMoveHandler = new PMUI.draw.SegmentMoveHandler({
* width: 8,
* height: 8,
* parent: segment,
* orientation: 0 // corresponds to a vertical segment
* representation: rectangle,
* color: new Color(255, 0, 0) // red !!
* });
* @extend PMUI.draw.Handler
* @constructor Creates an instance of the class SegmentMoveHandler
* @param {Object} options
* @cfg {number} [width=4] The width of this segment move handler.
* @cfg {number} [height=4] The height of this segment move handler.
* @cfg {PMUI.draw.Shape} [parent=null] The parent of this segment move handler.
* @cfg {number} [orientation=null] The orientation of this segment move handler.
* @cfg {string} [representation=null] The representation of this segment move handler.
* @cfg {number} [color=new Color(0, 255, 0)] The color of this segment move handler (green).
*/
var SegmentMoveHandler = function (options) {
SegmentMoveHandler.superclass.call(this, options);
/**
* Orientation of this segment move handler (useful to do the drag).
* @property {number}
*/
this.orientation = null;
/**
* Denotes whether the SegmentMove point is visible or not.
* @property {boolean} [visible=false]
*/
this.visible = false;
/**
* The default zOrder of this handler.
* @property {number} [zOrder=2]
*/
this.zOrder = 2;
// set defaults
SegmentMoveHandler.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Handler', SegmentMoveHandler);
/**
* Type of each instance of this class
* @property {String}
*/
SegmentMoveHandler.prototype.type = "SegmentMoveHandler";
/**
* Instance initializer which uses options to extend the config options to initialize the instance
* @param {Object} options The object that contains the config
* @private
*/
SegmentMoveHandler.prototype.init = function (options) {
var defaults = {
width: 4,
height: 4,
parent: null,
orientation: null,
representation: new PMUI.draw.Rectangle(),
color: new PMUI.util.Color(0, 255, 0)
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// init
this.setWidth(defaults.width)
.setHeight(defaults.height)
.setParent(defaults.parent)
.setColor(defaults.color)
.setOrientation(defaults.orientation)
.setRepresentation(defaults.representation);
};
/**
* Paints this resize handler by calling it's parent's `paint` and setting
* the visibility of this resize handler.
* @chainable
*/
SegmentMoveHandler.prototype.paint = function () {
// before it was: Rectangle.prototype.paint.call(this);
SegmentMoveHandler.superclass.prototype.paint.call(this);
this.setVisible(this.visible);
return this;
};
/**
* Attaches listeners to the segmentMoveHandler, by default it creates the click,
* mouseDown and draggable events.
* @param {PMUI.draw.SegmentMoveHandler} handler
* @chainable
*/
SegmentMoveHandler.prototype.attachListeners = function (handler) {
var $handler = $(handler.html);
$handler.on('click', handler.onClick(handler));
$handler.on('mousedown', handler.onMouseDown(handler));
$handler.draggable({
start: handler.onDragStart(handler),
drag: handler.onDrag(handler),
stop: handler.onDragEnd(handler),
axis: (handler.orientation === handler.HORIZONTAL) ? "y" : "x"
//containment: handler.parent.parent.html
});
return this;
};
/**
* @event mousedown
* MouseDown callback fired when the user mouse downs on the `handler`
* @param {PMUI.draw.SegmentMoveHandler} handler
*/
SegmentMoveHandler.prototype.onMouseDown = function (handler) {
return function (e, ui) {
// This is done to avoid the start of a selection in the canvas
// handler > segment > connection > canvas
handler.parent.parent.canvas.draggingASegmentHandler = true;
};
};
/**
* @event click
* Click callback fired when the user clicks on the handler
* @param {PMUI.draw.SegmentMoveHandler} handler
*/
SegmentMoveHandler.prototype.onClick = function (handler) {
return function (e, ui) {
e.stopPropagation();
};
};
/**
* @event dragStart
* DragStart callback fired when the handler is dragged (it's executed only once).
* It does the following:
*
* 1. Gather the connection by calling the handler's grandparent
* 2. Save the state if the connection (for the undo-redo stack)
* 3. Clear all the intersections of each segment of the connection
*
* @param {PMUI.draw.SegmentMoveHandler} handler
*/
SegmentMoveHandler.prototype.onDragStart = function (handler) {
return function (e, ui) {
var parentSegment = handler.parent,
segment,
connection = parentSegment.getParent(),
i;
// TESTING:
// save values for the undo-redo stack
connection.savePoints({
saveToOldPoints: true
});
// clear all intersections that exists in
// parentSegment.parent (connection)
for (i = 0; i < parentSegment.parent.lineSegments.getSize(); i += 1) {
segment = parentSegment.parent.lineSegments.get(i);
segment.clearIntersections();
}
// clear all intersections that exists among other connections and
// parentSegment (the ones that exists in the other connections)
parentSegment.parent.clearAllIntersections();
e.stopPropagation();
};
};
/**
* @event drag
* Drag callback fired when the handler is being dragged.
* It only moves the segment vertically or horizontally.
*
* @param {PMUI.draw.SegmentMoveHandler} handler
*/
SegmentMoveHandler.prototype.onDrag = function (handler) {
return function (e, ui) {
var parentSegment = handler.parent;
parentSegment.moveSegment(ui.position.left, ui.position.top);
};
};
/**
* @event dragEnd
* DragEnd callback fired when the handler stops being dragged.
* It does the following:
*
* 1. Gather the connection by calling the handler's grandparent
* 2. Save the state if the connection (for the undo-redo stack)
* 3. Create a command for the undo-redo stack
*
* @param {PMUI.draw.SegmentMoveHandler} handler
*/
SegmentMoveHandler.prototype.onDragEnd = function (handler) {
return function (e, ui) {
var parentSegment = handler.parent,
connection = parentSegment.getParent(),
canvas = connection.canvas,
command;
canvas.draggingASegmentHandler = false;
handler.onDrag(handler)(e, ui);
// LOGIC: connection.points is an array of points that is not updated
// automatically when a connection is painted, it must be
// explicitly called as connection.savePoints()
connection.savePoints();
command = new PMUI.command.CommandSegmentMove(connection, {
oldPoints: connection.getOldPoints(),
newPoints: connection.getPoints()
});
command.execute();
canvas.commandStack.add(command);
};
};
PMUI.extendNamespace('PMUI.draw.SegmentMoveHandler', SegmentMoveHandler);
if (typeof exports !== 'undefined') {
module.exports = SegmentMoveHandler;
}
}());
(function () {
/**
* @class PMUI.draw.Port
* Class Port represent a special point in a shape where each point is one end point of a connection
* (a customShape has many ports and each port has a reference to the connection it belongs to).
*
* The relation of this class with customShape and connections are described below:
*
* - Each port is one end point of a connection (the connection has links to the start port and the end port,
* the port has a link to the connection)
* - A custom shape might have *n* ports (the parent of the port is the custom shape)
* so the custom shape has the HTML of the port on it.
*
* Some examples of usage:
*
* // let's assume the connection is an instance of the class Connection
* // let's assume the customShape is an instance of the class CustomShape
* var port = new PMUI.draw.Port({
* width: 8,
* height: 8,
* visible: true,
* parent: customShape
* })
*
* // after a port is created, it need to be added to the customShape
* // let's add it at position [100, 100]
* customShape.addPort(port, 100, 100)
*
* // finally when a connection is created it needs to have links to the ports
* // let's assume that another port is an instance of the class Port
* // i.e
* connection = new PMUI.draw.Connection({
* srcPort: port,
* destPort: anotherPort,
* segmentColor: new PMUI.util.Color(0, 200, 0),
* segmentStyle: "regular"
* });
*
* @extend PMUI.draw.Core
*
*
* @param {Object} options Initialization options
* @cfg {number} [width=4] The width of this port
* @cfg {number} [height=4] The height of this port
* @cfg {boolean} [visible=false] The visibility of this port
* @cfg {PMUI.draw.CustomShape} [parent=null] The parent of this port
*
* @constructor Creates an instance of the class Port
*/
var Port = function (options) {
Port.superclass.call(this);
/**
* Connection to whom this port belongs to
* @property {PMUI.draw.Connection}
*/
this.connection = null;
/**
* Representation (Shape) of the port when it is connected (currently it's represented as an
{@link PMUI.draw.Oval})
* @property {PMUI.draw.Shape}
*/
this.representation = null;
/**
* Parent of this port.
* @property {PMUI.draw.CustomShape}
*/
this.parent = null;
/**
* Old parent of this port.
* @property {PMUI.draw.CustomShape}
*/
this.oldParent = null;
/**
* Port direction respect to its parent (its parent is an instance of {@link PMUI.draw.CustomShape}).
* @property {number}
*/
this.direction = null;
/**
* The percentage relative to where the port is located regarding one of
* the shape dimensions (useful to recalculate the ports position while resizing).
* @property {number}
*/
this.percentage = null;
/**
* Current zIndex of the port.
* @property {number} [zOrder=1]
*/
this.zOrder = 1;
/**
* Default zIndex of the ports.
* @property {number} [defaultZOrder=1]
*/
this.defaultZOrder = 1;
/**
* X coordinate sent to the database
* @property {number} [realX=0]
*/
this.realX = 0;
/**
* Y coordinate sent to the database
* @property {number} [realY=0]
*/
this.realY = 0;
Port.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.RegularShape', Port);
/**
* The distance moved when a connection is selected (when a connection is
* selected the ports move towards the center of the shape so that it's
* easier to drag the ports)
* @property {number} [TOWARDS_CENTER=5]
*/
Port.prototype.TOWARDS_CENTER = 5;
/**
* Type of each instance of this class
* @property {String}
*/
Port.prototype.type = "Port";
/**
* Instance initializer which uses options to extend the config options to initialize the instance.
* @param {Object} options The object that contains the config
* @private
*/
Port.prototype.init = function (options) {
/**
* Default options for the object
* @property {Object}
*/
var defaults = {
width: 4,
height: 4,
visible: false,
parent: null
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
$.extend(true, defaults, {
// oval is initialized with default values
representation: new PMUI.draw.Oval({
width: defaults.width,
height: defaults.height,
center: new PMUI.util.Point(0, 0),
visible: true
})
});
// call setters using the defaults object
this.setVisible(defaults.visible)
.setParent(defaults.parent)
.setDimension(defaults.width, defaults.height)
.setRepresentation(defaults.representation);
};
/**
* Creates the HTML Representation of the Port
* @returns {HTMLElement}
*/
Port.prototype.createHTML = function () {
Port.superclass.prototype.createHTML.call(this);
this.style.addClasses(["port"]);
this.setPosition(this.x, this.y);
return this.html;
};
/**
* Sets the x coordinate of this port
* @param {number} newX
* @chainable
*/
Port.prototype.setX = function (newX) {
if (this.canvas) {
this.realX = newX / this.canvas.zoomFactor;
} else {
this.realX = newX;
}
this.x = this.zoomX = Math.round(newX);
this.setAbsoluteX();
if (this.html) {
this.style.addProperties({left: this.zoomX});
}
return this;
};
/**
* Sets the y coordinate of this port
* @param {number} newY
* @chainable
*/
Port.prototype.setY = function (newY) {
this.y = this.zoomY = Math.round(newY);
this.setAbsoluteY();
if (this.canvas) {
this.realY = newY / this.canvas.zoomFactor;
} else {
this.realY = newY;
}
if (this.html) {
this.style.addProperties({top: this.zoomY});
}
return this;
};
/**
* Sets the width of this port
* @param {number} newWidth
* @chainable
*/
Port.prototype.setWidth = function (newWidth) {
this.width = newWidth;
this.zoomWidth = this.width;
if (this.html) {
this.style.addProperties({width: this.zoomWidth});
}
return this;
};
/**
* Sets the height of this port
* @param {number} newHeight
* @chainable
*/
Port.prototype.setHeight = function (newHeight) {
this.height = newHeight;
this.zoomHeight = this.height;
if (this.html) {
this.style.addProperties({height: this.zoomHeight});
}
return this;
};
/**
* Paint the port appending its `representation` HTML to `this` HTML.
* @chainable
*/
Port.prototype.paint = function () {
this.html.appendChild(this.representation.getHTML());
this.representation.paint();
// sets the visibility of this port
this.setVisible(this.visible);
this.style.applyStyle();
return this;
};
/**
* Repaints the port re-applying its left and top position.
* @param {PMUI.draw.Port} port
* @chainable
*/
Port.prototype.repaint = function (port) {
port.style.addProperties({
left: port.x,
top: port.y
});
port.connection.connect();
port.connection.setSegmentMoveHandlers();
port.connection.checkAndCreateIntersectionsWithAll();
return this;
};
/**
* @event dragStart
* DragStart callback fired when the port is dragged (it's executed only once).
* It does the following:
*
* 1. Moves the port away from the center
* 2. Moves the otherPort away from the center
* 3. Disconnects the connection
*
* @param {PMUI.draw.Port} port
* @param {PMUI.draw.Port} otherPort
*/
Port.prototype.onDragStart = function (port, otherPort) {
return function (e, ui) {
// move the ports off the center, they'll be correctly repositioned
// later (in "onDragEnd")
otherPort.moveTowardsTheCenter(true);
port.moveTowardsTheCenter(true);
port.connection.disconnect();
return true;
};
};
/**
* @event drag
* Drag callback fired when the port is being dragged.
* It makes a new segment from the other port to the current position of the mouse.
*
* @param {PMUI.draw.Port} port
* @param {PMUI.util.Point} endPoint
* @param {PMUI.draw.Port} otherPort
* @param {PMUI.draw.Canvas} canvas
*/
Port.prototype.onDrag = function (port, endPoint, otherPort, canvas) {
return function (e, ui) {
var startPoint;
if (canvas.connectionSegment) {
$(canvas.connectionSegment.getHTML()).remove();
}
endPoint.x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX();
endPoint.y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY();
//make connection segment
startPoint = otherPort.getPoint(false);
startPoint.x = startPoint.x - canvas.getAbsoluteX();
startPoint.y = startPoint.y - canvas.getAbsoluteY();
canvas.connectionSegment = new PMUI.draw.Segment({
startPoint: startPoint,
endPoint: endPoint,
parent: canvas
});
canvas.connectionSegment.pointsTo = port;
canvas.connectionSegment.createHTML();
canvas.connectionSegment.paint();
};
};
/**
* @event dragEnd
* DragEnd callback fired when the port stops being dragged.
* It does the following:
*
* 1. Repaints the port
* 2. Moves otherPort towards the center of the shape
* 3. Moves port towards the center of the shape
* 4. Shows the handlers of the connection
*
* @param {PMUI.draw.Port} port
* @param {PMUI.draw.Port} otherPort
* @param {PMUI.draw.Canvas} canvas
*/
Port.prototype.onDragEnd = function (port, otherPort, canvas) {
return function (e, ui) {
if (canvas.connectionSegment) {
$(canvas.connectionSegment.getHTML()).remove();
}
port.repaint(port);
// move the ports towards the center of its parent
// (they were moved off the center in "onDragStart")
otherPort.moveTowardsTheCenter();
port.moveTowardsTheCenter();
// show the segmentMoveHandlers
port.connection.showMoveHandlers();
};
};
/**
* Determine the percentage relative to the shape where the port is located.
* The range of `this.percentage` is from 0 to 1 (inclusive).
* @return {boolean}
*/
Port.prototype.determinePercentage = function () {
//Shape and port dimension to consider, it can be either width or height
var shapeDimension,
portDimension;
if (!this.parent) {
return false;
}
if (this.direction === this.TOP || this.direction === this.BOTTOM) {
shapeDimension = this.parent.getZoomWidth();
portDimension = this.x + (5 * this.canvas.getZoomFactor());
} else {
shapeDimension = this.parent.getZoomHeight();
portDimension = this.y + (5 * this.canvas.getZoomFactor());
}
this.percentage = Math.round((portDimension / shapeDimension) * 100.0);
return true;
};
/**
* Shows this port (moving it's HTML representation towards the center for easy dragging).
* @chainable
*/
Port.prototype.show = function () {
this.visible = true;
this.paint();
this.html.style.zIndex = 3;
// move the ports towards the center
return this;
};
/**
* Hides this port (moving it's HTML representation off the center of the shape).
* @chainable
*/
Port.prototype.hide = function () {
this.visible = false;
this.paint();
return this;
};
/**
* Detaches the HTML of the port from the DOM (saving it in `this.html`), it also removes the port
* from its parent.
* @chainable
*/
Port.prototype.saveAndDestroy = function () {
this.parent.removePort(this); //remove from shape
// save the html but detach it from the DOM
this.html = $(this.html).detach()[0];
return this;
};
/**
* Attaches event listeners to this port, currently it has the draggable and mouse over events.
* @param {PMUI.draw.Port} currPort
* @return {PMUI.draw.Port}
*/
Port.prototype.attachListeners = function (currPort) {
var otherPort,
portDragOptions;
otherPort = currPort.connection.srcPort.getPoint(false)
.equals(currPort.getPoint(false)) ? currPort.connection.destPort :
currPort.connection.srcPort;
portDragOptions = {
//containment : "parent"
start: currPort.onDragStart(currPort, otherPort),
drag: currPort.onDrag(currPort, currPort.getPoint(false),
otherPort, currPort.parent.canvas),
stop: currPort.onDragEnd(currPort, otherPort, currPort.parent.canvas)
};
$(currPort.html).draggable(portDragOptions);
$(currPort.html).mouseover(
function () {
$(currPort.html).css('cursor', 'Move');
}
);
return currPort;
};
/**
* Moves a port towards or off the center (for easy dragging).
* @param {boolean} reverse If it's set to true then it will move it off the center
* @chainable
*/
Port.prototype.moveTowardsTheCenter = function (reverse) {
var towardsCenterDistance = Port.prototype.TOWARDS_CENTER,
dx = [0, -towardsCenterDistance, 0, towardsCenterDistance],
dy = [towardsCenterDistance, 0, -towardsCenterDistance, 0],
multiplier = 1;
if (reverse) {
multiplier = -1;
}
this.setPosition(this.x + dx[this.direction] * multiplier,
this.y + dy[this.direction] * multiplier);
return this;
};
/**
* Sets the Direction to the port.
* @param {number} newDirection
* @chainable
*/
Port.prototype.setDirection = function (newDirection) {
if (newDirection >= 0 && newDirection < 4) {
this.direction = newDirection;
} else {
throw new Error("setDirection(): parameter '" + newDirection +
"'is not valid");
}
return this;
};
/**
* Get the direction to the port. (0 = TOP, 1 = RIGHT, 2 = BOTTOM, 3 = LEFT)
* @returns {number}
*/
Port.prototype.getDirection = function () {
return this.direction;
};
/**
* Sets the parent of the port.
* @param {PMUI.draw.Shape} newParent
* @param {boolean} triggerChange If set to true it'll fire {@link PMUI.draw.Canvas#event-changeelement}
* @chainable
*/
Port.prototype.setParent = function (newParent, triggerChange) {
if (this.canvas && triggerChange) {
this.canvas.updatedElement = {
"id": this.id,
"type": this.type,
"fields": [
{
"field": "parent",
"oldVal": this.parent,
"newVal": newParent
}
]
};
$(this.canvas.html).trigger("changeelement");
}
this.parent = newParent;
return this;
};
/**
* Gets the parent of the port.
* @return {PMUI.draw.Port}
*/
Port.prototype.getParent = function () {
return this.parent;
};
/**
* Sets the old parent of this port
* @param {PMUI.draw.CustomShape} parent
* @chainable
*/
Port.prototype.setOldParent = function (parent) {
this.oldParent = parent;
return this;
};
/**
* Gets the old parent of this port.
* @return {PMUI.draw.Port}
*/
Port.prototype.getOldParent = function () {
return this.oldParent;
};
/**
* Sets the connection associated with this port.
* @param {PMUI.draw.Connection} newConn
* @chainable
*/
Port.prototype.setConnection = function (newConn) {
if (newConn && newConn.family === "Connection") {
this.connection = newConn;
} else {
throw new Error("setConnection(): parameter is not valid");
}
return this;
};
/**
* Gets the connection associated with this port
* @returns {PMUI.draw.Connection}
*/
Port.prototype.getConnection = function () {
return this.connection;
};
/**
* Returns the representation of the port (currently an instance of the class {@link PMUI.draw.Oval})
* @returns {PMUI.draw.Oval}
*/
Port.prototype.getRepresentation = function () {
return this.representation;
};
/**
* Sets the representation of this port (not supported yet)
* @param {PMUI.draw.Shape} newRep
* @chainable
*/
Port.prototype.setRepresentation = function (newRep) {
if (newRep instanceof PMUI.draw.RegularShape) {
this.representation = newRep;
} else {
throw new Error("setRepresentation(): parameter must be an instance" +
" of any regularShape");
}
return this;
};
/**
* Gets the ports position (if `relativeToShape` is set to true it'll return the position
* respect to the shape, otherwise it'll return its position respect to the canvas)
* @param {boolean} relativeToShape
* @returns {PMUI.util.Point}
*/
Port.prototype.getPoint = function (relativeToShape) {
var border = parseInt(this.parent.style.getProperty('border'), 10) || 0;
if (relativeToShape) {
return new PMUI.util.Point(this.getX() + Math.round(this.getWidth() / 2),
this.getY() + Math.round(this.getHeight() / 2));
}
return new PMUI.util.Point(
this.getAbsoluteX() + Math.round(this.getWidth() / 2),
this.getAbsoluteY() + Math.round(this.getHeight() / 2)
);
};
/**
* Gets the percentage of this port relative to its parent.
* @return {number}
*/
Port.prototype.getPercentage = function () {
return this.percentage;
};
/**
* Serializes this port.
* @return {Object}
* @return {number} return.x
* @return {number} return.y
* @return {number} return.realX
* @return {number} return.realY
* @return {string} return.parent The ID of its parent.
*/
Port.prototype.stringify = function () {
var inheritedJSON = {},
thisJSON = {
x: this.getX(),
y: this.getY(),
realX: this.realX,
realY: this.realY,
parent: this.getParent().getID()
};
$.extend(true, inheritedJSON, thisJSON);
return inheritedJSON;
};
PMUI.extendNamespace('PMUI.draw.Port', Port);
if (typeof exports !== 'undefined') {
module.exports = Port;
}
}());
(function () {
/**
* @class PMUI.draw.Segment
* A class that represents a segment, a segment is defined with two points
(`startPoint` and `enPoint`).
* In the PMDraw library a segment is used as a the key part of connections,
it has also the following characteristics:
*
* - Since a segment is used as part of a connection, it has neighbors (`previousNeighbor` and `nextNeighbor`).
* - A segment is parallel to an axis if it forms part of a connection.
* - A segment has a move handler to move the segment.
* - A segment has info of other connections it has intersections with
*
* Some examples of usage:
*
* // let's assume that we want to connect two shapes, the shapes are connected
* // through the creation of a segment (the start point is the mouse position where
* // the user fired the mouse down event and the end point is the mouse position where the user
* // fired the mouse up event)
* // let's assume that canvas is an instance of the class Canvas and it's creating the segment
* var redSegment = new PMUI.draw.Segment({
* startPoint: new PMUI.util.Point(100, 100), // a random point
* endPoint: new PMUI.util.Point(200, 200), // a random point
* parent: canvas,
* color: new PMUI.util.Color(255, 0, 0) // red !!
* });
*
* @extend PMUI.draw.Core
*
* @constructor Creates an instance of the class Segment
* @param {Object} options Initialization options
* @cfg {PMUI.util.Point} [startPoint=new Point(0, 0)] The start point of the segment
* @cfg {PMUI.util.Point} [endPoint=new Point(0, 0)] The end point of the segment
* @cfg {PMUI.draw.Canvas / PMUI.draw.Connection} [parent=null] The parent of the segment
* @cfg {PMUI.util.Color} [color=new Color(0, 0, 0)] The color of this segment
*/
var Segment = function (options) {
Segment.superclass.call(this, options);
/**
* The parent of the segment.
* @property {PMUI.draw.Canvas / PMUI.draw.Connection} [parent=null]
*/
this.parent = null;
/**
* The start point of the segment.
* @property {PMUI.util.Point} [startPoint=null]
*/
this.startPoint = null;
/**
* The end point of the segment.
* @property {PMUI.util.Point} [endPoint=null]
*/
this.endPoint = null;
/**
* zOrder of the segment.
* @property {number} [zOrder=Shape.prototype.MAX_ZINDEX]
*/
this.zOrder = PMUI.draw.Shape.prototype.MAX_ZINDEX;
/**
* The segment to the left of this segment.
* @property {PMUI.draw.Segment} [previousNeighbor=null]
*/
this.previousNeighbor = null;
/**
* The segment to the right of this segment.
* @property {PMUI.draw.Segment} [nextNeighbor=null]
*/
this.nextNeighbor = null;
/**
* Orientation of the segment, the possible values are:
*
* - Vertical
* - Horizontal
*
* @property {String} [orientation=""]
*/
this.orientation = "";
/**
* The width of the segment.
* @property {number} [width=1]
*/
this.width = 1;
/**
* Graphics object
* @property {PMUI.draw.Graphics} [graphics=null]
*/
this.graphics = null;
/**
* This segment style, the possible values are:
*
* - "dotted"
* - "segmented"
* - "segmentdot"
* @property {string} [segmentStyle=null]
*/
this.segmentStyle = null;
/**
* This segment color.
* @property {PMUI.util.Color} [segmentColor=null]
*/
this.segmentColor = null;
/**
* The move handler is the segment move handler of this segment.
* @property {PMUI.draw.SegmentMoveHandler} [moveHandler=null]
*/
this.moveHandler = null;
/**
* Creates an ArrayList of the intersections with other connections.
*
* // the structure is like:
* //intersections = [
* // {
* // center: point of intersection,
* // IdOtherConnection: id of the other connection
* // }
* //]
* @property {PMUI.util.ArrayList} [intersections=new PMUI.ArrayList()]
*/
this.intersections = new PMUI.util.ArrayList();
/**
* True if this segment has a move handler.
* @property {boolean} [hasMoveHandler=false]
*/
this.hasMoveHandler = false;
// set defaults
Segment.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', Segment);
/**
* The type of each instance of this class
* @property {String}
*/
Segment.prototype.type = "Segment";
/**
* Instance initializer which uses options to extend the config options to initialize the instance.
* @param {Object} options The object that contains the config
* @private
*/
Segment.prototype.init = function (options) {
/**
* Default options for the constructor
* @property {Object}
*/
var defaults = {
startPoint: new PMUI.util.Point(0, 0),
endPoint: new PMUI.util.Point(0, 0),
parent: null,
color: new PMUI.util.Color(0, 0, 0),
zOrder: 10
};
// extend recursively the defaultOptions with the given options
$.extend(true, defaults, options);
// init
this.setStartPoint(defaults.startPoint)
.setEndPoint(defaults.endPoint)
.setColor(defaults.color)
.setParent(defaults.parent)
.setZOrder(defaults.zOrder);
};
/**
* Creates the HTML Representation of the Segment.
* @returns {HTMLElement}
*/
Segment.prototype.createHTML = function () {
this.html = document.createElement('div');
this.html.id = this.id;
this.html.style.position = "absolute";
this.html.style.left = "0px";
this.html.style.top = "0px";
this.html.style.height = "0px";
this.html.style.width = "0px";
this.html.style.zIndex = this.zOrder;
return this.html;
};
/**
* Paints a segment by creating an instance of the class {@link PMUI.draw.Graphics} and
* calling {@link PMUI.draw.Graphics#drawLine}, it also append it's HTML to its parent's HTML.
* @chainable
*/
Segment.prototype.paint = function () {
if (this.getHTML() === null) {
return this;
}
if (this.graphics === null) {
this.graphics = new PMUI.draw.Graphics(this.html);
}
//dibujas linea llamar a drawLine de la clase graphics con los puntos
this.graphics.drawLine(this.startPoint.x, this.startPoint.y,
this.endPoint.x, this.endPoint.y, this.segmentStyle, this.segmentColor);
this.parent.html.appendChild(this.html);
return this;
};
/**
* Removes its HTML from the DOM.
* @chainable
*/
Segment.prototype.destroy = function () {
$(this.html).remove();
return this;
};
/**
* Paint this segment with the intersections it has stored (this method is called from
* {@link PMUI.draw.Connection#checkAndCreateIntersectionsWithAll}), it also append it's HTML to its parent's HTML.
* @chainable
*/
Segment.prototype.paintWithIntersections = function () {
// we have to paint the segment again so destroy the previous one
this.destroy();
var startPoint,
endPoint,
diff,
i,
reverse = false;
if (this.getHTML() === null) {
return this;
}
if (this.graphics === null) {
this.graphics = new PMUI.draw.Graphics(this.html);
}
//console.log(this.hasMoveHandler);
if (this.hasMoveHandler) {
$(this.moveHandler.html).remove();
this.addSegmentMoveHandler();
}
// default differentials to split the segment
if (this.orientation === this.HORIZONTAL) {
diff = new PMUI.util.Point(PMUI.draw.Shape.prototype.DEFAULT_RADIUS, 0);
if (this.startPoint.x > this.endPoint.x) {
reverse = true;
}
// for this to work we need to sort the intersections
this.intersections.sort(function (i, j) {
return i.center.x - j.center.x;
});
} else {
diff = new PMUI.util.Point(0, PMUI.draw.Shape.prototype.DEFAULT_RADIUS);
if (this.startPoint.y > this.endPoint.y) {
reverse = true;
}
// for this to work we need to sort the intersections
this.intersections.sort(function (i, j) {
return i.center.y - j.center.y;
});
}
this.graphics.graphics.clear();
startPoint = this.startPoint.clone();
for (i = 0; i < this.intersections.getSize(); i += 1) {
// if the direction is reverse then we get the
// inverse position for i in the array
if (reverse) {
endPoint = this.intersections
.get(this.intersections.getSize() - i - 1).center;
} else {
endPoint = this.intersections.get(i).center;
}
if (reverse) {
endPoint = endPoint.add(diff);
} else {
endPoint = endPoint.subtract(diff);
}
this.graphics.drawLine(startPoint.x, startPoint.y,
endPoint.x, endPoint.y, this.segmentStyle,
this.segmentColor, 0, 0, true);
if (reverse) {
startPoint = endPoint.subtract(diff.multiply(2));
} else {
startPoint = endPoint.add(diff.multiply(2));
}
}
// draw last segment
endPoint = this.endPoint.clone();
this.graphics.drawLine(startPoint.x, startPoint.y,
endPoint.x, endPoint.y, this.segmentStyle, this.segmentColor,
0, 0, true);
this.parent.html.appendChild(this.html);
return this;
};
/**
* Adds a segmentMoveHandler to this segment, it also append the segmentMoveHandler instance HTML to this HTML
* @chainable
*/
Segment.prototype.addSegmentMoveHandler = function () {
var midX = (this.startPoint.x + this.endPoint.x) / 2,
midY = (this.startPoint.y + this.endPoint.y) / 2;
this.moveHandler = new PMUI.draw.SegmentMoveHandler({
parent: this,
orientation: this.orientation,
style: {
cssProperties: {
border: "1px solid black"
}
}
});
midX -= this.moveHandler.width / 2;
midY -= this.moveHandler.height / 2;
this.moveHandler.setPosition(midX, midY);
this.html.appendChild(this.moveHandler.getHTML());
this.moveHandler.paint();
this.moveHandler.attachListeners(this.moveHandler);
return this;
};
/**
* Returns the parent of the segment
* @returns {PMUI.draw.Canvas / PMUI.draw.Connection}
*/
Segment.prototype.getParent = function () {
return this.parent;
};
/**
* Returns the start point of the segment.
* @returns {PMUI.util.Point}
*/
Segment.prototype.getStartPoint = function () {
return this.startPoint;
};
/**
* Returns the end point of the segment.
* @returns {PMUI.util.Point}
*/
Segment.prototype.getEndPoint = function () {
return this.endPoint;
};
/**
* Sets the parent of the segment.
* @param {Object} newParent
* @chainable
*/
Segment.prototype.setParent = function (newParent) {
this.parent = newParent;
return this;
};
/**
* Sets the start point of the segment.
* @param {PMUI.util.Point} newPoint
* @chainable
*/
Segment.prototype.setStartPoint = function (newPoint) {
this.startPoint = newPoint;
return this;
};
/**
* Sets the end point of the segment.
* @param {PMUI.util.Point} newPoint
* @chainable
*/
Segment.prototype.setEndPoint = function (newPoint) {
this.endPoint = newPoint;
return this;
};
/**
* Sets the segmentStyle of this segment
* @param {string} newStyle
* @chainable
*
*/
Segment.prototype.setStyle = function (newStyle) {
this.segmentStyle = newStyle;
return this;
};
/**
* Sets the color of this segment
* @param {PMUI.util.Color} newColor
* @chainable
*/
Segment.prototype.setColor = function (newColor) {
this.segmentColor = newColor;
return this;
};
/**
* Creates an intersection with `otherSegment` and saves it in `this.intersections`.
* If it doesn't have an intersection point passed as a parameter it will determine the
* intersection point and add it to `this.intersections` (`this.intersections` considers only unique points)
* @param {PMUI.draw.Segment} otherSegment
* @param {PMUI.util.Point} [ip] Intersection Point
* @chainable
*/
Segment.prototype.createIntersectionWith = function (otherSegment, ip) {
var intersectionObject,
intersectionPoint,
i,
goodToInsert = true;
if (ip) {
intersectionPoint = ip;
} else {
intersectionPoint = PMUI.draw.Geometry.segmentIntersectionPoint(this.startPoint,
this.endPoint, otherSegment.startPoint, otherSegment.endPoint);
}
// let's consider the case when an intersection point is the same e.g. when a segment crosses two
// other segments in the same point
for (i = 0; i < this.intersections.getSize(); i += 1) {
if (ip.equals(this.intersections.get(i).center)) {
goodToInsert = false;
}
}
if (goodToInsert) {
intersectionObject = new PMUI.draw.Intersection(intersectionPoint,
otherSegment.parent.getID(), this);
this.html.appendChild(intersectionObject.getHTML());
intersectionObject.paint();
this.intersections.insert(intersectionObject);
}
//console.log(intersectionObject);
//console.log(this.intersections);
return this;
};
/**
* Clear all the intersections in this segment.
* @chainable
*/
Segment.prototype.clearIntersections = function () {
var i,
intersection,
size = this.intersections.getSize();
while (size > 0) {
intersection = this.intersections.get(size - 1);
$(intersection.html).remove();
this.intersections.popLast();
size -= 1;
}
return this;
};
/**
* Moves the segment either to x or y but not both
(this method is called from {@link PMUI.draw.SegmentMoveHandler#event-drag}).
* @param {number} x new x coordinate of the segment in the canvas
* @param {number} y new y coordinate of the segment in the canvas
*/
Segment.prototype.moveSegment = function (x, y) {
var handler = this.moveHandler,
prevNeighbor = this.previousNeighbor,
nextNeighbor = this.nextNeighbor,
midX,
midY;
if (handler.orientation === handler.VERTICAL) {
this.startPoint.x = x
+ handler.width / 2;
this.endPoint.x = x
+ handler.width / 2;
prevNeighbor.endPoint.x =
this.startPoint.x;
nextNeighbor.startPoint.x =
this.endPoint.x;
} else {
this.startPoint.y = y
+ handler.height / 2;
this.endPoint.y = y
+ handler.height / 2;
prevNeighbor.endPoint.y =
this.startPoint.y;
nextNeighbor.startPoint.y =
this.endPoint.y;
}
// fix handler for the this segment
if (this.moveHandler) { // of course yes!
midX = (this.startPoint.x + this.endPoint.x) / 2
- this.moveHandler.width / 2;
midY = (this.startPoint.y + this.endPoint.y) / 2
- this.moveHandler.height / 2;
this.moveHandler.setPosition(midX, midY);
}
// paint the previous segment
prevNeighbor.paint();
// fix handler for the prev segment if possible
if (prevNeighbor.moveHandler) {
midX = (prevNeighbor.startPoint.x + prevNeighbor.endPoint.x) / 2
- prevNeighbor.moveHandler.width / 2;
midY = (prevNeighbor.startPoint.y + prevNeighbor.endPoint.y) / 2
- prevNeighbor.moveHandler.height / 2;
prevNeighbor.moveHandler.setPosition(midX, midY);
}
// paint the next segment
nextNeighbor.paint();
// fix moveHandler for the next segment if possible
if (nextNeighbor.moveHandler) {
midX = (nextNeighbor.startPoint.x + nextNeighbor.endPoint.x) / 2
- nextNeighbor.moveHandler.width / 2;
midY = (nextNeighbor.startPoint.y + nextNeighbor.endPoint.y) / 2
- nextNeighbor.moveHandler.height / 2;
nextNeighbor.moveHandler.setPosition(midX, midY);
}
this.paint();
return this;
};
Segment.prototype.hit = function (coronaWidth, point) {
var dotprod, projlenSq;
// Adjust vectors relative to X1,Y1
// X2,Y2 becomes relative vector from X1,Y1 to end of segment
X1 = this.startPoint.x;
Y1 = this.startPoint.y;
X2 = this.endPoint.x;
Y2 = this.endPoint.y;
px = point.x;
py = point.y
X2 -= X1;
Y2 -= Y1;
px -= X1;
py -= Y1;
dotprod = px * X2 + py * Y2;
if (dotprod <= 0.0) {
projlenSq = 0.0;
} else {
px = X2 - px;
py = Y2 - py;
dotprod = px * X2 + py * Y2;
if (dotprod <= 0.0) {
projlenSq = 0.0;
} else {
projlenSq = dotprod * dotprod / (X2 * X2 + Y2 * Y2);
}
}
var lenSq = px * px + py * py - projlenSq;
if (lenSq < 0) {
lenSq = 0;
}
return Math.sqrt(lenSq) < coronaWidth;
};
PMUI.extendNamespace('PMUI.draw.Segment', Segment);
if (typeof exports !== 'undefined') {
module.exports = Segment;
}
}());
(function () {
/**
* @class PMUI.draw.ReadOnlyLayer
* Layer used to give the canvas a readonly state so that the user can just look
* at the diagram and not be able to perform any modification,
* the canvas is in charge of instantiating this object when its property
* readOnly is set to true, there is no need to instance this object
* independently
* @extends PMUI.draw.Core
*
* @constructor
* Creates an instance of this class
* @param {Object} options configuration options inherited from Core
*/
var ReadOnlyLayer = function (options) {
ReadOnlyLayer.superclass.call(this, options);
ReadOnlyLayer.prototype.initObject.call(this, options);
};
PMUI.inheritFrom('PMUI.draw.Core', ReadOnlyLayer);
/**
* Creates the HTML and attach the event listeners
* @param options
*/
ReadOnlyLayer.prototype.initObject = function (options) {
this.createHTML();
this.attachListeners();
};
/**
* Attach the event listeners necessary for blocking interactions
*/
ReadOnlyLayer.prototype.attachListeners = function () {
var $layer = $(this.html);
$layer.on('mousedown', this.onMouseDown(this))
.on('mouseup', this.onMouseUp(this))
.on('mousemove', this.onMouseMove(this))
.on('click', this.onClick(this))
.droppable({
accept: "*",
greedy: true,
onDrop: function () {
return false;
}
});
};
/**
* Stops the propagation of the mousedown event
* @param {PMUI.draw.ReadOnlyLayer} layer
* @return {Function}
*/
ReadOnlyLayer.prototype.onMouseDown = function (layer) {
return function (e, ui) {
e.stopPropagation();
};
};
/**
* Stops the propagation of the mouseup event
* @param {PMUI.draw.ReadOnlyLayer} layer
* @return {Function}
*/
ReadOnlyLayer.prototype.onMouseUp = function (layer) {
return function (e, ui) {
e.stopPropagation();
};
};
/**
* Stops the propagation of the click event
* @param {PMUI.draw.ReadOnlyLayer}layer
* @return {Function}
*/
ReadOnlyLayer.prototype.onClick = function (layer) {
return function (e, ui) {
e.stopPropagation();
};
};
/**
* Stops the propagation of the mousemove event
* @param {PMUI.draw.ReadOnlyLayer} layer
* @return {Function}
*/
ReadOnlyLayer.prototype.onMouseMove = function (layer) {
return function (e, ui) {
e.stopPropagation();
};
};
PMUI.extendNamespace('PMUI.draw.ReadOnlyLayer', ReadOnlyLayer);
if (typeof exports !== 'undefined') {
module.exports = ReadOnlyLayer;
}
}());
(function () {
var DataItem = function (key, value) {
this.id = PMUI.generateUniqueId();
this.val = value;
this.key = key;
};
DataItem.prototype.type = 'DataItem';
DataItem.prototype.family = 'DataItem';
DataItem.prototype.getKey = function () {
return this.key;
};
DataItem.prototype.getValue = function () {
return this.val;
};
DataItem.prototype.setKey = function (key) {
this.key = key;
return this;
};
DataItem.prototype.setValue = function (value) {
this.val = value;
return this;
};
DataItem.prototype.set = function (key, value) {
this.key = key;
this.val = value;
return this;
};
DataItem.prototype.get = function () {
var aux = {};
aux[this.key] = this.val;
return aux;
};
DataItem.prototype.getRecord = function () {
return {
key: this.key,
value: this.val
};
};
DataItem.prototype.clear = function () {
this.val = null;
this.key = null;
return this;
};
PMUI.extendNamespace('PMUI.data.DataItem', DataItem);
if (typeof exports !== 'undefined') {
module.exports = DataItem;
}
}());
(function () {
var DataField = function (options) {
options = options || {};
DataField.superclass.call(this, options.key, options.value);
this.typeField = null;
this.custom = null;
this.customKeys = {};
DataField.prototype.init.call(this, options);
};
PMUI.inheritFrom('PMUI.data.DataItem', DataField);
DataField.prototype.type = "DataField";
DataField.prototype.dataTypes = {
"string": "native",
"number": "native",
"date": "native",
"boolean": "native",
"object": "native"
};
DataField.prototype.basicKeys = {
"key": "native",
"value": "native",
"type": "class",
"val": "private",
"custom": "native",
"customKeys": "native",
"typeField": "native",
"family": "class"
};
DataField.prototype.defaultType = "string";
DataField.prototype.init = function (options) {
var that = this;
if (options) {
this.setKey(options.key || null);
this.setValue(options.value || null);
this.setType(options.type || null);
}
jQuery.each(options, function (k, v) {
if (!that.basicKeys[k]) {
if (typeof v !== 'object' && typeof v !== 'function') {
that[k] = v;
that.customKeys[k] = 'custom';
}
}
});
};
DataField.prototype.setType = function (type) {
if (type) {
if (this.dataTypes[type]) {
this.typeField = type;
this.custom = false;
} else {
this.typeField = type;
this.custom = true;
}
} else {
this.custom = false;
this.typeField = this.defaultType;
}
return this;
};
DataField.prototype.addAttribute = function (key, value) {
this.customKeys[key] = value;
return this;
};
DataField.prototype.getAttribute = function (key) {
if (key === 'val' || key === 'key') {
return this[key];
}
return this.customKeys[key];
};
DataField.prototype.existsKey = function (key) {
if (key === "val" || key === "key") {
return true;
}
return this.customKeys.hasOwnProperty(key);
};
DataField.prototype.getRecord = function (format) {
var castType = format || this.typeField,
out = {
key: this.key,
type: this.typeField,
value: PMUI.castValue(this.val, castType)
};
jQuery.each(this.customKeys, function (key, value) {
out[key] = value;
});
return out;
};
DataField.prototype.clear = function () {
var key;
DataField.superclass.prototype.clear.call(this);
for (key in this.customKeys) {
if (this.customKeys.hasOwnProperty(key)) {
delete this.customKeys[key];
}
}
return this;
};
PMUI.extendNamespace('PMUI.data.DataField', DataField);
if (typeof exports !== 'undefined') {
module.exports = DataField;
}
}());
(function () {
var DataSet = function (options) {
this.items = null;
this.factory = null;
DataSet.prototype.init.call(this, options);
};
DataSet.prototype.type = "DataSet";
DataSet.prototype.family = "DataSet";
DataSet.prototype.init = function (options) {
var defaults = {
factory: {
products: {
"datafield": PMUI.data.DataField,
"dataitem": PMUI.data.DataItem
},
defaultProduct: "dataitem"
},
items: []
};
jQuery.extend(true, defaults, options);
this.items = new PMUI.util.ArrayList();
this.factory = new PMUI.util.Factory(defaults.factory);
this.setItems(defaults.items);
};
DataSet.prototype.setItems = function (items) {
var that = this;
jQuery.each(items, function (k, item) {
that.items.insert(that.factory.make(item));
});
return this;
};
DataSet.prototype.addItem = function (item) {
if (item) {
this.items.insert(this.factory.make(item));
}
return this;
};
DataSet.prototype.getData = function () {
var items,
that = this,
out = [];
items = this.items.asArray();
jQuery.each(items, function (k, item) {
out.push(item.getRecord());
});
return out;
};
DataSet.prototype.getJSON = function () {
return JSON.stringify(this.getData());
};
DataSet.prototype.getXML = function () {
return PMUI.json2xml(this.getJSON());
};
DataSet.prototype.removeItem = function (item) {
this.items.remove(item);
};
DataSet.prototype.getValue = function (key) {
var item = this.items.find('key', key);
return item.getValue();
};
DataSet.prototype.setValue = function (key, value) {
var item = this.items.find('key', key);
item.setValue(value);
return this;
};
DataSet.prototype.keys = function () {
var items,
out = [];
items = this.items.asArray();
jQuery.each(items, function (k, item) {
out.push(item.getKey());
});
return out;
};
DataSet.prototype.values = function () {
var items,
out = [];
items = this.items.asArray();
jQuery.each(items, function (k, item) {
out.push(item.getValue());
});
return out;
};
PMUI.extendNamespace('PMUI.data.DataSet', DataSet);
if (typeof exports !== 'undefined') {
module.exports = DataSet;
}
}());
(function () {
/**
* @class PMUI.control.OptionsSelectorItemControl
* @extends PMUI.control.HTMLControl
*
* The class is the basic element for {@link PMUI.control.OptionsSelectorControl OptionsSelectorControl} class.
*
* For instance the class, the sentences below show how do it.
*
* @example
* new PMUI.control.OptionsSelectorItemControl({
* text: ....
* });
*
* @cfg {Boolean} selected Sets an item as selected inside of the {@link PMUI.control.OptionsSelectorControl component}
* @cfg {String} text Defines the label for the component, the text that appear inside of the component
*
*/
var OptionsSelectorItemControl = function (settings) {
OptionsSelectorItemControl.superclass.call(this, settings);
/**
* @property {Boolean} [selected=false]
* Sets an item as selected inside of the {@link PMUI.control.OptionsSelectorControl component}
*/
this.selected = null;
/**
* @property {String} [cls='pmui-switch-buttonitem']
* Sets the class name for the component. It is related to the styles for the element
*/
this.cls = null;
/**
* @property {String} [iconCls]
* Sets the class name for the icon of the component.
*/
this.iconCls = null;
/**
* @property {String} [clsSelected= 'pmui-switch-buttonitem-selected']
* Sets the class name when an item is selected
*/
this.clsSelected = null;
/**
* @property {Object} [dom=null]
* Represents the DOM of the component
*/
this.dom = null;
/**
* @property {Object} [disabled=false]
* Defines whether the item is disabled or enabled
*/
this.disabled = null;
/**
* @property {String} [text=null]
* Defines the label for the component, the text that appear inside of the component
*/
this.text = null;
OptionsSelectorItemControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.control.HTMLControl", OptionsSelectorItemControl);
/**
*
*
*/
OptionsSelectorItemControl.prototype.type = "button";
/**
*
*
*/
OptionsSelectorItemControl.prototype.family = "Item";
OptionsSelectorItemControl.prototype.init = function (settings) {
var defaults = {
selected: false,
disabled: false,
cls: 'pmui-switch-buttonitem',
iconCls: 'pmui-switch-buttonitem-icon',
clsSelected: 'pmui-switch-buttonitem-selected',
items: [],
text: '',
value: ''
};
jQuery.extend(true, defaults, settings);
this.setSelected(defaults.selected)
.setCls(defaults.cls)
.setClsSelected(defaults.clsSelected)
.setIconClass(defaults.iconCls)
.setText(defaults.text)
.setDisable(defaults.disabled)
.setValue(defaults.value);
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.setSelected = function (status) {
var otherElements,
i,
that = this;
this.selected = (typeof status === 'boolean') ? status : false;
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.setCls = function (className) {
this.cls = (typeof className === 'string') ? className : '';
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.setClsSelected = function (className) {
this.clsSelected = (typeof className === 'string') ? className : '';
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.setIconClass = function (className) {
this.iconCls = (typeof className === 'string') ? className : '';
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.setText = function (text) {
this.text = (typeof text === 'string') ? text : '';
return this;
};
OptionsSelectorItemControl.prototype.setValue = function (value) {
value = value.toString();
if (typeof value === 'string') {
if (value.length === 0) {
this.value = this.text;
} else {
this.value = value;
}
}
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.activeElement = function (that) {
var i;
if (typeof that === "undefined") {
that = this;
}
if (!that.disabled) {
if (!that.parent.itemsSelected.contains(that)) {
that.parent.itemsSelected.insert(that);
}
that.setSelected(true);
if (this.dom) {
that.dom.container.className = that.clsSelected;
for (i = 0; i < that.style.getClasses().length; i += 1) {
that.dom.container.className += " " + that.style.getClasses()[i];
}
}
}
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.desactiveElement = function (that) {
var i;
if (typeof that === "undefined") {
that = this;
}
if (!that.disabled) {
that.parent.itemsSelected.remove(that);
that.setSelected(false);
if (this.dom) {
that.dom.container.className = that.cls;
for (i = 0; i < that.style.getClasses().length; i += 1) {
that.dom.container.className += " " + that.style.getClasses()[i];
}
}
}
return this;
};
/**
* Disables/enables the control
* @param {Boolean} setDisable If the value is evaluated as true then the control is disabled,
* otherwise the control is enabled.
* @chainable
*/
OptionsSelectorItemControl.prototype.setDisable = function (disable) {
if (typeof disable === 'boolean') {
this.disabled = disable;
if (this.html) {
if (disable) {
this.disable();
} else {
this.enable();
}
}
}
return this;
};
/**
* [disable description]
* @return {[type]} [description]
*/
OptionsSelectorItemControl.prototype.disable = function () {
if (arguments[0] === undefined) {
this.disabled = true;
if (this.html) {
jQuery(this.html).unbind("click");
this.html.setAttribute("class", "pmui-switch-buttonitem-disabled");
}
} else {
if (arguments[0]) {
this.disable();
} else {
this.enable();
}
}
return this;
};
/**
* [enable description]
* @return {[type]} [description]
*/
OptionsSelectorItemControl.prototype.enable = function () {
if (arguments[0] === undefined) {
this.disabled = false;
if (this.html) {
this.html.setAttribute("class", this.cls);
this.defineEvents();
}
} else {
if (arguments[0]) {
this.disable();
} else {
this.enable();
}
}
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.selectItem = function () {
var that = this,
otherElements;
if (this.selected) {
that.desactiveElement(that);
} else {
that.activeElement(that);
if (this.parent.multipleSelection === false) {
otherElements = this.parent.items.asArray();
for (i = 0; i < otherElements.length; i += 1) {
if (that.id !== otherElements[i].id) {
that.desactiveElement(otherElements[i]);
}
}
}
}
return this;
};
/**
*
*
*/
OptionsSelectorItemControl.prototype.createHTML = function () {
var li,
display,
button,
icon,
text,
i;
OptionsSelectorItemControl.superclass.prototype.setElementTag.call(this, "a");
OptionsSelectorItemControl.superclass.prototype.createHTML.call(this);
//button = PMUI.createHTMLElement("a");
//button.id = PMUI.generateUniqueId()
if (this.disabled) {
this.html.className = "pmui-switch-buttonitem-disabled";
} else {
this.html.className = this.cls;
for (i = 0; i < this.style.getClasses().length; i += 1) {
this.html.className += " " + this.style.getClasses()[i];
}
}
this.html.href = '#';
if (this.parent.orientation === 'vertical') {
this.html.setAttribute("style", "display: block;");
}
icon = PMUI.createHTMLElement("span");
icon.className = this.iconCls;
text = PMUI.createHTMLElement("span");
text.innerHTML = this.text;
this.html.appendChild(icon);
this.html.appendChild(text);
this.dom = {
container: this.html,
items: [
{
icon: icon
},
{
text: text
}
]
};
if (this.selected) {
this.activeElement(this);
}
this.html.id = this.id;
return this.html;
};
/**
* Defines events related to the object.
*/
OptionsSelectorItemControl.prototype.defineEvents = function () {
var j,
fnSelect,
children,
that = this;
this.removeEvents().eventsDefined = true;
if (!this.disabled) {
that.addEvent('click').listen(this.html, function (e) {
e.preventDefault();
that.selectItem();
fnSelect = that.parent.listeners.select;
fnSelect(that, e);
e.stopPropagation();
});
if (e.which === PMUI.keyCodeF5) {
this.blur();
e.preventDefault();
window.location.reload(true);
}
}
};
PMUI.extendNamespace("PMUI.control.OptionsSelectorItemControl", OptionsSelectorItemControl);
if (typeof exports !== "undefined") {
module.exports = OptionsSelectorItemControl;
}
}());
(function () {
/**
* @class PMUI.control.OptionsSelectorControl
* @extends PMUI.control.HTMLControl
* Class to handle the Select HTML form element.
*
* Example usage:
*
* @example
* OptionsSelectorControl = new PMUI.control.OptionsSelectorControl({
* items: [
* {
* text: "First option"
* },
* {
* text: "Second option",
* selected: true
* },
* {
* text: "Third option"
* }
* ],
* listeners: {
* select: function (item, event) {
* console.log("selected", item);
* }
* }
* });
* document.body.appendChild(OptionsSelectorControl.getHTML());
* OptionsSelectorControl.defineEvents();
*
* @cfg {Array} items Defines the data items as an array objects.
*
* items: [
* {
* ...
* },
* {
* text: "Item1",
* selected: true
* },
* {
* ...
* }
* ]
*
* @cfg {Boolean} multipleSelection Defines wether the component will support the behavior of the check group.
*
*
*/
var OptionsSelectorControl = function (settings) {
OptionsSelectorControl.superclass.call(this, settings);
/**
* @property {Boolean} [multipleSelection=false] If the property is enabled, the items can be
* selected like checkGroup otherwise is like to radioGroup
*/
this.multipleSelection = null;
/**
* @property {String} [orientation=null]
*
*/
this.orientation = null;
/**
* @property {Object} [container=null]
* @private
*/
this.container = null;
/**
* @property {Object} [listeners] A config object containing one or more handlers to be added
* to this object during initialization. By default the 'select' handler is enabled
* @readOnly
*/
this.listeners = {
/**
* @event select
* Fires when the component has been selected. Returning the current object and the event.
* @return {Object} {@link PMUI.control.OptionsSelectorItemControl OptionsSelectorItemControl}
* @return {Object} event
*/
select: function () {
}
};
/**
* @property {@link PMUI.util.ArrayList ArrayList}
*
*/
this.itemsSelected = null;
this.items = new PMUI.util.ArrayList();
OptionsSelectorControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', OptionsSelectorControl);
OptionsSelectorControl.prototype.type = "control";
OptionsSelectorControl.prototype.family = "control";
OptionsSelectorControl.prototype.init = function (settings) {
var defaults = {
multipleSelection: false,
orientation: 'horizontal',
items: [],
iconClass: '',
listeners: {
select: function () {
}
}
};
jQuery.extend(true, defaults, settings);
this.setMultipleSelection(defaults.multipleSelection)
.setOrientation(defaults.orientation)
.setChildren(defaults.items)
.setIconClass(defaults.iconClass)
.setListeners(defaults.listeners);
this.itemsSelected = new PMUI.util.ArrayList();
};
/**
* Sets the value whether the {@link PMUI.control.OptionsSelectorItemControl items} can be selected
* at the same time
* @param {Boolean} status
*/
OptionsSelectorControl.prototype.setMultipleSelection = function (status) {
this.multipleSelection = (typeof status === 'boolean') ? status : false;
return this;
};
/**
* Sets the orientation for every {@link PMUI.control.OptionsSelectorItemControl element}
* There are two possibilities:
* - horizontal, the elements will appear horizontally
* - vertical, the elements will appear vertically
* @param {String} orientation
*/
OptionsSelectorControl.prototype.setOrientation = function (orientation) {
var i,
items,
options = {
horizontal: true,
vertical: true
},
layout;
this.orientation = (options[orientation]) ? orientation : 'horizontal';
if (this.html) {
items = this.items.asArray();
for (i = 0; i < items.length; i += 1) {
if (this.orientation === "vertical") {
items[i].html.setAttribute("style", "display:block;");
this.html.setAttribute("style", "display:inline-table;");
} else {
items[i].html.removeAttribute("style");
this.html.removeAttribute("style");
}
}
}
return this;
};
/**
* Sets the {@link PMUI.control.OptionsSelectorItemControl elements} child inside of
* {@link PMUI.control.OptionsSelectorControl container}
* @param {Array} items Represents to children
*/
OptionsSelectorControl.prototype.setChildren = function (items) {
var i;
if (items instanceof Array) {
this.items.clear();
for (i = 0; i < items.length; i += 1) {
this.addItem(items[i]);
}
}
return this;
};
/**
* SetValue method
* @param {String} values Defines the current values of the items
*/
OptionsSelectorControl.prototype.setValue = function (values) {
var options,
items,
i,
separator;
if (values) {
if (values.length > 0) {
//options = JSON.parse(values);
separator = this.field.separator;
options = values.split(separator);
items = this.items.asArray();
for (i = 0; i < items.length; i += 1) {
if (jQuery.inArray(items[i].getValue(), options) !== -1) {
items[i].selected = false;
items[i].selectItem();
}
}
if (this.multipleSelection === false && options.length > 1) {
throw new Error("Is not possible sets more than one when the multipleSelection property is false.")
}
}
}
return this;
};
/**
* Sets the class name for the icon that appears in the header
* @param {String} class name
*/
OptionsSelectorControl.prototype.setIconClass = function (className) {
this.iconClass = (typeof className === 'string') ? className : '';
return this;
};
/**
* Gets items selected
* @return {Object} this
*/
OptionsSelectorControl.prototype.getItemsSelected = function () {
return this.itemsSelected.asArray()
};
/**
* Sets listeners to object
* @param {Object} listeners
*/
OptionsSelectorControl.prototype.setListeners = function (listeners) {
if (typeof listeners === 'object') {
this.listeners = listeners;
}
return this;
};
/**
* Adds new items to the component
* @param {Object|{@link PMUI.control.OptionsSelectorItemControl OptionsSelectorItemControl}} item Represents the item
* for the parent container.
* @return {PMUI.control.OptionsSelectorControl} this
*/
OptionsSelectorControl.prototype.addItem = function (item) {
var factory,
itemToBeAdded;
factory = new PMUI.behavior.BehaviorFactory({
products: {
"optionsSelectorItem": PMUI.control.OptionsSelectorItemControl
},
defaultProduct: "optionsSelectorItem"
});
if (typeof item === "object") {
itemToBeAdded = factory.make(item);
}
itemToBeAdded.parent = this;
this.items.insert(itemToBeAdded);
if (this.html) {
this.html.appendChild(itemToBeAdded.getHTML());
itemToBeAdded.defineEvents();
}
return this;
};
/**
* Removes and item
* @param {[type]} item [description]
* @return {[type]} [description]
*/
OptionsSelectorControl.prototype.removeOption = function (position) {
var items = this.items.asArray(),
item;
if (items[position]) {
item = items[position];
this.items.remove(item);
this.html.removeChild(item.html);
}
return item;
};
/**
* [disableOption description]
* @param {[type]} position [description]
* @param {[type]} disabled [description]
* @return {[type]} [description]
*/
OptionsSelectorControl.prototype.disableOption = function (position, disable) {
var items = this.items.asArray(),
item;
if (items[position]) {
item = items[position];
item.setDisable(disable)
}
return item;
};
OptionsSelectorControl.prototype.getOptions = function () {
return this.items.asArray();
};
OptionsSelectorControl.prototype.clearOptions = function () {
var items = this.items.asArray(),
i;
for (i = 0; i < items.length; i += 1) {
this.html.removeChild(items[i].html);
}
this.items.clear();
return this;
};
/**
* Creates the HTML elements
* @return {HTMLElement} Returns the HTMLElement of the current component
*/
OptionsSelectorControl.prototype.createHTML = function () {
var container,
items,
i;
container = PMUI.createHTMLElement("span");
container.className = "pmui-field-control-container";
if (this.orientation === "vertical") {
container.setAttribute("style", "display:inline-table;");
}
items = this.items.asArray();
if (typeof items.length === 'number') {
for (i = 0; i < items.length; i += 1) {
container.appendChild(items[i].getHTML());
}
}
container.id = this.id;
this.html = container;
return this.html;
};
/**
* Defines and active all the events related to the object
*/
OptionsSelectorControl.prototype.defineEvents = function () {
var that = this,
items,
i;
this.eventsDefined = true;
if (that.items.getSize() > 0) {
items = that.items.asArray();
for (i = 0; i < items.length; i += 1) {
items[i].defineEvents();
}
}
return this;
};
PMUI.extendNamespace("PMUI.control.OptionsSelectorControl", OptionsSelectorControl);
if (typeof exports !== "undefined") {
module.exports = OptionsSelectorControl;
}
}());
(function () {
/**
* @class PMUI.field.OptionsSelectorField
* @extends PMUI.form.Field
*
* How use the class.
* The component represents basically two types of known controls, those are the radio group and check group fields.
* By default the component has the functionality to the radio group field with a better look and feel.
*
* The example below shows the parameters (config options) for build an field of options selector (radio group).
*
* @example
* OptionsSelectorControl = new PMUI.field.OptionsSelectorField({
* label: "radiogroup",
* items: [
* {
* text: "First option"
* },
* {
* text: "Second option",
* selected: true
* },
* {
* text: "Third option"
* }
* ],
* listeners: {
* select: function (item, event) {
* console.log("selected", item);
* }
* }
* });
* document.body.appendChild(OptionsSelectorControl.getHTML());
* OptionsSelectorControl.defineEvents();
*
*
* The otherwise is posible create a check group field applying the following sentence on the JSON config:
*
* { ...
* multipleSelection: true
* ...
* }
*
* With the above parameter the component will have the behavior of the check group field.
*
* @example
* checkboxGroup = new PMUI.field.OptionsSelectorField({
* label: "radiogroup",
* multipleSelection: true,
* items: [
* {
* text: "First option"
* },
* {
* text: "Second option",
* selected: true
* },
* {
* text: "Third option"
* }
* ],
* listeners: {
* select: function (item, event) {
* console.log("selected", item);
* }
* }
* });
* document.body.appendChild(checkboxGroup.getHTML());
* checkboxGroup.defineEvents();
*
*
* @cfg {Array} items Defines the data items as an array objects.
*
* items: [
* {
* ...
* },
* {
* text: "Item1",
* selected: true
* },
* {
* ...
* }
* ]
*
* @cfg {Boolean} [multipleSelection=false] Defines wether the component will support the behavior of the check group.
* @cfg {String} [orientation="horizontal"] Defines the orientation of the component, it means if the orientation is vertical or
* horizontal
*
*
*/
var OptionsSelectorField = function (settings) {
OptionsSelectorField.superclass.call(this, settings);
/**
* @property {Boolean} [disabled=false] If the field is disabled or not.
* @readonly
*/
this.listWidth = null;
/**
* @property {String} controlPositioning The direction for the options to be added in the field.
* @readonly
*/
this.controlPositioning = null;
/**
* @property {String} [separator=","]
* Defines the separator between every item selected on the field
*/
this.separator = null;
OptionsSelectorField.prototype.init.call(this, settings);
};
PMUI.inheritFrom("PMUI.form.Field", OptionsSelectorField);
OptionsSelectorField.prototype.type = "OptionsSelectorField";
OptionsSelectorField.prototype.init = function (settings) {
var defaults = {
options: [],
listWidth: 'auto',
separator: ",",
controlPositioning: "vertical"
};
jQuery.extend(true, defaults, settings);
this.setConfigControl(defaults);
this.setSeparator(defaults.separator);
this.setListWidth(defaults.listWidth);
this.setControlPositioning(defaults.controlPositioning);
};
/**
* Set if the HTML element is visible or not.
* @param {Boolean} visible
* @chainable
*/
OptionsSelectorField.prototype.setVisible = function (visible) {
var j,
k;
visible = !!visible;
this.visible = visible;
if (this.controls) {
for (j = 0; j < this.controls.length; j += 1) {
this.controls[j].visible = visible;
}
}
if (this.html) {
this.style.addProperties({"display": this.visible ? this.display : "none"});
if (this.controls) {
for (k = 0; k < this.controls.length; k += 1) {
this.controls[k].style.addProperties({"display": this.visible ? this.display : "none"});
}
}
}
return this;
};
/**
* Sets the separator for the item when use the {@link PMUI.field.OptionsSelectorField#setValue setvalue} method
* or the {@link PMUI.field.OptionsSelectorField#getValue getvalue} method
* @param {String} type
* It is a string that represent the separator. For example: '#', '|', ',', ...
*
*/
OptionsSelectorField.prototype.setSeparator = function (type) {
if (typeof type === "string") {
this.separator = type;
}
return this;
};
/**
* Sets the options/option groups for the control.
* @param {Array} options An array with the same structure that the
* {@link PMUI.field.OptionsSelectorField#cfg-options "options"} property in the
* Config settings section.
*/
OptionsSelectorField.prototype.setOptions = function (options) {
var i = 0,
c,
j,
that = this,
controls,
k;
if (jQuery.isArray(options)) {
for (j = 0; j < that.controls.length; j += 1) {
that.dom.controlContainer.removeChild(that.controls[j].html);
}
;
that.setConfigControl({
items: options
});
controls = this.controls.slice();
for (k = 0; k < controls.length; k += 1) {
if (controls[k] !== null) {
if (that.dom.controlContainer) {
that.dom.controlContainer.appendChild(controls[k].getHTML());
controls[k].defineEvents();
controls[k] = null;
}
}
}
} else {
throw new Error("setOptions(): the supplied argument must be an array.");
}
return this;
};
OptionsSelectorField.prototype.setConfigControl = function (options) {
var newControl;
newControl = new PMUI.control.OptionsSelectorControl(options);
newControl.setField(this);
this.controls = [];
this.controls.push(newControl);
return this;
};
OptionsSelectorField.prototype.setControlPositioning = function (positioning) {
var that = this,
k;
if (typeof positioning === 'string') {
this.controlPositioning = positioning;
controls = this.controls.slice();
for (k = 0; k < controls.length; k += 1) {
if (controls[k] !== null) {
if (that.dom.controlContainer) {
that.dom.controlContainer.appendChild(controls[k].getHTML());
controls[k] = null;
}
}
}
}
return this;
};
OptionsSelectorField.prototype.setOnChangeHandler = function () {
return this;
};
/**
* Sets the value for the field
* If the parameter is simple string like to
*
* "myoption" || "1"
*
* the value will be assigned to all the items.
*
* Otherwise the parameter must have the follow structure
*
* "1,2,3" || "first,second,third, ...]"
*
* @param {String} value
*/
OptionsSelectorField.prototype.setValue = function (value) {
value = value.toString();
if (typeof value === 'string') {
controls = this.controls[0];
this.value = value;
this.data.setValue(value);
this.setValueToControls(value);
} else {
throw new Error("The setValue() method only accepts string values!");
}
return this;
};
/**
* Gets the value for the item
* @return {[type]} [description]
*/
OptionsSelectorField.prototype.getValue = function () {
var controls,
values,
i;
controls = this.controls[0];
items = this.getItemsSelected();
if (controls.multipleSelection) {
values = [];
for (i = 0; i < items.length; i += 1) {
values.push(items[i].getValue());
}
values.sort();
values = values.join(this.separator);
} else {
values = items[0].getValue();
}
this.value = values
return this.value;
};
/**
* Adds a new item to current options of the OptionsSelector field
* @param {Boolean} option This is a basic {@link PMUI.control.OptionsSelectorItemControl element} for the field.
*/
OptionsSelectorField.prototype.addOption = function (option) {
var newOption,
control;
control = this.controls[0];
if (control.html) {
control.addItem(jQuery.extend(true, option, {name: this.controls.length + 1}));
}
return this;
};
/**
* Gets the options or items of the field
* @return {Array} options Items of the field
*/
OptionsSelectorField.prototype.getOptions = function (includeGroups) {
return this.controls[0].getOptions(includeGroups);
};
/**
* Clear all the options from the control.
* @chainable
*/
OptionsSelectorField.prototype.clearOptions = function () {
this.controls[0].clearOptions();
return this;
};
/**
* [disableOption description]
* @param {[type]} position [description]
* @return {[type]} [description]
*/
OptionsSelectorField.prototype.disableOption = function (position) {
this.controls[0].disableOption(position, true);
return this;
};
/**
* [enableOption description]
* @param {[type]} position [description]
* @return {[type]} [description]
*/
OptionsSelectorField.prototype.enableOption = function (position) {
this.controls[0].disableOption(position, false);
return this;
};
/**
* Removes an option of the field
* @param {Number} position Represent the index of the options array
*
* For example if the options array is:
*
* options = [a, b, c, d]
*
* And the option 'b' has to be removed, the method will be:
*
* obj.removeOption(2);
*
* @return {Object} this
*/
OptionsSelectorField.prototype.removeOption = function (position) {
this.controls[0].removeOption(position);
return this;
};
OptionsSelectorField.prototype.getSelectedLabel = function () {
this.controls[0].getSelectedLabel();
return this;
};
/**
* Disables the field, the label and the control of the field are disabled
* @return {Object} this
*/
OptionsSelectorField.prototype.disable = function () {
var items,
i;
this.disabled = true;
OptionsSelectorField.superclass.prototype.disable.call(this);
if (jQuery.isArray(this.controls)) {
if (this.controls[0]) {
control = this.controls[0];
for (i = 0; i < control.items.getSize(); i += 1) {
control.disableOption(i, true);
}
}
}
return this;
};
/**
* This method enable the field if the label and the control of the item are disabled.
* @return {Object} this
*/
OptionsSelectorField.prototype.enable = function () {
var control,
i;
this.disabled = false;
OptionsSelectorField.superclass.prototype.enable.call(this);
if (jQuery.isArray(this.controls)) {
if (this.controls[0]) {
control = this.controls[0];
for (i = 0; i < control.items.getSize(); i += 1) {
control.disableOption(i, false);
}
}
}
return this;
};
OptionsSelectorField.prototype.setListWidth = function (width) {
var items,
i;
if (typeof this.width === "number") {
this.listWidth = width;
if (jQuery.isArray(this.controls)) {
items = this.controls;
for (i = 0; i < items.length; i += 1) {
items[i].setWidth(this.listWidth);
}
}
}
return this;
};
OptionsSelectorField.prototype.getListWidth = function (width) {
return this.listWidth;
};
/**
* Gets all items selected on the OptionsSelector
* @return {Array} items Represent all the items selected on the Field
*/
OptionsSelectorField.prototype.getItemsSelected = function () {
var items,
i,
selected = [];
if (jQuery.isArray(this.controls)) {
items = this.controls;
for (i = 0; i < items.length; i += 1) {
selected = jQuery.merge(selected, items[i].getItemsSelected());
}
}
return selected;
};
/**
* Sets the orientation for every {@link PMUI.control.OptionsSelectorItemControl element}
* There are two possibilities:
*
* - horizontal, the elements will be aligned horizontally
* - vertical, the elements will be aligned vertically
*
* @param {String} orientation
* @return {Object} this
*/
OptionsSelectorField.prototype.setOrientation = function (orientation) {
var items,
i;
if (typeof orientation === "string" && jQuery.isArray(this.controls) === true) {
items = this.controls;
for (i = 0; i < items.length; i += 1) {
items[i].setOrientation(orientation);
}
}
return this;
};
OptionsSelectorField.prototype.setLabel = function (label) {
if (typeof label === 'string') {
this.label = label;
} else {
throw new Error("The setLabel() method only accepts string values!");
}
if (this.dom.labelTextContainer) {
if (label === '[field]' || label === '') {
this.label = '';
this.dom.labelTextContainer.textContent = this.label;
} else {
this.dom.labelTextContainer.textContent = this.label + (this.colonVisible ? ':' : '');
}
}
return this;
};
PMUI.extendNamespace('PMUI.field.OptionsSelectorField', OptionsSelectorField);
if (typeof exports !== "undefined") {
module.exports = OptionsSelectorField;
}
}());
(function () {
var UploadControl = function (settings) {
UploadControl.superclass.call(this, settings);
this.accept = null;
this.multiple = null;
this.files = null;
UploadControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.HTMLControl', UploadControl);
UploadControl.prototype.type = 'UploadControl';
UploadControl.prototype.family = 'control';
UploadControl.prototype.init = function (settings) {
var defaults;
defaults = {
accept: "",
multiple: false,
height: '100%'
};
jQuery.extend(true, defaults, settings);
this.setAcceptedFiles(defaults.accept)
.setMultiple(defaults.multiple)
.setHeight(defaults.height);
};
/**
* @method setAccepted
* here we can define the type files our send, the valid types are MIME, separation with ","
* for more information go to http://reference.sitepoint.com/html/mime-types-full
* @param {String} accept MIME types are valid for our upload
*/
UploadControl.prototype.setAcceptedFiles = function (accept) {
if (!(typeof accept === "string")) {
throw new Error('setAcceptedFiles(): The parameter type is invalid, should be type string');
}
this.accept = '';
this.accept = accept;
if (this.html) {
this.html.accept = accept;
}
return this;
};
UploadControl.prototype.defineEvents = function () {
var that = this;
UploadControl.superclass.prototype.defineEvents.call(this);
this.html.addEventListener("click", function (e) {
e.stopPropagation();
});
return this;
};
UploadControl.prototype.setMultiple = function (value) {
if (!(typeof value === 'boolean')) {
throw new Error('setMultiple(): The parameter type is invalid, should be type string');
}
this.multiple = value;
if (this.html) {
this.html.multiple = value;
}
return this;
};
UploadControl.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
UploadControl.superclass.prototype.createHTML.call(this);
this.html.type = 'file';
this.setMultiple(this.multiple);
this.setAcceptedFiles(this.accept);
return this.html;
};
PMUI.extendNamespace('PMUI.control.UploadControl', UploadControl);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = UploadControl;
}
}());
(function () {
/**
* @class PMUI.control.ButtonControl
* @extends PMUI.control.Control
*
* this button is a control, the config options is not equal to button of UI
* the use of this control is for a field
*/
var ButtonControl = function (settings) {
ButtonControl.superclass.call(this, settings);
/**
* this a PMUI.ui.Button object
* @type {PMUI.ui.Button}
*/
this.button = null;
ButtonControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.Control', ButtonControl);
ButtonControl.prototype.type = "ButtonControl";
ButtonControl.prototype.init = function (settings) {
var defaults = {
handler: null
};
jQuery.extend(true, defaults, settings);
this.button = new PMUI.ui.Button();
this.setHandler(defaults.handler);
};
/**
* diabled the buttonControl
* @param {Boolean} value if true disable button, default value false
* @chainable
*/
ButtonControl.prototype.setDisabled = function (value) {
if (this.button) {
this.button.setDisabled(value);
}
return this;
}
/**
* disable Button
* @chainable
*/
ButtonControl.prototype.disable = function () {
if (this.button) {
this.button.disable();
}
return this;
};
/**
* ebable button
* @chainable
*/
ButtonControl.prototype.enable = function () {
this.button.enable();
return this;
};
/**
* set bordet for Button
* @chainable
*/
ButtonControl.prototype.setBorder = function (border) {
this.button.setBorder(border);
return this;
};
/**
* this click is established with jQuery, simulate the behavior click
* @chainable
*/
ButtonControl.prototype.click = function () {
this.button.click();
return this;
};
/**
* set handler for button
* @param {Function} handle the event to trigger
* @chainable
*/
ButtonControl.prototype.setHandler = function (handler) {
this.button.setHandler(handler);
return this;
};
/**
* remove one event of the events
* @param {String} type It is an event that is stored in an object event
* @chainable
*/
ButtonControl.prototype.removeEvent = function (type) {
this.button.removeEvent(type);
return this;
};
/**
* removes all events
* @return {[type]}
*/
ButtonControl.prototype.removeEvents = function () {
this.button.removeEvents();
return this;
};
/**
* fix the text for the button
* @param {String} text
*/
ButtonControl.prototype.setText = function (text) {
this.button.setText(text);
return this;
};
ButtonControl.prototype.setIcon = function (iconClass) {
this.button.setIcon(iconClass);
return this;
};
ButtonControl.prototype.setAliasButton = function (alias) {
this.button.setAliasButton(alias);
return this;
};
ButtonControl.prototype.setParent = function (parent) {
this.button.setParent(parent);
return this;
};
ButtonControl.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
this.html = this.button.getHTML();
return this.html;
};
ButtonControl.prototype.defineEvents = function () {
this.button.defineEvents();
return this;
};
ButtonControl.prototype.setHeight = function (height) {
if (this.button) {
this.button.setHeight(height);
}
return this;
};
ButtonControl.prototype.setIconPosition = function (position) {
this.setIconPosition(position);
return this;
};
/**
* fix a message for button
* @param {String} messageTooltip
*/
ButtonControl.prototype.setTooltipMessage = function (messageTooltip) {
this.button.setTooltipMessage(messageTooltip);
return this;
};
PMUI.extendNamespace('PMUI.control.ButtonControl', ButtonControl);
if (typeof exports !== "undefined") {
module.exports = ButtonControl;
}
}());
(function () {
var NumberControl = function (settings) {
NumberControl.superclass.call(this, settings);
/**
* @event onKeyUp
* Fired when a pressed key is released.
* @param {Object} event The event object.
*/
this.onKeyUp = null;
this.mask = null;
this.percentage = null;
this.filterKey = null;
this.maskList = [];
this.expRegList = [];
this.expRegMask = [];
this.optionDecimal = null;
this.decimals = null;
this.valueInit = null;
this.onlyNumber = null;
this.onHandlerMaskKeyDown = null;
this.onHandlerMaskKeyPress = null;
NumberControl.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.control.TextControl', NumberControl);
NumberControl.prototype.type = "TextControl";
/**
* Initializes the object.
* @param {Object} [settings=null] A JSON object with the config options.
* @private
*/
NumberControl.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 524288,
onKeyUp: null,
optionDecimal: null,
valueInit: "",
mask: "###,###,###,###.###########"
};
$.extend(true, defaults, settings);
this.setMaskDefault();
this.setPlaceholder(defaults.placeholder)
.setMaxLength(defaults.maxLength)
.setOnKeyUpHandler(defaults.onKeyUp)
.setOptionDecimal(defaults.optionDecimal)
.setMask(defaults.mask);
this.mask = defaults.mask;
};
/**
* Sets the callback function to be called when the keyup event occurs.
* @chainable
*/
NumberControl.prototype.evalMask = function (textvalue) {
var i;
for (i = 0; i < this.expRegList.length; i += 1) {
if (this.expRegList[i].test(textvalue))
return true;
}
return false;
};
NumberControl.prototype.setOptionDecimal = function (option) {
if (typeof option === "string")
this.optionDecimal = option;
return this;
};
NumberControl.prototype.evalMaskNumeric = function (textvalue) {
var sw = true,
i;
if (this.expRegMask === undefined) {
return true;
}
else {
for (i = 0; i < this.expRegMask.length; i += 1) {
if (this.expRegMask[i][2] != null) {
if (!this.expRegMask[i][2].test(textvalue))
sw = false;
}
}
return sw;
}
};
NumberControl.prototype.truncate = function (number, digits) {
var multiplier = Math.pow(10, digits),
adjustedNum = number * multiplier,
truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
return truncatedNum / multiplier;
};
NumberControl.prototype.setValue = function (strControl) {
var strAux;
this.valueInit = strControl;
if (this.isNumber(strControl)) {
if (strControl != null && strControl != "") {
if (this.evalMaskNumeric(strControl) || strControl == "") {
NumberControl.superclass.prototype.setValue.call(this, strControl);
//this.setValue(strControl);
}
else {
if (this.optionDecimal == "truncate") {
}
strControl = this.removeNumberCommas(strControl);
if (this.optionDecimal == "truncate")
strAux = this.truncate(parseFloat(strControl), this.decimals);
if (this.optionDecimal == "round")
strAux = Math.ceil(strControl * ('1e' + this.decimals)) / ('1e' + this.decimals);
strAux = this.setNumberCommas(strAux == undefined ? "" : strAux.toString());
if (this.evalMaskNumeric(strAux)) {
NumberControl.superclass.prototype.setValue.call(this, strAux);
// this.setValue(strAux);
}
}
}
}
return this;
};
NumberControl.prototype.getValue = function (control) {
var value,
array,
result,
i;
if (this.valueInit != null) {
if (control)
return this.removeNumberCommas(this.valueInit);
else {
if (this.html)
return this.html.value;
else
return "";
}
}
if (control) {
value = this.getValue();
array = value.split(",");
result = "";
for (i = 0; i < array.length; i += 1) {
result = result + array[i];
}
return result;
}
return NumberControl.superclass.prototype.getValue.call(this);
};
NumberControl.prototype.isNumber = function (strControl) {
var validator = /^[\d\,\.]*$/;
if (validator.test(strControl))
return true;
else
return false;
};
NumberControl.prototype.removeNumberCommas = function (strControl) {
var pint = this.getParseInt(strControl),
pdec = this.getParseDec(strControl);
if (pdec != undefined)
strControl = pint + "." + pdec;
else
strControl = pint;
return strControl;
};
NumberControl.prototype.setNumberCommas = function (strControl) {
var pint = this.getParseInt(strControl),
pdec = this.getParseDec(strControl),
array,
arraypos,
a1,
a2,
strFinal = "",
i;
array = pint.split(",");
for (i = 0; i <= array.length - 1; i += 1) {
strFinal += array[i];
}
arraypos = strFinal.length;
while (arraypos > 3) {
a1 = strFinal.substr(0, arraypos - 3);
a2 = strFinal.substr(arraypos - 3, strFinal.length);
strFinal = a1 + "," + a2;
arraypos++;
arraypos = arraypos - 4;
}
if (pdec != undefined)
return strFinal + "." + pdec;
else
return strFinal;
};
NumberControl.prototype.setMaskDefault = function () {
this.expRegMask.push([/^\#{0,3}(\,\#{3})*\%{0,1}$/, '^\\d{0,3}(\\,\\d{3}){@n}\%{0,1}$', null, false]); // captura de numeros sin decimal con comas
this.expRegMask.push([/^[\#\,0]+(?:(\.)([\#0]*)){1}(\%){0,1}$/, '^[\\d\\,]+(\\.){0,1}(\\d{@n}){0,1}(\%){0,1}$', null, false]); // decimales
this.expRegMask.push([/^[\#0]{0,3}(\,[\#0]{3})*(?:(\.)([\#0]*))(\%){0,1}$/, '^\\d{@x}(\\,\\d{3}){@n}((\\.)(\\d*)){0,1}(\%){0,1}$', null, false]); // parte entera
this.expRegMask.push([/^[\#\,]*(0*)\.(0*)(\%){0,1}$/, '^[\\d\\,]*(0{@x})\\.([\\d]{@y})(\%){0,1}$', null, false]); // zeros fijos
this.expRegMask.push([/^[\#\,\.]*\%{1}$/, '^[\\d\\,\\.]*\\%{1}$', null, false]); // percentage
this.expRegMask.push([/^\#*\%{0,1}$/, '^\\d{@n}\%{0,1}$', null, false]); // captura de numeros sin decimal con comas
};
NumberControl.prototype.setMaskNumeric = function (mask) {
var array,
ngroups,
ini,
nreg,
nx,
ny,
a;
if (this.expRegMask[0][0].test(mask)) {
array = this.expRegMask[0][0].exec(mask);
ngroups = array[0].split(",").length - 1;
this.expRegMask[0][2] = new RegExp(this.expRegMask[0][1].replace(/@n/g, '0,' + ngroups.toString()));
this.expRegMask[0][3] = true;
}
if (this.expRegMask[1][0].test(mask)) {
array = this.expRegMask[1][0].exec(mask);
ngroups = array[2].length;
this.decimals = ngroups;
this.expRegMask[1][2] = new RegExp(this.expRegMask[1][1].replace(/@n/g, '0,' + ngroups.toString()));
this.expRegMask[1][3] = true;
}
if (this.expRegMask[2][0].test(mask)) {
array = this.expRegMask[2][0].exec(mask);
ngroups = array[0].split(",").length - 1;
ini = this.expRegMask[2][0].exec("##,###.##")[0].split(",")[0].length
nreg = this.expRegMask[2][1].replace(/@n/g, '0,' + ngroups.toString());
this.expRegMask[2][2] = new RegExp(nreg.replace(/@x/g, '0,3'));
this.expRegMask[2][3] = true;
}
if (this.expRegMask[3][0].test(mask)) {
array = this.expRegMask[3][0].exec(mask);
nx = array[1].length;
ny = array[2].length;
a = this.expRegMask[3][1].replace(/@x/g, '0,' + nx.toString());
a = a.replace(/@y/g, '0,' + ny.toString());
this.expRegMask[3][2] = new RegExp(a);
this.expRegMask[3][3] = true;
}
//percentage
if (this.expRegMask[4][0].test(mask)) {
this.expRegMask[4][2] = new RegExp(this.expRegMask[4][1]);
this.expRegMask[4][3] = true;
this.percentage = true;
}
//number not comas
if (this.expRegMask[5][0].test(mask)) {
array = this.expRegMask[5][0].exec(mask);
nx;
if (this.percentage)
nx = array[0].length - 1;
else
nx = array[0].length;
this.expRegMask[5][2] = new RegExp(this.expRegMask[5][1].replace(/@n/g, '0,' + nx.toString()));
this.expRegMask[5][3] = true;
this.onlyNumber = true;
}
};
NumberControl.prototype.setMaskHandlerNumeric = function (mask) {
//handler numeric
var that = this,
thatMask = mask;
handlerKeyDown = function (e) {
var pressKey,
textValue,
regResult,
selStart,
selEnd,
strStart,
strEnd,
strControl = "",
textLength,
strFinal,
pressKey = (window.event) ? window.event.keyCode : e.which,
onlynumbers,
dash,
flagequal,
flagZeros,
pint,
pdec;
textValue = that.html.value;
selStart = that.html.selectionStart;
selEnd = that.html.selectionEnd;
textLength = that.html.value.length;
onlynumbers = that.expRegMask[0][3];
dash = that.expRegMask[1][3];
flagequal = false;
flagZeros = that.expRegMask[3][3];
if (selStart == selEnd) {
flagequal = true;
}
if ((pressKey > 46 && pressKey <= 57) || (pressKey >= 96 && pressKey <= 105) || pressKey == 8 || pressKey == 190 || pressKey == 37 || pressKey == 39) {
this.valueInit = null;
//quitar porcentaje
if (this.percentage)
textValue = textValue.substr(0, textLength - 1);
if (pressKey >= 96 && pressKey <= 105)
pressKey = pressKey - 48;
if (pressKey > 45 && pressKey <= 57) {
if (selStart == textLength) {
strControl += textValue + String.fromCharCode(pressKey);
selStart = selStart + 1;
}
else {
strStart = textValue.substr(0, selStart);
strEnd = textValue.substr(selStart, textLength);
strControl = strStart + String.fromCharCode(pressKey) + strEnd;
selStart++;
}
}
if (pressKey == 8) {
if (flagequal && selStart == textLength) {
strControl = textValue.substr(0, selStart - 1);
}
else {
strStart = textValue.substr(0, selStart - 1);
strEnd = textValue.substr(selStart, textLength);
strControl = strStart + strEnd;
selStart--;
}
}
if (onlynumbers && pressKey == 190) {
strControl = textValue;
}
if (pressKey == 37) {
selStart = selStart - 1;
strControl = textValue;
}
if (pressKey == 39) {
if (selStart == selEnd) {
selEnd++;
}
selStart = selStart + 1;
strControl = textValue;
}
if (flagZeros) {
strControl = that.setZeros(strControl, thatMask);
}
//convierto con comas
if (this.onlyNumber) {
if (pressKey == 190) {
strFinal = textValue;
}
if (pressKey != 190) {
strFinal = strControl;
}
}
else {
pint = that.getParseInt(strControl);
pdec = that.getParseDec(strControl);
selStart = selStart - (strControl.split(",").length - 1);
strFinal = that.setComas(pint);
selStart = selStart + (strFinal.split(",").length - 1);
if (pdec != undefined) {
strFinal = strFinal + "." + pdec;
if (flagequal && selStart > selEnd)
selEnd = selStart;
else {
selEnd = selStart;
}
}
}
if (dash && pressKey == 190) {
strControl += textValue + ".";
strFinal = strControl;
selStart = selStart + 1;
}
}
else {
strFinal = this.html.value;
}
//agregar signo de porcentaje
if (this.percentage) {
strFinal += "%";
}
if (this.evalMaskNumeric(strFinal) || strFinal == "") {
that.html.value = strFinal;
that.html.selectionStart = selStart;
if (flagequal) {
that.html.selectionEnd = selStart;
}
}
e.preventDefault();
}
this.onHandlerMaskKeyDown = handlerKeyDown;
};
NumberControl.prototype.setComas = function (strControl) {
var array,
arraypos,
a1,
a2,
i,
strFinal = "";
array = strControl.split(",");
for (i = 0; i <= array.length - 1; i += 1) {
strFinal += array[i];
}
arraypos = strFinal.length;
while (arraypos > 3) {
a1 = strFinal.substr(0, arraypos - 3);
a2 = strFinal.substr(arraypos - 3, strFinal.length);
strFinal = a1 + "," + a2;
arraypos++;
arraypos = arraypos - 4;
}
return strFinal;
};
NumberControl.prototype.getParseInt = function (strControl) {
var aux1,
aux2,
array,
strFinal = "",
i;
aux1 = strControl.split(".")[0];
array = aux1.split(",");
for (i = 0; i <= array.length - 1; i += 1) {
strFinal += array[i];
}
return strFinal;
};
NumberControl.prototype.getParseDec = function (strControl) {
var aux1,
aux2,
array,
strFinal = "";
aux1 = strControl.split(".")[1];
return aux1;
}
NumberControl.prototype.setZeros = function (strControl, mask) {
var i,
aux1,
aux2,
array,
strFinal = "",
nZerosLeft,
nZerosRight,
nRightStr;
array = this.expRegMask[3][0].exec(mask);
nZerosLeft = array[1].length;
nZerosRight = array[2].length;
if (strControl.split(".").length != 1) {
nRightStr = strControl.split(".")[1].length;
if (nRightStr < nZerosRight) {
for (i = 0; i < nZerosRight - nRightStr; i += 1) {
strControl += "0";
}
return strControl;
}
if (nRightStr > nZerosRight) {
strControl = strControl.substr(0, strControl.length - 1);
}
return strControl;
}
else {
if (strControl == "")
return array[1] + "." + array[2];
else {
return strControl + "." + array[2];
}
}
return aux1;
}
NumberControl.prototype.setMask = function (mask) {
var expMaskPercentage = /^(\#+)(?:(\.)(\#*))?\%$/i,
expMaskNumber1 = /^(\#+)(?:(\.)(\#*))?$/i,
expMaskNumber2 = /^(\#{0,3})(?:(\,)(\#{3}))*(?:(\.)(\#*))?$/i;
this.onHandlerMaskKeyDown = null;
this.onHandlerMaskKeyPress = null;
this.maskList = [];
this.expRegMask[0][3] = false;
this.expRegMask[1][3] = false;
this.expRegMask[2][3] = false;
this.expRegMask[3][3] = false;
this.expRegMask[4][3] = false;
this.expRegMask[0][2] = null;
this.expRegMask[1][2] = null;
this.expRegMask[2][2] = null;
this.expRegMask[3][2] = null;
this.expRegMask[4][2] = null;
// Save the list of mask
if (typeof mask === "string") {
this.percentage = null;
this.onlyNumber = null;
this.maskList.push(mask);
this.setMaskNumeric(mask);
this.setMaskHandlerNumeric(mask);
}
};
/**
* Set the events for the object.
* @chainable
*/
NumberControl.prototype.defineEvents = function () {
var that = this;
if (this.html && !this.eventsDefined) {
NumberControl.superclass.prototype.defineEvents.call(this);
this.addEvent('keyup').listen(this.html, function (e) {
if (typeof that.onKeyUp === 'function') {
that.onKeyUp(e);
}
});
this.addEvent('keydown').listen(this.html, function (e) {
if (e.which === PMUI.keyCodeF5) {
this.blur();
e.preventDefault();
window.location.reload(true);
}
if (typeof that.onHandlerMaskKeyDown === 'function') {
that.onHandlerMaskKeyDown(e);
}
});
this.addEvent('keypress').listen(this.html, function (e) {
if (typeof that.onHandlerMaskKeyPress === 'function') {
that.onHandlerMaskKeyPress(e);
}
});
this.eventsDefined = true;
}
return this;
};
/**
* Creates the HTML element for the object
* @return {HTMLElement}
*/
NumberControl.prototype.createHTML = function () {
NumberControl.superclass.prototype.createHTML.call(this);
this.html.type = "text";
this.setPlaceholder(this.placeholder)
.setMaxLength(this.maxLength)
.setReadOnly(this.readonly);
//this.defineEvents();
this.setMask(this.maskList[0]);
return this.html;
};
/**
* Sets if the control will be enabled for read only.
* @param {Boolean}
* @chainable
*/
PMUI.extendNamespace('PMUI.control.NumberControl', NumberControl);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = NumberControl;
}
}());
(function () {
var NumberField = function (settings) {
NumberField.superclass.call(this, settings);
/**
* @property {Boolean} [trimOnBlur=true] If the field's value must be trimmed every time it loses focus.
*/
this.trimOnBlur = null;
NumberField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', NumberField);
NumberField.prototype.type = "NumberField";
NumberField.prototype.init = function (settings) {
var defaults = {
placeholder: "",
maxLength: 0,
trimOnBlur: true,
readOnly: false,
mask: "",
value: "",
optionDecimal: null
};
$.extend(true, defaults, settings);
this.setPlaceholder(defaults.placeholder)
.setMaxLength(defaults.maxLength)
.setTrimOnBlur(defaults.trimOnBlur)
.setReadOnly(defaults.readOnly)
.setOptionDecimal(defaults.optionDecimal)
.setMask(defaults.mask);
};
/**
* Sets the value for the field.
* @param {String} value
* @chainable
*/
NumberField.prototype.setValue = function (value) {
if (this.trimOnBlur) {
value = jQuery.trim(value);
}
NumberField.superclass.prototype.setValue.call(this, value);
//this.controls[0].setValue(value);
return this;
};
/**
* Sets the value for the field.
* @param {String} value
* @chainable
*/
NumberField.prototype.setOptionDecimal = function (value) {
if (this.trimOnBlur) {
value = jQuery.trim(value);
}
this.controls[0].setOptionDecimal(value);
return this;
};
/**
* Sets the value for the field.
* @param {String} value
* @chainable
*/
NumberField.prototype.setMask = function (value) {
if (this.trimOnBlur) {
value = jQuery.trim(value);
}
this.controls[0].setMask(value);
return this;
};
/**
* Sets the placeholder for the control. Note that this feature is only supported
* by browsers which support the "placeholder" for input elements.
* @param {String} placeholder
* @chainable
*/
NumberField.prototype.setReadOnly = function (readonly) {
this.controls[0].setReadOnly(readonly);
return this;
};
/**
* [setPlaceholder description]
* @param {[type]} placeholder [description]
*/
NumberField.prototype.setPlaceholder = function (placeholder) {
this.controls[0].setPlaceholder(placeholder);
return this;
};
/**
* [getPlaceholder description]
* @return {[type]} [description]
*/
NumberField.prototype.getPlaceholder = function () {
return this.controls[0].getPlaceholder();
};
/**
* [setMaxLength description]
* @param {[type]} maxLength [description]
*/
NumberField.prototype.setMaxLength = function (maxLength) {
this.controls[0].setMaxLength(maxLength);
return this;
};
/**
* [getMaxLength description]
* @return {[type]} [description]
*/
NumberField.prototype.getMaxLength = function () {
return this.controls[0].getMaxLength();
};
/**
* [isReadOnly description]
* @return {Boolean} [description]
*/
NumberField.prototype.isReadOnly = function () {
return this.controls[0].isReadOnly();
};
/**
* Switches on/off the value trimming for the field's value when it loses focus.
* @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
* @chainable
*/
NumberField.prototype.setTrimOnBlur = function (trimOnBlur) {
this.trimOnBlur = !!trimOnBlur;
return this;
};
/**
* Returns a boolean value that indicates if the trimming function is enabled/disabled
* @return {Boolean}
*/
NumberField.prototype.getTrimOnBlur = function () {
return this.trimOnBlur;
};
/**
* Sets the control for the NumberField
* @chainable
* @private
*/
NumberField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.NumberControl());
return this;
};
/**
* Sets the control for the NumberField
* @chainable
* @private
*/
NumberField.prototype.getValue = function (control) {
return this.controls[0].getValue(control);
};
/**
* Establish the handler function for the internal onChange event
* @return {Function}
* @private
*/
NumberField.prototype.onChangeHandler = function () {
var that = this;
return function () {
var previousValue = that.value,
value,
i;
if (that.trimOnBlur) {
value = that.controls[0].getValue();
value = jQuery.trim(value);
that.controls[0].setValue(value);
}
that.updateValueFromControls();
if (that.validAtChange) {
that.isValid();
}
if (typeof that.onChange === 'function') {
that.onChange(that.getValue(), previousValue);
}
if (that.form) {
(that.form.onChangeHandler())(that, that.getValue(), previousValue);
}
if (previousValue !== value) {
for (i = 0; i < that.dependentFields.getSize(); i += 1) {
that.dependentFields.get(i).dependencyHandler(that.dependentFields.get(i).dependencyFields.asArray().slice(0));
}
}
};
};
PMUI.extendNamespace('PMUI.field.NumberField', NumberField);
if (typeof exports !== "undefined") {
module.exports = NumberField;
}
}());
(function () {
var UploadField = function (settings) {
UploadField.superclass.call(this, settings);
UploadField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', UploadField);
UploadField.prototype.type = 'UploadField';
UploadField.prototype.family = 'field';
UploadField.prototype.init = function (settings) {
var defaults;
defaults = {
accept: "",
multiple: false
};
jQuery.extend(true, defaults, settings);
this.setAcceptedFiles(defaults.accept)
.setMultipleFiles(defaults.multiple);
};
/**
* @method setAccepted
* here we can define the type files our send, the valid types are MIME, separation with ","
* for more information go to http://reference.sitepoint.com/html/mime-types-full
* @param {String} accept MIME types are valid for our upload
*/
UploadField.prototype.setAcceptedFiles = function (accept) {
this.controls[0].setAcceptedFiles(accept);
return this;
};
UploadField.prototype.setMultipleFiles = function (value) {
this.controls[0].setMultiple(value);
return this;
};
UploadField.prototype.setControls = function () {
if (this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.UploadControl());
return this;
};
PMUI.extendNamespace('PMUI.field.UploadField', UploadField);
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== "undefined") {
module.exports = UploadField;
}
}());
(function () {
/**
* @class PMUI.field.ButtonField
* @extends PMUI.form.Field
* is a special field not return a value unlike other fields it is not
* equal intanciacion a button PMUI.ui.Button, new properties tine see examples:
*
* @example
* var b1,b2,b3;
* $(function() {
* b1 = new PMUI.field.ButtonField({
* value : "button1",
* handler : function () {
* alert("hi! I 'm Button 1")
* },
* name : 'button1'
* });
*
* document.body.appendChild(b1.getHTML());
* b1.defineEvents();
*
*
* b2 = new PMUI.field.ButtonField({
* value : "button2",
* handler : function () {
* alert("hi! I 'm Button 2")
* },
* name : 'button2',
* labelVisible : false,
* style : {
* cssClasses : ['customButton']
* },
* buttonAlign : 'center'
* });
*
* document.body.appendChild(b2.getHTML());
* b2.defineEvents();
*
* b3 = new PMUI.field.ButtonField({
* value : "button3",
* handler : function () {
* alert("hi! I 'm Button 3")
* },
* name : 'button3',
* labelVisible : false,
* buttonAlign : 'center'
* });
*
* document.body.appendChild(b3.getHTML());
* b3.defineEvents();
* });
*
*
* @cfg {Boolean} [disableButtonControl = [false]] button will be disabled if
* @cfg {String} [value = "[button-field]"] be the value with the button as text, this value does not reflect data on a form
* @cfg {String} [tooltipMessage = [""]] message will have the button
* @cfg {String} [controlIconClass = [""]] icon wearing this button is a class
* @cfg {Function} [handler = function(){}] the handler of button in the field
* @cfg {String} [buttonAlign = [""]] the position will have the pin is reflected when the label of the field is hidden
*/
var ButtonField = function (settings) {
ButtonField.superclass.call(this, settings);
this.buttonAlign = null;
this.handler = null;
ButtonField.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.form.Field', ButtonField);
ButtonField.prototype.type = "ButtonField";
ButtonField.prototype.init = function (settings) {
var defaults = {
disableButtonControl: false,
value: '[button-field]',
tooltipMessage: "",
controlIconClass: '',
handler: function () {
},
buttonAlign: '',
handler: null
}
jQuery.extend(true, defaults, settings);
this.setHandler(defaults.handler);
this.setControls();
this.setDisabled(defaults.disableButtonControl);
this.setValue(defaults.value);
this.setTooltipMessage(defaults.tooltipMessage);
this.setButtonAlign(defaults.buttonAlign);
};
/**
* The callback function to be executed when the the field's button is clicked.
* @return {Function} The callback function.
* @private
*/
ButtonField.prototype.onButtonClickHandler = function () {
var that = this;
return function (button) {
if (typeof that.handler === 'function') {
that.handler(that, button);
}
};
};
/**
* fix the control PMUI.control.ButtonControl
* @chainable
*/
ButtonField.prototype.setControls = function () {
var that = this;
if (this.handler === undefined || this.controls.length) {
return this;
}
this.controls.push(new PMUI.control.ButtonControl({
handler: that.onButtonClickHandler()
}));
return this;
};
/**
* diabled the Buttonfield
* @param {Boolean} value if true disable button, default value false
* @chainable
*/
ButtonField.prototype.setDisabled = function (value) {
this.controls[0].setDisabled(value);
return this;
}
/**
* disable Button
* @chainable
*/
ButtonField.prototype.disable = function () {
if (this.controls[0]) {
this.controls[0].disable();
}
return this;
};
/**
* ebable button
* @chainable
*/
ButtonField.prototype.enable = function () {
if (this.controls[0]) {
this.controls[0].enable();
}
return this;
};
ButtonField.prototype.click = function () {
if (this.controls[0].click) {
this.controls[0].click();
}
return this;
};
/**
* this click is established with jQuery, simulate the behavior click
* @chainable
*/
ButtonField.prototype.setHandler = function (handler) {
if (!(handler === null || typeof handler === 'function')) {
throw new Error("setHandler(): The parameter must be a function or null.");
}
this.handler = handler;
return this;
};
/**
* remove one event of the events
* @param {String} type It is an event that is stored in an object event
* @chainable
*/
ButtonField.prototype.removeEvent = function (type) {
this.controls[0].removeEvent(type);
return this;
};
/**
* removes all events
* @return {[type]}
*/
ButtonField.prototype.removeEvents = function () {
this.controls[0].removeEvents();
return this;
};
/**
* fix the text for the button
* @param {String} value
*/
ButtonField.prototype.setValue = function (value) {
this.value = value;
if (this.controls[0]) {
this.controls[0].setText(value);
}
return this;
};
ButtonField.prototype.setIcon = function (iconClass) {
this.controls[0].setIcon(iconClass);
return this;
};
ButtonField.prototype.setAliasButton = function (alias) {
this.controls[0].setAliasButton(alias);
return this;
};
ButtonField.prototype.setParent = function (parent) {
this.controls[0].setParent(parent);
return this;
};
ButtonField.prototype.defineEvents = function () {
ButtonField.superclass.prototype.defineEvents.call(this);
this.controls[0].defineEvents();
return this;
};
/**
* fix a message for button
* @param {String} messageTooltip
*/
ButtonField.prototype.setTooltipMessage = function (messageTooltip) {
this.controls[0].setTooltipMessage(messageTooltip);
return this;
};
/**
* the value return is forever null
* @return {object}
*/
ButtonField.prototype.getValue = function () {
return null;
}
/**
* hide label of the field, display none
* @chainable
*/
ButtonField.prototype.hideLabel = function () {
jQuery(this.dom.labelTextContainer).hide();
this.labelVisible = false;
return this;
}
/**
* hide label of the field, display inline-block
* @chainable
*/
ButtonField.prototype.showLabel = function () {
jQuery(this.dom.labelTextContainer).show();
this.labelVisible = true;
return this;
}
ButtonField.prototype.setButtonAlign = function (position) {
if (position == "left" || position == 'center' || position == "right" || position == '') {
this.buttonAlign = position;
}
if (this.html) {
this.style.addProperties({'textAlign': position});
}
return this;
};
ButtonField.prototype.createHTML = function () {
ButtonField.superclass.prototype.createHTML.call(this);
this.setButtonAlign(this.buttonAlign);
return this.html;
};
PMUI.extendNamespace('PMUI.field.ButtonField', ButtonField);
if (typeof exports !== "undefined") {
module.exports = ButtonField;
}
}());
(function () {
var ToolbarItem = function (settings) {
ToolbarItem.superclass.call(this, settings);
this.text = null;
this.dom = {};
ToolbarItem.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Item', ToolbarItem);
ToolbarItem.prototype.type = 'ToolbarItem';
ToolbarItem.prototype.init = function (settings) {
var defaults = {
text: ''
};
jQuery.extend(true, defaults, settings);
this.setText(defaults.text);
};
ToolbarItem.prototype.setText = function (text) {
if (typeof text !== 'string') {
throw new Error('setText(): The parameter must be a string.');
}
this.text = text;
if (this.dom.textContainer) {
this.dom.textContainer = text;
}
return this;
};
ToolbarItem.prototype.createHTML = function () {
if (this.html) {
return this.html;
}
ToolbarItem.superclass.prototype.createHTML.call(this);
this.dom.textContainer = this.html;
return this.html;
};
PMUI.extendNamespace('PMUI.toolbar.ToolbarItem', ToolbarItem);
}());
(function () {
var ToolbarAction = function (settings) {
ToolbarAction.superclass.call(this, settings);
this.action = null;
this.visibleIcon = null;
this.iconClass = null;
ToolbarAction.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.toolbar.ToolbarItem', ToolbarAction);
ToolbarAction.prototype.init = function (settings) {
var defaults = {
action: null,
visibleIcon: true,
iconClass: null,
elementTag: 'li'
};
jQuery.extend(true, defaults, settings);
this.setElementTag(defaults.elementTag)
.setAction(defaults.action)
.setIconClass(defaults.iconClass);
if (defaults.visibleIcon) {
this.showIcon();
} else {
this.hideIcon();
}
};
ToolbarAction.prototype.setIconClass = function (iconClass) {
if (!(iconClass === null || typeof iconClass === 'string')) {
throw new Error("setIconClass(): the parameter must be a string or null.");
}
this.iconClass = iconClass;
if (this.dom.iconContainer) {
this.dom.iconContainer.className = 'pmui-toolbaraction-icon ' + (iconClass || "");
}
return this;
};
ToolbarAction.prototype.setText = function (text) {
if (typeof text !== 'string') {
throw new Error('setText(): The parameter must be a string.');
}
this.text = text;
if (this.dom.textContainer) {
this.dom.textContainer.textContent = text;
}
return this;
};
ToolbarAction.prototype.setAction = function (action) {
if (!(action === null || typeof action === 'function')) {
throw new Error("setAction(): the parameter must be a function or null.");
}
this.action = action;
return this;
};
ToolbarAction.prototype.showIcon = function () {
this.visibleIcon = true;
if (this.dom.iconContainer) {
this.dom.iconContainer.style.display = '';
}
return this;
};
ToolbarAction.prototype.hideIcon = function () {
this.visibleIcon = false;
if (this.dom.iconContainer) {
this.dom.iconContainer.style.display = 'none';
}
return this;
};
ToolbarAction.prototype.attachEventListeners = function () {
var that = this;
if (this.html) {
this.removeEvents();
this.addEvent('click').listen(this.dom.link, function (e) {
e.preventDefault();
e.stopPropagation();
if (typeof that.action === 'function') {
that.action(that);
}
});
}
return this;
};
ToolbarAction.prototype.createHTML = function () {
var link,
iconContainer,
textContainer;
if (this.html) {
return this.html;
}
ToolbarAction.superclass.prototype.createHTML.call(this);
link = PMUI.createHTMLElement('a');
link.className = 'pmui-toolbaraction-link';
link.href = "#";
iconContainer = PMUI.createHTMLElement('i');
iconContainer.className = 'pmui-toolbaraction-icon';
textContainer = PMUI.createHTMLElement('span');
textContainer.className = 'pmui-toolbaraction-text';
link.appendChild(iconContainer);
link.appendChild(textContainer);
this.dom.iconContainer = iconContainer;
this.dom.link = link;
this.dom.textContainer = textContainer;
this.html.appendChild(link);
this.setText(this.text)
.setIconClass(this.iconClass);
this.attachEventListeners();
return this.html;
};
PMUI.extendNamespace('PMUI.toolbar.ToolbarAction', ToolbarAction);
}());
(function () {
var Toolbar = function (settings) {
Toolbar.superclass.call(this, settings);
Toolbar.prototype.init.call(this, settings);
};
PMUI.inheritFrom('PMUI.core.Container', Toolbar);
Toolbar.prototype.type = "Toolbar";
Toolbar.prototype.init = function (settings) {
var defaults = {
elementTag: 'ul'
};
jQuery(true, defaults, settings);
this.setElementTag(defaults.elementTag);
};
Toolbar.prototype.setFactory = function () {
this.factory = new PMUI.util.Factory({
products: {
'toolbaraction': PMUI.toolbar.ToolbarAction
},
defaultProduct: 'toolbaraction'
});
return this;
};
PMUI.extendNamespace('PMUI.toolbar.Toolbar', Toolbar);
}());
(function () {
var I18N = {
indexes: {},
repository: {},
observers: [],
defaultLanguage: 'en',
currentLanguage: 'en',
contextIndex: {},
setDefaultLanguage: function (lang) {
this.defaultLanguage = lang;
},
MD5: function (string) {
function RotateLeft(lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
}
function AddUnsigned(lX, lY) {
var lX4,
lY4,
lX8,
lY8,
lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
}
function F(x, y, z) {
return (x & y) | ((~x) & z);
}
function G(x, y, z) {
return (x & z) | (y & (~z));
}
function H(x, y, z) {
return (x ^ y ^ z);
}
function I(x, y, z) {
return (y ^ (x | (~z)));
}
function FF(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
};
function GG(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
};
function HH(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
};
function II(a, b, c, d, x, s, ac) {
a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
return AddUnsigned(RotateLeft(a, s), b);
};
function ConvertToWordArray(string) {
var lWordCount,
lMessageLength = string.length,
lNumberOfWords_temp1 = lMessageLength + 8,
lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64,
lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16,
lWordArray = Array(lNumberOfWords - 1),
lBytePosition = 0,
lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
function WordToHex(lValue) {
var WordToHexValue = "",
WordToHexValue_temp = "",
lByte,
lCount;
for (lCount = 0; lCount <= 3; lCount += 1) {
lByte = (lValue >>> (lCount * 8)) & 255;
WordToHexValue_temp = "0" + lByte.toString(16);
WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
}
return WordToHexValue;
};
function Utf8Encode(string) {
var utftext = "",
n,
c;
string = string.replace(/\r\n/g, "\n");
for (n = 0; n < string.length; n += 1) {
c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
};
var x,
k,
AA,
BB,
CC,
DD,
a,
b,
c,
d,
S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22,
S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20,
S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23,
S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21,
temp;
string = Utf8Encode(string);
x = ConvertToWordArray(string);
a = 0x67452301;
b = 0xEFCDAB89;
c = 0x98BADCFE;
d = 0x10325476;
for (k = 0; k < x.length; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = AddUnsigned(a, AA);
b = AddUnsigned(b, BB);
c = AddUnsigned(c, CC);
d = AddUnsigned(d, DD);
}
temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);
return temp.toLowerCase();
}
};
I18N.setDefaultLanguage = function (lang) {
this.defaultLanguage = lang;
if (this.repository[lang]) {
this.loadDefaultLanguege(this.repository[lang]);
}
return this;
};
I18N.loadLanguage = function (data, lang, loaded) {
if (typeof data != 'object') {
throw new Error("loadLanguage(): the first parameter is not valid, should be 'object'");
}
if (typeof lang != 'string') {
throw new Error("loadLanguage(): the second parameter is not valid, should be 'string'");
}
if (this.defaultLanguage == lang) {
this.loadDefaultLanguege(data);
}
if (!loaded) {
this.repository[lang] = data;
}
return this;
};
I18N.loadDefaultLanguege = function (data) {
var label,
value;
this.indexes = {};
this.contextIndex = {};
for (label in data) {
if (!this.indexes[data[label]]) {
this.indexes[data[label]] = label;
} else {
if (!this.contextIndex[data[label]]) {
value = this.indexes[data[label]];
this.contextIndex[data[label]] = {};
this.contextIndex[data[label]]['0'] = value;
this.contextIndex[data[label]]['1'] = label;
} else {
var n = this.getSizeJson(this.contextIndex[data[label]]);
this.contextIndex[data[label]][n.toString()] = label;
}
}
}
return this;
};
I18N.getSizeJson = function (json) {
var size = 0,
i;
if (typeof json == 'object') {
for (i in json) {
size += 1;
}
return size;
} else {
throw new Error('the parameter is not a JSON');
}
return this;
};
I18N.setCurrentLanguage = function (lang) {
this.currentLanguage = lang;
if (this.repository[lang]) {
this.loadLanguage(this.repository[lang], lang, true);
}
return this;
};
I18N.translate = function (variablesLabels) {
var translation = String(this),
index,
i;
index = I18N.indexes[this];
if (index && I18N.repository[I18N.currentLanguage][index]) {
translation = I18N.repository[I18N.currentLanguage][index];
}
if (variablesLabels) {
for (i = 0; i < variablesLabels.length; i += 1) {
translation = translation.replace("{" + i + "}", String(variablesLabels[i]));
}
}
return translation;
};
I18N.translateContext = function (value) {
var translation,
index,
label;
if (typeof value != 'number') {
for (label in I18N.contextIndex[this]) {
if (I18N.contextIndex[this][label] == value) {
index = I18N.contextIndex[this][label];
}
}
} else {
index = I18N.contextIndex[this][value.toString()];
}
if (index) {
translation = I18N.repository[I18N.currentLanguage][index];
return translation;
}
return String(this);
};
// Declarations created to instantiate in NodeJS environment
if (typeof exports !== 'undefined') {
module.exports = I18N;
}
PMUI.extendNamespace('PMUI.lang.I18N', I18N);
}());
PMUI.init();