include("lib.prototype");
if (!modularjs.loaded["lib.bsharptree"]) {
var BSharpTree = Class.create( {
root: null,
initialize: function(comparator) {
this.root = $A();
this.comparator = comparator || function(x) {
return x
};
},
add: function(text, object, node) {
var node = node || this.root;
node.objects = node.objects || $A();
if (node != this.root) {
node.objects.push(object);
}
if (text.length > 0) {
var chr = text.charCodeAt(0) - 32;
var next = node[chr] = chr == 0 ? this.root : node[chr] || $A();
this.add(text.substring(1), object, next);
}
},
getAll: function() {
var node = this.root;
var result = [];
node.forEach(function(r) {
if(r !== undefined) {
r.objects.forEach(function(d) {
result.push(d);
});
}
});
return result;
},
getAllSingle: function() {
var node = this.root;
var result = [];
node.forEach(function(r) {
if(r !== undefined) {
r.objects.forEach(function(d) {
if(result.indexOf(d) === -1) {
result.push(d);
}
});
}
});
return result;
},
search: function(text, objects) {
var node = this.root;
var result = null;
while (text.length > 0) {
var chr = text.charCodeAt(0) - 32;
text = text.substring(1);
if (chr == 0) {
result = this.search(text, result);
break;
} else {
node = node[chr];
if (!node) {
return $A();
}
result = node.objects;
}
}
if (objects) {
if (node == this.root) {
return objects;
} else {
return this.intersect(result, objects);
}
} else {
if (node == this.root) {
return $A();
} else {
return result;
}
}
},
intersect: function(a, b) {
var i = 0;
var j = 0
var intersect = $A();
a = a.sortBy(this.comparator);
b = b.sortBy(this.comparator);
while (i < a.length && j < b.length) {
if (this.comparator(a[i]) == this.comparator(b[j])) {
intersect.push(a[i]);
i++;
j++;
} else if (this.comparator(a[i]) > this.comparator(b[j])) {
j++;
} else {
i++;
}
}
return intersect;
}
});
}
modularjs.loaded["lib.bsharptree"] = true;
if (!modularjs.loaded["lib.nprogress"]) {
/*! NProgress (c) 2013, Rico Sta. Cruz
* http://ricostacruz.com/nprogress */
;(function(factory) {
if (typeof module === 'function') {
module.exports = factory(this.jQuery || require('jquery'));
} else {
this.NProgress = factory(this.jQuery);
}
})(function($j) {
var NProgress = {};
NProgress.version = '0.1.2';
var Settings = NProgress.settings = {
minimum: 0.08,
easing: 'ease',
positionUsing: '',
speed: 200,
trickle: true,
trickleRate: 0.02,
trickleSpeed: 800,
showSpinner: true,
template: '
'
};
/**
* Updates configuration.
*
* NProgress.configure({
* minimum: 0.1
* });
*/
NProgress.configure = function(options) {
$j.extend(Settings, options);
return this;
};
/**
* Last number.
*/
NProgress.status = null;
/**
* Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
*
* NProgress.set(0.4);
* NProgress.set(1.0);
*/
NProgress.set = function(n) {
var started = NProgress.isStarted();
n = clamp(n, Settings.minimum, 1);
NProgress.status = (n === 1 ? null : n);
var $jprogress = NProgress.render(!started),
$jbar = $jprogress.find('[role="bar"]'),
speed = Settings.speed,
ease = Settings.easing;
$jprogress[0].offsetWidth; /* Repaint */
$jprogress.queue(function(next) {
// Set positionUsing if it hasn't already been set
if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();
// Add transition
$jbar.css(barPositionCSS(n, speed, ease));
if (n === 1) {
// Fade out
$jprogress.css({ transition: 'none', opacity: 1 });
$jprogress[0].offsetWidth; /* Repaint */
setTimeout(function() {
$jprogress.css({ transition: 'all '+speed+'ms linear', opacity: 0 });
setTimeout(function() {
NProgress.remove();
next();
}, speed);
}, speed);
} else {
setTimeout(next, speed);
}
});
return this;
};
NProgress.isStarted = function() {
return typeof NProgress.status === 'number';
};
/**
* Shows the progress bar.
* This is the same as setting the status to 0%, except that it doesn't go backwards.
*
* NProgress.start();
*
*/
NProgress.start = function() {
if (!NProgress.status) NProgress.set(0);
var work = function() {
setTimeout(function() {
if (!NProgress.status) return;
NProgress.trickle();
work();
}, Settings.trickleSpeed);
};
if (Settings.trickle) work();
return this;
};
/**
* Hides the progress bar.
* This is the *sort of* the same as setting the status to 100%, with the
* difference being `done()` makes some placebo effect of some realistic motion.
*
* NProgress.done();
*
* If `true` is passed, it will show the progress bar even if its hidden.
*
* NProgress.done(true);
*/
NProgress.done = function(force) {
if (!force && !NProgress.status) return this;
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
};
/**
* Increments by a random amount.
*/
NProgress.inc = function(amount) {
var n = NProgress.status;
if (!n) {
return NProgress.start();
} else {
if (typeof amount !== 'number') {
amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
}
n = clamp(n + amount, 0, 0.994);
return NProgress.set(n);
}
};
NProgress.trickle = function() {
return NProgress.inc(Math.random() * Settings.trickleRate);
};
/**
* (Internal) renders the progress bar markup based on the `template`
* setting.
*/
NProgress.render = function(fromStart) {
if (NProgress.isRendered()) return $j("#nprogress");
$j('html').addClass('nprogress-busy');
var $jel = $j("")
.html(Settings.template);
var perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0);
$jel.find('[role="bar"]').css({
transition: 'all 0 linear',
transform: 'translate3d('+perc+'%,0,0)'
});
if (!Settings.showSpinner)
$jel.find('[role="spinner"]').remove();
$jel.appendTo(document.body);
return $jel;
};
/**
* Removes the element. Opposite of render().
*/
NProgress.remove = function() {
$j('html').removeClass('nprogress-busy');
$j('#nprogress').remove();
};
/**
* Checks if the progress bar is rendered.
*/
NProgress.isRendered = function() {
return ($j("#nprogress").length > 0);
};
/**
* Determine which positioning CSS rule to use.
*/
NProgress.getPositioningCSS = function() {
// Sniff on document.body.style
var bodyStyle = document.body.style;
// Sniff prefixes
var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
('MozTransform' in bodyStyle) ? 'Moz' :
('msTransform' in bodyStyle) ? 'ms' :
('OTransform' in bodyStyle) ? 'O' : '';
if (vendorPrefix + 'Perspective' in bodyStyle) {
// Modern browsers with 3D support, e.g. Webkit, IE10
return 'translate3d';
} else if (vendorPrefix + 'Transform' in bodyStyle) {
// Browsers without 3D support, e.g. IE9
return 'translate';
} else {
// Browsers without translate() support, e.g. IE7-8
return 'margin';
}
};
/**
* Helpers
*/
function clamp(n, min, max) {
if (n < min) return min;
if (n > max) return max;
return n;
}
/**
* (Internal) converts a percentage (`0..1`) to a bar translateX
* percentage (`-100%..0%`).
*/
function toBarPerc(n) {
return (-1 + n) * 100;
}
/**
* (Internal) returns the correct CSS for changing the bar's
* position given an n percentage, and speed and ease from Settings
*/
function barPositionCSS(n, speed, ease) {
var barCSS;
if (Settings.positionUsing === 'translate3d') {
barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
} else if (Settings.positionUsing === 'translate') {
barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
} else {
barCSS = { 'margin-left': toBarPerc(n)+'%' };
}
barCSS.transition = 'all '+speed+'ms '+ease;
return barCSS;
}
return NProgress;
});
}
modularjs.loaded["lib.nprogress"] = true;
if (!modularjs.loaded["fitbank.util"]) {
if (!modularjs.loaded["lib.split"]) {
/*!
* Cross-Browser Split 1.1.1
* Copyright 2007-2012 Steven Levithan
* Available under the MIT License
* ECMAScript compliant, uniform cross-browser split method
*/
/**
* Splits a string into an array of strings using a regex or string separator. Matches of the
* separator are not included in the result array. However, if `separator` is a regex that contains
* capturing groups, backreferences are spliced into the result each time `separator` is matched.
* Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably
* cross-browser.
* @param {String} str String to split.
* @param {RegExp|String} separator Regex or string to use for separating the string.
* @param {Number} [limit] Maximum number of items to include in the result array.
* @returns {Array} Array of substrings.
* @example
*
* // Basic use
* split('a b c d', ' ');
* // -> ['a', 'b', 'c', 'd']
*
* // With limit
* split('a b c d', ' ', 2);
* // -> ['a', 'b']
*
* // Backreferences in result array
* split('..word1 word2..', /([a-z]+)(\d+)/i);
* // -> ['..', 'word', '1', ' ', 'word', '2', '..']
*/
var split;
// Avoid running twice; that would break the `nativeSplit` reference
split = split || function (undef) {
var nativeSplit = String.prototype.split,
compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group
self;
self = function (str, separator, limit) {
// If `separator` is not a regex, use `nativeSplit`
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
return nativeSplit.call(str, separator, limit);
}
var output = [],
flags = (separator.ignoreCase ? "i" : "") +
(separator.multiline ? "m" : "") +
(separator.extended ? "x" : "") + // Proposed for ES6
(separator.sticky ? "y" : ""), // Firefox 3+
lastLastIndex = 0,
// Make `global` and avoid `lastIndex` issues by working with a copy
separator = new RegExp(separator.source, flags + "g"),
separator2, match, lastIndex, lastLength;
str += ""; // Type-convert
if (!compliantExecNpcg) {
// Doesn't need flags gy, but they don't hurt
separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
}
/* Values for `limit`, per the spec:
* If undefined: 4294967295 // Math.pow(2, 32) - 1
* If 0, Infinity, or NaN: 0
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
* If other: Type-convert, then use the above rules
*/
limit = limit === undef ?
-1 >>> 0 : // Math.pow(2, 32) - 1
limit >>> 0; // ToUint32(limit)
while (match = separator.exec(str)) {
// `separator.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0].length;
if (lastIndex > lastLastIndex) {
output.push(str.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if (!compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undef) {
match[i] = undef;
}
}
});
}
if (match.length > 1 && match.index < str.length) {
Array.prototype.push.apply(output, match.slice(1));
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if (output.length >= limit) {
break;
}
}
if (separator.lastIndex === match.index) {
separator.lastIndex++; // Avoid an infinite loop
}
}
if (lastLastIndex === str.length) {
if (lastLength || !separator.test("")) {
output.push("");
}
} else {
output.push(str.slice(lastLastIndex));
}
return output.length > limit ? output.slice(0, limit) : output;
};
// For convenience
String.prototype.split = function (separator, limit) {
return self(this, separator, limit);
};
return self;
}();
}
modularjs.loaded["lib.split"] = true;
/**
* Namespace Util - Define funciones utilitarias.
*/
var Util = {
//Selectores para encontrar los elementos candidatos a verificar si su valor
//ha cambiado, pueden agregarse mas filtros
selectors: [
"input:not([readonly='']), select:not([readonly='']), textarea:not([readonly=''])",
"[type!='hidden']",
".record, [class*='control']"
],
getCaret: function(elemento) {
var caret = '';
return caret;
},
//Aplicar selectores para encontrar campos candidatos a revisar cambios
applySelectors: function() {
var target = new Element("input");
var first = true;
this.selectors.each(function(selector) {
if (first) {
target = c.form;
first = false;
}
var selected = Prototype.Selector.select(selector, target);
target = new Element("input");
selected.each(function(el) {
if(el.type === "select-one") {
var tmpInput = new Element("input", {
id: el.id,
"class": el.className
});
tmpInput.value = el.options[el.selectedIndex].value;
target.appendChild(tmpInput);
} else if(el.type === "checkbox") {
var tmpInput = new Element("input", {
id: el.id,
"class": el.className
});
tmpInput.value = el.value;
target.appendChild(tmpInput);
} else {
target.appendChild(el.clone(true));
}
});
});
return target.childElements();
},
//Obtener el estatos actual de campos y valores en el formulario
getOriginalElements: function() {
var elements = {};
var targetElements = Util.applySelectors();
targetElements.each(function(e) {
elements[e.id] = e.value;
});
return elements;
},
//Revisar si hay cambios en los elementos candidatos, segun su estado original
checkForChanges: function(originalElements) {
if (!originalElements || !c.form || !c.formulario) {
return false;
}
var changes = false;
var currentElements = {};
var targetElements = Util.applySelectors();
targetElements.each(function(e) {
if(e.type !== "select-one") {
currentElements[e.id] = e.value;
} else {
currentElements[e.id] = e.options[e.selectedIndex].text;
}
});
$H(originalElements).each(function(pair) {
if (currentElements[pair.key]) {
var check = pair.value !== currentElements[pair.key];
changes = changes || check;
}
});
return changes;
},
/**
* Función que obtiene el valor del nombre de la clase css.
*
* @param className
* Valor del nombre de la clase css.
*/
getStyleClass: function(className) {
for ( var s = 0; s < document.styleSheets.length; s++) {
if (document.styleSheets[s].rules) {
for ( var r = 0; r < document.styleSheets[s].rules.length; r++) {
if (document.styleSheets[s].rules[r].selectorText == '.' + className) {
return document.styleSheets[s].rules[r];
}
}
} else if (document.styleSheets[s].cssRules) {
for ( var r = 0; r < document.styleSheets[s].cssRules.length; r++) {
if (document.styleSheets[s].cssRules[r].selectorText == '.' + className) {
return document.styleSheets[s].cssRules[r];
}
}
}
}
return null;
},
encodeHTML: function(string) {
return string.replace(/&/g, "&").replace(//g, ">");
},
firstUpperOtherLower: function(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
},
getContentWindow: function(iframe) {
iframe = $(iframe);
return iframe.contentWindow || iframe;
},
getMensaje: function(mensajes, msg) {
var mobileMsg = msg + "_MOBILE";
if(window.Mobile && mensajes[mobileMsg]) {
return mensajes[mobileMsg];
} else {
return mensajes[msg];
}
},
/**
* Inicializa un elemento para que use un oculto.
*/
initHtmlElement: function(elementId, reverse) {
var elemento = $(elementId);
var oculto = $(elementId + "_oculto");
if (reverse && reverse == "1") {
oculto.widget = oculto;
oculto.oculto = oculto;
} else {
oculto.widget = elemento;
oculto.oculto = oculto;
}
elemento.oculto = oculto;
elemento.widget = elemento;
oculto.hide = Element.hideCurry(elemento);
oculto.show = Element.showCurry(elemento);
},
/**
* Inicializa un elemento para que use un oculto.
*/
initHiddenInput: function(elemento, suffix) {
elemento = $(elemento);
if (!elemento) {
return;
}
elemento.sync = function(oculto, e) {
elemento.changeValue(oculto.value, e && e.options || e);
};
elemento.setValueOculto = function (oculto) {
oculto.changeValue(elemento.value);
};
Util._initHiddenInput(elemento, suffix);
},
/**
* Inicializa un checkbox para que use un oculto.
*/
initCheckBox: function(elemento) {
elemento = $(elemento);
elemento.valueOn = elemento.getAttribute("value-on");
elemento.valueOff = elemento.getAttribute("value-off");
elemento.hide = Element.hideCurry(elemento.next("label"));
elemento.show = Element.showCurry(elemento.next("label"));
elemento.sync = function(e) {
elemento.setChecked(e.target.value == elemento.valueOn, e && e.options || e);
};
elemento.fixValue = function() {
var value = elemento.checked ? elemento.valueOn : elemento.valueOff;
elemento.value = value;
return value;
};
elemento.setValueOculto = function (oculto) {
oculto.value = elemento.fixValue();
};
Util._initHiddenInput(elemento, "checkbox");
},
/**
* Inicializa un combobox para que use un oculto.
*/
initComboBox: function(elemento) {
elemento = $(elemento);
elemento.on("keyup", function(e) {
elemento.fireDOMEvent("change", {
generated: false
});
});
elemento.sync = function(e) {
for (var i = 0 ; i < elemento.options.length ; i++) {
if (e.target && e.target.value == elemento.options[i].value) {
elemento.selectedIndex = i;
break;
}
}
};
elemento.setValueOculto = function (oculto) {
oculto.value = elemento.options[elemento.selectedIndex].value;
};
Util._initHiddenInput(elemento, "combobox");
},
/**
* Inicializa un elemento para que use un oculto.
* Se sincronizan los cambios efectuados por evento onChange
*/
_initHiddenInput: function(elemento, suffix) {
var name = elemento.name;
elemento.name = name + "_" + (suffix || "widget");
var oculto = new Element("input", {
type: "hidden",
name: name,
registro: elemento.registro
});
elemento.insert( {
before: oculto
});
oculto.oculto = oculto;
elemento.oculto = oculto;
oculto.widget = elemento;
elemento.widget = elemento;
oculto.hide = Element.hide.curry(elemento);
oculto.show = Element.show.curry(elemento);
if (suffix) {
oculto[suffix] = elemento;
elemento[suffix] = elemento;
}
if (elemento.sync) {
oculto.on("change", elemento.sync.curry(oculto));
oculto.on("widget:init", elemento.sync.curry(oculto, {
load: true
}));
}
if (elemento.setValueOculto) {
elemento.on("change", elemento.setValueOculto.curry(oculto));
elemento.setValueOculto(oculto);
}
return oculto;
},
/**
* Inicializa una tabla para que tenga scroll.
*/
initTableScroll: function(elemento, rows, horizontal) {
if (Mobile) {
return;
}
elemento = $(elemento);
if (elemento.visible()) {
Tabs.removeListener(elemento.scrollHandler);
Util._initTableScroll(elemento, rows, horizontal);
c.resize(elemento);
} else if (!elemento.scrollHandler) {
elemento.scrollHandler = Util.initTableScroll.curry(elemento, rows,
horizontal);
Tabs.addListener(elemento.scrollHandler);
}
},
_initTableScroll: function(elemento, rows, horizontal) {
var div = elemento.down("div");
if (div && !div.hasClassName('*-container')) {
div = elemento;
}
var table = elemento.down("table");
var thead = table.down("thead");
var tbody = table.down("tbody");
var tfoot = table.down("tfoot");
var tr = tbody && tbody.down("tr");
var borders = 2;
if (!tbody || !tr || (div && div.className.match('dynamic-*').length > 0)) {
return;
}
if (!horizontal) {
var resize = function(td) {
var w = parseInt(td.getStyle("width"));
td.setStyle("width:" + w + "px");
};
table && table.select("th, td").each(resize);
var totalHeight = (((thead && thead.getHeight() || 0) + 4) + (rows * ((tr && tr.getHeight() || 0) + borders)));
div && div.setStyle({
display: "block",
overflowY: "auto",
overflowX: "auto",
height: totalHeight + "px"
});
thead && thead.setStyle({
display: "block"
});
tbody && tbody.setStyle({
display: "block"
});
tfoot && tfoot.setStyle({
display: "block"
});
} else if (horizontal) {
// TODO implementar scroll horizontal
} else {
var height = rows * tr.getHeight()
+ (thead ? thead.getHeight() : 0)
+ (tfoot ? tfoot.getHeight() : 0);
elemento.setStyle({
overflowY: "auto",
overflowX: "hidden",
paddingRight: "20px",
height: height + "px"
});
}
},
initDeleteRecord: function(name) {
c.$N(name).each(function(elemento) {
if (elemento.widget) {
elemento = elemento.widget;
}
elemento.setDisabled(true);
elemento.on("change", function(e) {
var tr = elemento.up("td").up("tr");
if (elemento.checked) {
tr.addClassName("delete-record");
} else {
tr.removeClassName("delete-record");
}
c.formulario.evalFormulas(elemento);
c.calcular();
});
});
},
generarIdUnicoTemporal: function() {
// IMPORTANTE: Mantener sincronizado con Servicios.java
var id = "_id_";
id += Math.round(Math.random() * 999);
id += "_";
id += Math.round(Math.random() * 999);
id += "_";
id += Math.round(Math.random() * 999);
return id;
},
generarIdUnicoPermanente: function() {
// IMPORTANTE: Mantener sincronizado con Servicios.java
return "ID_" + Util.generarIdUnicoTemporal();
},
eliminarReferenciasCirculares: function(obj) {
var seen = {};
function isPrimitive(obj) {
var t = typeof obj;
return !obj || t == 'string' || t == 'number' || t == 'boolean';
}
function deCycle(obj) {
var deCycled;
if (!isPrimitive(obj)) {
seen[obj] = true;
}
if (obj instanceof Array) {
deCycled = [];
for (var i = 0; i < obj.length; i += 1) {
if (!seen[obj[i]]) {
deCycled[i] = deCycle(obj[i]);
}
}
} else if (typeof obj == 'object' && obj) {
deCycled = {};
for (var k in obj) {
if (obj.hasOwnProperty(k) && !seen[obj[k]]) {
try {
deCycled[k] = deCycle(obj[k]);
} catch(e) {
// no hacer nada
}
}
}
} else {
deCycled = obj;
}
return deCycled;
}
return deCycle(obj);
},
clean: function(obj) {
if (!obj) {
return;
}
try {
obj.initialize = null;
obj.constructor = null;
delete obj.initialize;
delete obj.constructor;
} catch(e) {
// no hacer nada
}
return obj;
},
isError: function(codigo) {
// Esto debe ser igual a ManejoExcepcion.isError()
return codigo && !/0|.*-0|ok-.*/i.test(codigo);
},
/**
* Hace un elemento que sea movible.
*
* @param movableElement El elemento movible
* @param handlerElement El elemento que puede mover
*/
makeMovable: function(movableElement, handlerElement) {
handlerElement.unselectable = "on";
handlerElement.onselectstart = function() { return false };
handlerElement.style.userSelect = handlerElement.style.MozUserSelect = "none";
handlerElement.on('mousedown', function(e) {
e = $E(e);
var initX = e.X;
var initY = e.Y;
var divx = movableElement.style.left;
var divy = movableElement.style.top;
var divLeft = divx.substr(0, divx.indexOf('px')) * 1;
var divTop = divy.substr(0, divy.indexOf('px')) * 1;
var divNewLeft = initX - divLeft;
var divNewTop = initY - divTop;
handlerElement.moveHandler = Event.on(document.body, 'mousemove', function(e) {
e = $E(e);
movableElement.style.left = Math.max(0, e.X - divNewLeft) + 'px';
movableElement.style.top = Math.max(0, e.Y - divNewTop) + 'px';
});
});
handlerElement.on('mouseup', function(e) {
e = $E(e);
if (handlerElement.moveHandler) {
handlerElement.moveHandler.stop();
}
});
}
};
/**
* Extensiones para elementos de html
*/
Element.addMethods({
ensureVisible: function(element) {
element = $(element);
var parent = element.parentNode;
if (parent) {
var parentPosition = parent.positionedOffset().top;
var minScroll = parentPosition + parent.scrollTop;
var maxScroll = parentPosition + parent.scrollTop
+ parent.getHeight();
var position = element.positionedOffset().top;
if (position < minScroll) {
parent.scrollTop = position - parentPosition;
} else if (position + element.getHeight() > maxScroll) {
parent.scrollTop = position + element.getHeight()
- parent.getHeight() - parentPosition;
}
}
},
ensureInside: function(element, parent) {
element = $(element);
parent = ($(parent) || document.viewport);
var d = element.getDimensions();
var vd = parent.getDimensions();
var p = element.viewportOffset();
var diff = vd.height - p.top - d.height;
if (diff < 0) {
element.style.top = (parseInt("0" + element.style.top, 10) + diff) + "px";
}
p = element.viewportOffset();
diff = p.top;
if (diff < 0) {
element.style.top = (parseInt("0" + element.style.top, 10) - diff) + "px";
}
p = element.viewportOffset();
diff = vd.width - p.left - d.width;
if (diff < 0) {
element.style.left = (parseInt("0" + element.style.left, 10) + diff) + "px";
}
p = element.viewportOffset();
diff = p.left;
if (diff < 0) {
element.style.left = (parseInt("0" + element.style.left, 10) - diff) + "px";
}
},
center: function(element, parent) {
parent = parent || element.getOffsetParent();
var top = (parent.getHeight() - element.getHeight()) / 2;
var left = (parent.getWidth() - element.getWidth()) / 2;
if (top < 0) {
top = 0;
}
if (left < 0) {
left = 0;
}
element.absolutize();
element.setStyle( {
top: top + "px",
left: left + "px"
});
},
relativize2: function(element) {
element = $(element);
var pos = element.positionedOffset();
element.relativize();
var newPos = element.positionedOffset();
var dx = pos[0] - newPos[0];
var dy = pos[1] - newPos[1];
element.moveMargin(dx, dy);
element.nextSiblings().each(function(sibling) {
if (element.visible()) {
sibling.moveMargin(dx, dy);
}
});
},
moveMargin: function(element, dx, dy) {
if (dx != 0) {
element.style.marginLeft = (parseInt(element.style.marginLeft) + dx) + "px";
}
if (dy != 0) {
element.style.marginTop = (parseInt(element.style.marginTop) + dy) + "px";
}
},
fireDOMEvent: function(element, event, options) {
element = $(element);
options = Object.extend({
generated: true
}, options);
if (document.createEventObject){
// dispatch para IE
var evt = document.createEventObject();
evt.options = options;
return element.fireEvent('on' + event, evt)
} else{
// dispatch para firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true); // event type,bubbling,cancelable
evt.options = options;
return !element.dispatchEvent(evt);
}
},
getOriginalDimensions: function(element) {
element = $(element);
var currentW;
var currentH;
if (element.tagName == "IMG") {
currentW = element.width;
currentH = element.height;
} else {
currentW = element.getWidth();
currentH = element.getHeight();
}
element.removeAttribute("width");
element.removeAttribute("height");
var h = element.getHeight();
var w = element.getWidth();
if (element.tagName == "IMG") {
element.width = currentW;
element.height = currentH;
} else {
element.setStyle({
width: currentW + "px",
height: currentH + "px"
});
}
return {width: w, height: h};
},
hide: function(element) {
element = $(element);
element.style.display = 'none';
var nextElement = element.next();
if (nextElement && nextElement.hasClassName("asistente-icono")) {
nextElement.style.display = 'none';
}
return element;
},
show: function(element) {
element = $(element);
element.style.display = '';
var nextElement = element.next();
if (nextElement && nextElement.hasClassName("asistente-icono")) {
nextElement.style.display = '';
}
return element;
},
// Metodos para optimizar carga de formularios solamente
hideCurry: function(element) {
return function() { element.hide(); };
},
showCurry: function(element) {
return function() { element.show(); };
},
syncCurry: function(element) {
return function(e) { element.sync(e); };
},
setValueOcultoCurry: function(element, oculto) {
return function() { element.setValueOculto(oculto); };
}
});
/**
* Estos metodos sirven para elementos de tipo Form.Element aunque hay que
* registrarlos en Element directamente
*/
Element.addMethods({
/**
* Cambia el valor de un elemento de un formulario.
*
* @param element Elemento donde se cambia el valor
* @param newValue Nuevo valor del elemento
* @param options Opciones donde se especifica si el evento es parcial
*/
changeValue: function(element, value, options) {
if (element.nodeName == "SPAN" || element.nodeName == "LABEL") {
var originalValue = element.innerHTML;
if (value != originalValue) {
element.update(value);
return true;
} else {
return false;
}
}
var originalValue = element.value;
element._formatValue(value, options);
if (originalValue != element.value) {
element.fireDOMEvent("change", options);
return true;
} else {
return false;
}
},
_processValue: function(element, options) {
element = $(element);
var originalValue = element.value;
var cambios = false;
if (element.value != "") {
Validar.ok(element, "required");
} else {
Validar.ok(element, "empty");
}
element._formatValue(element.value, options);
if (originalValue != element.value) {
if (!options.partial) {
element.fireDOMEvent("change", options);
}
cambios = true;
}
c.formulario.evalFormulas(element);
return cambios;
},
_formatValue: function(element, value, options) {
var newValue = value;
var className = null;
options = options || {};
try {
// Formatear valor con todos los formateadores
(element.formatters || $A()).each(function(formatter) {
className = formatter.constructor.simpleClassName;
newValue = formatter.transform(newValue, options.partial);
Validar.ok(element, className);
});
} catch(e) {
Validar.error(element, e, className);
return element.value;
}
newValue = typeof newValue != "undefined" && newValue != null && newValue.toString() || "";
if (element.value != newValue) {
var pos = element.value.length - element.getCaretPosition();
//Hack temporal. Ver Incidencia #8266, bug Chrome #152537
element.value = "";
element.value = newValue;
element.setCaretPosition(element.value.length - pos, false);
}
(element.formatters || $A()).each(function(formatter) {
formatter.changeValues(element, options.partial);
});
},
/**
* Obtiene el objectValue o value adecuadamente
*
* @param element Elemento de donde obtener el objectValue
*/
getObjectValue: function(element) {
element = $(element);
return typeof element.objectValue != "undefined" ? element.objectValue : element.value;
},
/**
* Obtiene el elemento visible de un objeto HTML
*
* @param element Elemento de referencia
*/
getWidget: function(element) {
element = $(element);
return typeof element.widget != "undefined" ? element.widget : element;
},
getCaretPosition: function(element) {
element = $(element);
if (!element.focused) {
return -1;
}
try {
if (element.type === "date" || element.type === "number") {
return element.value.length;
}
if (typeof document.selection != "undefined") {
// IE
var sel = document.selection.createRange();
sel.moveStart('character', -element.value.length);
return sel.text.length;
} else if (typeof element.selectionStart != "undefined") {
// Otros browsers
return element.selectionStart;
}
} catch(e) {
console.log("No se puede obtener la posicion en el texto: " + e);
}
return element.value.length;
},
setCaretPosition: function(element, pos, focus) {
element = $(element);
if (!element.focused && !focus) {
return;
} else {
element.focus();
}
if (element.type === "date" || element.type === "number") {
return;
}
if (typeof element.createTextRange != "undefined") {
// IE
var range = element.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
} else if (typeof element.setSelectionRange != "undefined") {
// Otros browsers
element.setSelectionRange(pos, pos);
}
},
setChecked: function(elemento, checked, options) {
if (!Object.isElement(elemento)) {
elemento = c.$(elemento);
}
if (elemento.widget) {
elemento = elemento.widget;
}
elemento.checked = checked;
elemento.fireDOMEvent("change", options);
},
setDisabled: function(elemento, disabled) {
if (!Object.isElement(elemento)) {
elemento = c.$(elemento);
}
if (elemento.widget) {
elemento = elemento.widget;
}
if (elemento.type != "checkbox" && elemento.type != "radio"
&& elemento.type != "image" && elemento.type != "select-one"
&& elemento.type != "button") {
elemento.readOnly = disabled;
} else {
elemento.disabled = disabled;
}
if (elemento.hasDatePicker) {
datePickerController[disabled ? "disable" : "enable"](elemento.id);
}
elemento.tabIndex = disabled ? -1 : elemento.originalTabIndex;
},
getScrollFromBottom: function(elemento) {
return elemento.scrollHeight - elemento.clientHeight - elemento.scrollTop;
}
});
String.prototype.hashCode = function(){
var hash = 0;
for (var i = 0; i < this.length; i++) {
hash = 31 * hash + this.charCodeAt(i);
hash = hash & hash;
}
return hash;
};
String.prototype.capitalizeFirstLetterWords = function() {
var l = this.split(" ");
var result = "";
var counter = 0;
l.map(function(i) {
result = result + Util.firstUpperOtherLower(i) + (counter === l.lenght ? "" : " ");
});
return result;
};
/**
* Obtiene un array de elementos dado su name. Los parametros pueden ser así:
*
* $N(name) => Obtiene un array de elementos buscando en toda la página
* $N(name, index) => Obtiene el elemento indicado del array
* $N(base, name) => Obtiene un elemento con el name indicado dentro de la base
* $N(base, name, index) => Obtiene el elemento indicado del array
*
* @param a Ver arriba.
* @param b Ver arriba.
* @param c Ver arriba.
*/
var $N = function(a, b, c) {
var base = null;
var index = null;
var name = null;
if (typeof c == "number") {
base = a;
name = b;
index = c;
} else if (typeof b == "number") {
base = document;
name = a;
index = b;
} else if (a && (a.querySelectorAll || base.getElementsByName || a.elements || a.select)) {
base = a;
name = b;
} else {
base = document;
name = a;
}
var isNumber = (typeof index == "number");
if (!name) {
return isNumber ? null : [];
}
var elements = null;
if (base.querySelectorAll) {
// Nativo rapido
if (isNumber) {
return base.querySelector("#c_" + name + "_" + index);
}
elements = $A(base.querySelectorAll("*[name='" + name + "']"));
} else if (base.getElementsByName) {
// También rápido
elements = $A(base.getElementsByName(name));
} else if (base.elements) {
// Optimizado
elements = $A();
var els = base.elements;
for (var i = 0; i < els.length; i++) {
if (els[i].name == name) {
elements.push(els[i]);
}
}
} else {
// Mas lento
elements = base.select("*[name='" + name + "']");
}
return isNumber ? elements[index] : elements;
};
/**
* Obtiene un array de valores de un elemento dado su name.
*
* @param i, j, k se pasan directo a $N
*/
var $V = function(i, j, k) {
var n = $N(i, j, k);
return Object.isArray(n) ? n.invoke("getObjectValue") : n && n.getObjectValue();
};
/**
* Obtiene un array de widgets visibles de un elemento dado su name.
*
* @param i, j, k se pasan directo a $N
*/
var $W = function(i, j, k) {
var n = $N(i, j, k);
return Object.isArray(n) ? n.invoke("getWidget") : n && n.getWidget();
};
var trim = function(s) {
Logger.trace("Usar mejor s.strip en vez de trim");
return s.strip();
};
var recargar = function(modulo) {
modularjs.loading[modulo] = false;
modularjs.loaded[modulo] = false;
include(modulo);
};
var rethrow = function(transport, e) {
console && console.log("Exception", transport, e, arguments);
Estatus.mensaje(e && e.message || Util.getMensaje(Mensajes, 'fitbank.validar.ERROR'),
e && e.stack, "error");
throw e;
};
var deepCopy = function(object) {
if (!object || typeof object != "object") {
return object;
} else if (Object.isArray(object)) {
var copy = $A();
object.each(function(value) {
copy.push(deepCopy(value));
});
return copy;
} else {
var copy = new object.constructor();
$H(object).each(function(pair) {
copy[pair.key] = deepCopy(pair.value);
});
return copy;
}
};
var copyConstructor = function(params) {
if (this.id && this.id.startsWith("_id_")) {
this.id = Util.generarIdUnicoTemporal();
}
$H(this).each(function(pair) {
this[pair.key] = deepCopy(pair.value);
}, this);
if (params) {
Object.extend(this, params);
}
if (this.constructor.className) {
this.className = this.constructor.className;
this.simpleClassName = this.constructor.simpleClassName;
}
};
var caducar = function () {
window.onbeforeunload = null;
document.location.href = "caducidad.html#cerrar";
}
Object.extend(Function.prototype, (function() {
var slice = Array.prototype.slice;
function tryCatch() {
var args = slice.call(arguments, 0);
try {
return this.apply(this, args);
} catch (e) {
Estatus.mensaje(e, e && e.stack, 'error');
throw e;
}
}
function bindTryCatch() {
var args = slice.call(arguments, 0);
var f = this.bind.apply(this, args);
return f.tryCatch.bind(f);
}
return {
tryCatch: tryCatch,
bindTryCatch: bindTryCatch,
tryDefer: tryCatch.defer
}
})());
var tryEval = function(code) {
return (function() {
eval(code);
}).tryCatch();
};
var tryEvent = function(nameOrId, event, func) {
var elements = c.$N(nameOrId) || [ $(nameOrId) ];
elements.each(function(element) {
if (element.widget) {
element = element.widget;
}
element.on(event, func.bindTryCatch(element));
});
};
Ajax.Request.addMethods({
abort: function() {
this.transport.onreadystatechange = Prototype.emptyFunction;
this.transport.abort();
if (Ajax.activeRequestCount > 0) {
Ajax.activeRequestCount--;
}
}
});
document.disableContextMenu = function() {
if (this.captureEvents) {
this.captureEvents(Event.MOUSEDOWN);
this.onmousedown = function(e) {
if (e.which == 2 || e.which == 3) {
return false;
}
};
}
this.oncontextmenu = function() {
return false;
};
};
}
modularjs.loaded["fitbank.util"] = true;
if (!modularjs.loaded["fitbank.evento"]) {
/**
* Inicializa el evento si no está inicializado y lo devuelve.
*/
$E = function(e) {
// Si está extendido, simplemente devolverlo
if (window.event && window.event.inicializado) {
return window.event.evento;
} else if (e && e.inicializado) {
return e.evento;
}
// Si se pasó un objeto tipo Evento usar ese.
var evento = e && e.evento ? e : e && new Evento(e);
if (evento) {
evento.evento.evento = evento;
}
return evento;
};
/**
* Clase Evento - Clase que contiene los manejadores de eventos disparados desde
* los elementos. Se recomienda usar mejor el método $E en lugar de esta clase
* directamente.
*/
var Evento = Class.create( {
/**
* Es el evento en si
*/
evento: null,
/**
* Contiene la tecla si se apalastó una tecla
*/
tecla: null,
/**
* Verdadero si se aplastó alt
*/
alt: null,
/**
* Verdadero si se aplastó ctrl
*/
ctrl: null,
/**
* Verdadero si se aplastó shift
*/
shift: null,
/**
* Contiene el caracter correspondiente a la tecla
*/
caracter: null,
/**
* Contiene el elemento que lanzó el evento
*/
elemento: null,
/**
* Contiene la coordenada en x del elemento que lanzó el evento
*/
x: null,
/**
* Contiene la coordenada en y del elemento que lanzó el evento
*/
y: null,
/**
* @private
*/
initialize: function(e, debug) {
// capturar el evento para todos los navegadores
// capturar tecla del evento
if (window.event) {
this.evento = window.event; // IE
this.tecla = window.event.keyCode;
} else {
this.evento = e;
this.tecla = e.which;
}
this.initTeclas();
switch (this.tecla) {
case this.SLASH:
this.caracter = '/';
break;
case this.DOT:
this.caracter = '.';
break;
case this.DASH:
this.caracter = '-';
break;
case this.BACKSLASH:
this.caracter = '\\';
break;
default:
if (!this.esEspecial()) {
this.caracter = String.fromCharCode(this.tecla);
} else {
this.caracter = 13;
}
break;
}
this.alt = this.evento.altKey;
this.ctrl = this.evento.ctrlKey;
this.shift = this.evento.shiftKey;
// capturar posición del evento
if (this.evento.layerX && this.evento.layerY) {
this.x = this.evento.layerX;
this.y = this.evento.layerY;
} else {
this.x = this.evento.offsetX;
this.y = this.evento.offsetY;
}
if (window.scrollX && window.scrollY) {
this.X = this.evento.clientX + window.scrollX;
this.Y = this.evento.clientY + window.scrollY;
} else {
this.X = this.evento.clientX + document.documentElement.scrollLeft
+ document.body.scrollLeft;
this.Y = this.evento.clientY + document.documentElement.scrollTop
+ document.body.scrollTop;
}
// capturar origen del evento
if (this.evento.srcElement) {
this.elemento = this.evento.srcElement;
} else if (this.evento.target) {
this.elemento = this.evento.target;
} else {
Logger.warning("No hay elemento que genere el evento!!!!");
}
if (debug) {
Logger.debug(this.toString());
}
},
toString: function() {
var string = '';
string += 'Evento\n';
string += 'evento:' + this.evento + '\n';
string += 'tecla:' + this.tecla + '\n';
string += 'caracter:' + this.caracter + '\n';
string += 'alt:' + this.alt + '\n';
string += 'ctrl:' + this.ctrl + '\n';
string += 'shift:' + this.shift + '\n';
string += 'elemento:' + this.elemento + '\n';
string += 'x:' + this.x + '\n';
string += 'y:' + this.y + '\n';
string += 'X:' + this.X + '\n';
string += 'Y:' + this.Y + '\n';
return string;
},
/**
* Cancela la propagación del evento.
*
* @param (Event)
* e Evento pasado por el browser.
*/
cancelar: function(e) {
if (!e || !this.esEspecial()) {
try {
event.keyCode = 0;
} catch (e) {
}
try {
event.returnValue = false;
} catch (e) {
}
Event.stop(this.evento);
}
},
/**
* Verifica si una tecla especial fue presionada.
*
* @return (Boolean) Verdadero si una tecla especial fue presionada
*/
esEspecial: function() {
return !this.tecla || (this.tecla == this.TAB)
|| (this.tecla == this.BACKSPACE) || (this.tecla == this.ENTER)
|| (this.tecla == this.FIN) || (this.tecla == this.INICIO)
|| (this.tecla == this.IZQUIERDA)
|| (this.tecla == this.ARRIBA) || (this.tecla == this.DERECHA)
|| (this.tecla == this.ABAJO) || (this.tecla == this.ESC)
|| (this.tecla == this.SUPR);
},
/**
* Verifica si una tecla especial fue presionada.
*
* @return (Boolean) Verdadero si una tecla especial fue presionada
*/
esIngreso: function() {
return !this.esEspecial() || (this.tecla == this.BACKSPACE)
|| (this.tecla == this.SUPR);
},
esFuncion: function() {
return !Object.isNumber(this.tecla) || (this.F1 == this.tecla)
|| (this.F2 == this.tecla) || (this.F3 == this.tecla)
|| (this.F4 == this.tecla) || (this.F5 == this.tecla)
|| (this.F6 == this.tecla) || (this.F7 == this.tecla)
|| (this.F8 == this.tecla) || (this.F9 == this.tecla)
|| (this.F10 == this.tecla) || (this.F11 == this.tecla)
|| (this.F12 == this.tecla);
},
/**
* Encuentra la longitud de la selección en el campo donde se disparó el
* evento.
*
* @return (int) La longitud de la selección
*/
getSelLength: function() {
if (document.getSelection) {
return (document.getSelection() + '').length;
} else if (this.evento.elemento
&& Object.isNumber(this.evento.elemento.selectionStart)) {
return this.evento.elemento.selectionEnd
- this.evento.elemento.selectionStart;
} else if (window.getSelection) {
return (window.getSelection() + '').length;
} else if (document.selection) {
return (document.selection.createRange().text + '').length;
} else {
return -1;
}
},
/**
* @private
*/
initTeclas: function() {
this.TAB = 9;
this.BACKSPACE = 8;
this.ENTER = 13;
this.SHIFT = 16;
this.CTRL = 17;
this.ESC = 27;
this.ESPACIO = 32;
this.PGUP = 33;
this.PGDOWN = 34;
this.FIN = 35;
this.INICIO = 36;
this.IZQUIERDA = 37;
this.ARRIBA = 38;
this.DERECHA = 39;
this.ABAJO = 40;
this.INS = 45;
this.SUPR = 46;
this.PUNTO = 190;
this.DECIMAL = 110;
this.F1 = 112;
this.F2 = 113;
this.F3 = 114;
this.F4 = 115;
this.F5 = 116;
this.F6 = 117;
this.F7 = 118;
this.F8 = 119;
this.F9 = 120;
this.F10 = 121;
this.F11 = 122;
this.F12 = 123;
this.DOT = 190;
this.SLASH = 191;
if (Prototype.Browser.IE) {
this.DASH = 189;
} else {
this.DASH = 109;
}
this.BACKSLASH = 220;
}
});
}
modularjs.loaded["fitbank.evento"] = true;
if (!modularjs.loaded["fitbank.templates"]) {
if (!modularjs.loaded["lib.underscore"]) {
// Underscore.js 1.5.2
// http://underscorejs.org
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.5.2';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
return _.filter(obj, function(value, index, list) {
return !iterator.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (isFunc ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs, first) {
if (_.isEmpty(attrs)) return first ? void 0 : [];
return _[first ? 'find' : 'filter'](obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
return true;
});
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.where(obj, attrs, true);
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity, value: -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed > result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity, value: Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
});
return shuffled;
};
// Sample **n** random values from an array.
// If **n** is not specified, returns a single random element from the array.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
if (arguments.length < 2 || guard) {
return obj[_.random(obj.length - 1)];
}
return _.shuffle(obj).slice(0, Math.max(0, n));
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, value, context) {
var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
return function(obj, value, context) {
var result = {};
var iterator = value == null ? _.identity : lookupIterator(value);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, key, value) {
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, key, value) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, key) {
_.has(result, key) ? result[key]++ : result[key] = 1;
});
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = iterator == null ? _.identity : lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
return (n == null) || guard ? array[0] : slice.call(array, 0, n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) {
return array[array.length - 1];
} else {
return slice.call(array, Math.max(array.length - n, 0));
}
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var length = _.max(_.pluck(arguments, "length").concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(arguments, '' + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, length = list.length; i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(length);
while(idx < length) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context.
_.partial = function(func) {
var args = slice.call(arguments, 1);
return function() {
return func.apply(this, args.concat(slice.call(arguments)));
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length === 0) throw new Error("bindAll must be passed function names");
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
return function() {
context = this;
args = arguments;
timestamp = new Date();
var later = function() {
var last = (new Date()) - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) result = func.apply(context, args);
}
};
var callNow = immediate && !timeout;
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow) result = func.apply(context, args);
return result;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func];
push.apply(args, arguments);
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = new Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var pairs = new Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
}
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(Math.max(0, n));
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
_.extend(_.prototype, {
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
}).call(this);
}
modularjs.loaded["lib.underscore"] = true;
var Templates = {
PRECOMPILED: $H(),
load: function(name, callback) {
new Ajax.Request("templates/" + name + ".template.html", {
asynchronous: false,
method: "get",
onSuccess: function(response) {
Templates.PRECOMPILED[name] = _.template(response.responseText);
callback && callback.defer();
},
onException: function(response, e) {
console.log(response);
console.log(e);
}
});
return function() {
if (!Templates.PRECOMPILED[name]) {
console.error("Template not loaded yet!");
return "";
}
return Templates.PRECOMPILED[name].apply(Templates.PRECOMPILED[name], arguments);
}
},
toDOM: function(string) {
var div = new Element('div');
div.update(string);
return div.firstChild;
}
};
}
modularjs.loaded["fitbank.templates"] = true;
if (!modularjs.loaded["fitbank.ui.ventana"]) {
if (!modularjs.loaded["lib.onload"]) {
__onload__ = false;
addOnLoad = function(f) {
if (__onload__) {
f();
} else {
document.observe("dom:loaded", f);
}
}
addOnLoad(function() { __onload__ = true; });
}
modularjs.loaded["lib.onload"] = true;
/**
* Clase Ventana - Representa una ventana con cualquier tipo de contenido.
*/
var Ventana = Class.create({
/**
* Título de la ventana.
*/
titulo: null,
/**
* Contenedor del título de la ventana.
*/
divTitulo: null,
/**
* Elemento dentro del que va a crear la ventana si se lo especifica.
*/
elementoPadre: null,
/**
* Elemento junto al que va a crear la ventana si se lo especifica.
*/
elemento: null,
/**
* Posición en x de la ventana.
*/
x: null,
/**
* Posición en y de la ventana.
*/
y: null,
/**
* Ancho de la ventana.
*/
w: null,
/**
* Alto de la ventana.
*/
h: null,
/**
* Condición de centrado de la ventana.
*/
centrada: true,
/**
* Contenido de la ventana.
*/
contenido: null,
/**
* Ver fondo oscuro.
*/
verFondo: true,
/**
* Activar el bloqueo de ventanas modales. Cuando el bloqueo está activado, cualquier cambio que se haga a la
* ventana causa que se cierre la sesión.
*/
bloqueoModal: false,
/**
* Indica si se debe destruir al cerrar.
*/
destruirAlCerrar: true,
/**
* Indica si se puede seleccionar el texto del contenido.
*/
selectable: true,
/**
* Indica si se puede mover la ventana.
*/
movable: true,
/**
* @private
*/
table: null,
/**
* @private
*/
barra: null,
/**
* @private
*/
onVer: function() {
if (!this.selectable) {
this.divContenido.onmousedown = function() { return false; };
}
},
/**
* @private
*/
onCerrar: function() {
},
/**
* @private
*/
initialize: function(parametros) {
this.verFondo = Parametros["fitbank.ui.ventana.fondo.ENABLED"] == "true";
this.movable = Parametros["fitbank.ui.ventana.MAKE_MOVABLE"] == "true";
this.bloqueoModal = Parametros["fitbank.ui.ventana.BLOQUEOMODAL"] == "true";
Object.extend(this, parametros);
if (!this.elementoPadre) {
this.elementoPadre = document.body;
} else {
this.elementoPadre = $(this.elementoPadre);
}
if (this.elemento) {
this.elemento = $(this.elemento);
this.centrada = false;
}
if (Mobile) {
this.centrada = false;
this.x = 0;
this.y = 0;
this.w = "100%";
this.h = "100%";
this.verFondo = false;
this.movable = false;
}
this.crear();
this.insertar();
},
/**
* @private
*/
crear: function() {
this.div = new Element('div', {
className: "ventana"
}).hide();
var x = function(n) {
return n + (Object.isNumber(n) ? 'px' : '');
};
if (this.x) {
this.div.style.left = x(this.x);
}
if (this.y) {
this.div.style.top = x(this.y);
}
var divTitulo = new Element("div", {
className: "titulo holo-action-bar"
});
this.div.insert(divTitulo);
var cerrar = new Element("a", {
'href': "#",
'class': "holo-up"
}).update(new Element("img", {
src: "img/cerrar.png"
}));
if (Mobile) {
divTitulo.insert(new Element("h1").update(cerrar).insert(this.titulo));
} else {
divTitulo.insert(cerrar);
divTitulo.insert(new Element("span").update(this.titulo || "FitBank"));
}
cerrar.on("click", this.cerrar.bind(this));
if (this.movable) {
Util.makeMovable(this.div, divTitulo);
}
if (!Mobile) {
divTitulo.on("dblclick", function(e) {
this.divContenido.toggle();
}.bind(this));
}
this.divTitulo = divTitulo;
this.divContenido = new Element("div", {
className: "contenido"
});
this.div.insert(this.divContenido);
this.divContenido.style.width = x(this.w);
this.divContenido.style.height = x(this.h);
if (this.contenido) {
this.setContenido(this.contenido);
}
},
/**
* Inserta la ventana en el documento en la posicion necesitada.
*
* @private
*/
setContenido: function(contenido) {
if (typeof contenido == "string") {
contenido = new Element("span").update(contenido);
}
this.contenido = contenido;
this.divContenido.update(this.contenido);
},
/**
* Inserta la ventana en el documento en la posicion necesitada.
*
* @private
*/
insertar: function() {
if (this.elemento) {
Element.insert(this.elemento, {
after: this.div
});
} else {
Element.insert(this.elementoPadre, this.div);
}
},
_getSecureModalStyle: function() {
return Object.toJSON(new copyConstructor(getComputedStyle(Ventana.fondo)));
},
/**
* Muestra la ventana.
*/
ver: function() {
this.insertar();
if (this.verFondo) {
Ventana.fondo.show();
c && c.form && c.form.setStyle("pointer-events: none");
var style = this._getSecureModalStyle();
if (this.bloqueoModal) {
this.verFondoInterval = setInterval(function () {
if (!Ventana.fondo.visible() || !Ventana.fondo.parentElement
|| style != this._getSecureModalStyle()) {
clearInterval(this.verFondoInterval);
alert("Acción no permitida");
Entorno.caducar(null);
}
}.bind(this), 1000);
}
Ventana.abiertas++;
}
this.div.show();
Element.addClassName.defer(this.div, "visible");
var maxTamano = document.viewport.getHeight() - (Mobile ? 0 : 10);
if (this.div.getHeight() > maxTamano) {
this.div.setStyle({
height: maxTamano + "px"
});
this.divContenido.setStyle({
height: (maxTamano - this.divTitulo.getHeight() - 20) + "px"
});
}
if (this.centrada) {
this.div.style.position = "absolute";
if (this.contenido.getWidth() && !this.w) {
this.div.setStyle({
width: (this.contenido.getWidth() + 20) + "px"
});
}
this.div.center();
}
this.visible = true;
this.onVer();
},
/**
* Esconde la ventana.
*/
esconder: function() {
if (this.visible) {
if (this.verFondo) {
Ventana.abiertas--;
if (!Ventana.abiertas) {
Ventana.fondo.hide();
c && c.form && c.form.setStyle("pointer-events: initial");
this.bloqueoModal && this.verFondoInterval && clearInterval(this.verFondoInterval);
}
}
(function() {
this.div.hide();
this.visible = false;
}).bind(this).delay(0.2);
this.div.removeClassName("visible");
}
},
/**
* Cierra la ventana.
*/
cerrar: function() {
if (this.onCerrar() !== false) {
this.esconder();
if (this.destruirAlCerrar) {
this.destruir.bind(this).delay(0.2);
}
}
},
/**
* Destruye la ventana.
*/
destruir: function() {
this.div.remove();
}
});
addOnLoad(function() {
Ventana.fondo = new Element('div', {
className: "ventana-fondo"
});
document.body.appendChild(Ventana.fondo);
Ventana.fondo.setOpacity(0.8);
Ventana.fondo.hide();
Ventana.abiertas = 0;
});
}
modularjs.loaded["fitbank.ui.ventana"] = true;
/**
* Clase MenuRapido - Funciones para crear un menú rapido.
*/
var MenuRapido = Class.create({
transacciones: null,
seleccionado: null,
cuenta: 0,
menu: null,
favs: {},
template: Templates.load("menu-rapido"),
templateTransacciones: Mobile && Templates.load("menu-rapido-transacciones"),
initialize: function() {
this.ventana = new Ventana({
titulo: Mobile ? this.input : "Cargar nueva transacción",
contenido: this.template(),
fondo: true,
destruirAlCerrar: false
});
this.elemento = this.ventana.contenido;
this.input = this.elemento.select(".menu-rapido-input")[0];
this.favoritos = this.elemento.select(".favoritos")[0];
this.filtrosDiv = this.elemento.select(".menu")[0];
this.resultadosDiv = this.elemento.select(".resultados")[0];
Mobile && $('menu-rapido').update(this.resultadosDiv);
this.input.on("keyup", this.keyup.bind(this));
this.resultadosDiv.on("click", function(e) {
this.activar(Event.element(e));
}.bind(this));
this.resultadosDiv.on("mouseover", function(e) {
this.seleccionar(Event.element(e));
}.bind(this));
this.reset();
},
reset: function() {
this.transacciones = new BSharpTree(function(x) {
return x.n;
});
this.filtrosDiv.update("");
},
cargarMenu: function(menu) {
var transacciones = $A();
var cargarTransacciones = function(parent, item) {
item.parent = parent;
if (item.transaccion) {
transacciones.push(item);
}
if (item.items) {
item.items.each(cargarTransacciones.curry(item));
}
};
if (menu.items) {
var itemsArbol = $H();
menu.items.each(function(item) {
cargarTransacciones(null, item);
if (item.items) {
item.items.each(function(subitem) {
if (!subitem.transaccion) {
itemsArbol.set(subitem.nombre, subitem);
}
});
}
}.bind(this));
if (itemsArbol.size() > 0) {
this.cargarArbolFiltros(itemsArbol.values());
}
}
if (menu.favoritos) {
this.cargarFavoritos(menu.favoritos);
}
this.cargarTransacciones(transacciones);
},
cargarArbolFiltros: function(items) {
if (Mobile) return;
var ul = new Element("ul", {
className: "menu-rapido-arbol"
});
this.filtrosDiv.insert(ul);
items.each(function(item) {
this.cargarItem(item, ul);
}, this);
},
cargarTransacciones: function(transacciones) {
if (Mobile) {
this.menu = new Element("div");
this.menu.update(this.templateTransacciones({ tran: transacciones }));
}
transacciones.each(this.indexar.bind(this));
this.llenaConTodo();
},
cargarItem: function(item, ul) {
var li = new Element("li");
ul.insert(li);
var a = new Element("a", {
href: "#"
}).update(item.nombre);
li.insert(a);
a.on("click", (function(e) {
this.input.value = "";
this.query("p:" + item.nombre + "*");
$E(e).cancelar();
}).bind(this));
a.on("dblclick", (function(e) {
li.toggleClassName("show");
$E(e).cancelar();
}).bind(this));
li.on("click", (function(e) {
this.input.value = "";
this.input.select();
li.toggleClassName("show");
$E(e).cancelar();
}).bind(this));
var ulSub = new Element("ul");
item.items.each(function(item) {
if (!item.transaccion) {
this.cargarItem(item, ulSub);
}
}, this);
if (ulSub.hasChildNodes()) {
li.addClassName("subitems");
li.insert(ulSub);
}
},
cargarFavoritos: function(favoritos) {
var f = 1;
favoritos.each((function(fav) {
var button = new Element("button", {
title: fav.nombre
}).update(fav.nombre);
if (f <= 12) {
button.insert({
top: new Element("span").update("F" + f)
});
this.favs[f] = function(e) {
this.activar(button);
}.bind(this);
f++;
}
button.contenido = fav;
button.on("click", function(e) {
this.activar(button);
}.bind(this));
this.favoritos.insert(button);
}).bind(this));
},
ver: function() {
this.ventana.ver();
Mobile && this.limpiarResultados();
this.llenaConTodo();
this.input.activate.bind(this.input).delay(0.1);
},
esconder: function() {
this.ventana.esconder();
this.deseleccionar();
this.input.value = "";
!Mobile && this.resultadosDiv.update("");
},
alternar: function() {
if (this.ventana.visible) {
this.esconder();
} else {
this.ver();
}
},
llenaConTodo: function() {
if (!Mobile) return;
var all = this.transacciones.getAllSingle();
all.each(this.resultadosDiv.insert.bind(this.resultadosDiv));
},
limpiarResultados: function() {
this.deseleccionar();
this.resultadosDiv.childElements().each(Element.remove);
},
/**
* @private
*/
seleccionar: function(elemento) {
if (Mobile) return;
if (elemento && elemento.contenido) {
var seleccionado = this.deseleccionar();
this.seleccionado = elemento || seleccionado;
if (this.seleccionado) {
this.seleccionado.addClassName("seleccionado");
this.seleccionado.ensureVisible();
}
}
},
/**
* @private
*/
deseleccionar: function() {
if (Mobile) return;
var seleccionado = this.seleccionado;
this.seleccionado = null;
if (seleccionado) {
seleccionado.removeClassName("seleccionado");
}
return seleccionado;
},
/**
* @private
*/
activar: function(elemento) {
elemento = elemento || this.seleccionado;
if (Mobile) {
$('entorno-formulario').hide();
Mobile.snapper.close();
}
if (elemento) {
if (elemento.nodeName.toLowerCase() === "span") {
elemento = elemento.parentNode;
}
if (elemento.contenido) {
this.esconder();
Entorno.contexto.cargar({
subsistema: elemento.contenido.subsistema,
transaccion: elemento.contenido.transaccion
});
}
}
},
/**
* @private
*/
keyup: function(e) {
e = $E(e);
switch (e.tecla) {
case e.ARRIBA:
if (this.seleccionado) {
this.seleccionar(this.seleccionado.previous());
}
return;
case e.ABAJO:
if (this.seleccionado) {
this.seleccionar(this.seleccionado.next());
}
return;
case e.ENTER:
this.activar();
return;
case e.ESC:
this.esconder();
return;
}
if (!this.input.value) {
!Mobile && this.limpiarResultados();
this.llenaConTodo();
return;
}
if (!this.timeout) {
var query = this.input.value;
this.timeout = setTimeout(this.query.bind(this, query), 100);
}
},
query: function(query) {
this.limpiarResultados();
var resultados = this.transacciones.search(query.toLowerCase());
resultados.each(this.resultadosDiv.insert
.bind(this.resultadosDiv));
if (resultados.length > 0) {
this.seleccionar(this.resultadosDiv.firstChild);
}
this.timeout = null;
},
/**
* @private
*/
indexar: function(contenido) {
var elemento = this.getElemento(contenido);
this.addIndice(contenido.subsistema, elemento);
this.addIndice(contenido.transaccion, elemento);
this.addIndice(contenido.nombre, elemento);
this.addIndice(contenido.parent && contenido.parent.nombre, elemento);
this.addIndice(contenido.subsistema + contenido.transaccion, elemento);
this.addIndice("p:" + (contenido.parent && contenido.parent.nombre)
+ "*", elemento);
this.addIndice("st:" + contenido.subsistema + contenido.transaccion
+ "*", elemento);
},
/**
* @private
*/
addIndice: function(texto, elemento) {
if (texto) {
this.transacciones.add(texto.toLowerCase(), elemento);
}
},
/**
* @private
*/
getElemento: function(contenido) {
var elemento;
if (Mobile) {
var elementName = contenido.subsistema + contenido.transaccion;
elemento = this.menu.select('.' + elementName)[0];
} else {
elemento = new Element("div").update(contenido.nombre);
if (contenido.transaccion) {
elemento.insert({
top: new Element("span", {
className: "tag transaccion"
}).update(contenido.transaccion)
});
}
if (contenido.subsistema) {
elemento.insert({
top: new Element("span", {
className: "tag subsistema"
}).update(contenido.subsistema)
});
}
}
elemento.contenido = contenido;
elemento.n = this.cuenta++;
return elemento;
}
});