nest-parrot
Version:
Parrot built on react
1,692 lines (1,622 loc) • 508 kB
JavaScript
(function( global, factory ) {
if ( typeof module === "object" && typeof module.exports === "object" ) {
// CMD
// all dependencies need to passed as parameters manually,
// will not require here.
module.exports = factory;
} else if ( typeof define === 'function' && define.amd ) {
// AMD. Register as parrot
// TODO how to define the jquery plugin here?
define('parrot', ['jquery', 'jsface', 'moment', 'react', 'react-dom'], factory);
} else {
// in browser, global is window.
// all dependencies were loaded already.
// bootstrap and jquery's plugin are all attached to jquery,
// expose $pt and all components to window.
factory(global, jQuery, jsface, moment, React, ReactDOM);
}
}(typeof window !== "undefined" ? window : this, function(window, jQuery, jsface, moment, React, ReactDOM, DONT_EXPOSE_PARROT_TO_GLOBAL) {
var _pt = window.$pt;
var $pt = {};
window.$pt = $pt;
var browser = jQuery.browser;
var deparam = jQuery.deparam;
$pt.noConflict = function() {
window.$pt = _pt;
return $pt;
};
// insert all source code here
/** nest-parrot.V0.6.26 2019-05-16 */
(function (window) {
var patches = {
console: function () {
var noop = function () {};
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
var length = methods.length;
var console = window.console = window.console || {};
while (length--) {
var method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
window.console = window.console ? window.console : console;
},
string: function () {
if (String.prototype.upperFirst === undefined) {
String.prototype.upperFirst = function () {
if (this.length == 1) {
return this.toUpperCase();
} else {
return this.substring(0, 1).toUpperCase() + this.substring(1);
}
};
}
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (searchString, position) {
var subjectString = this.toString();
if (position === undefined || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.indexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (prefix) {
return this.slice(0, prefix.length) === prefix;
};
}
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}
if (String.prototype.isEmpty === undefined) {
String.prototype.isEmpty = function () {
return this.length === 0;
};
}
if (String.prototype.isBlank === undefined) {
String.prototype.isBlank = function () {
return this.trim().length === 0;
};
}
/**
* replace place holders %1, %2, etc with given string array
*
* @param strArray
* @returns
*/
if (String.prototype.format === undefined) {
String.prototype.format = function (strArray) {
return this.replace(/%(\d+)/g, function (_, m) {
return strArray[--m];
});
};
}
if (String.prototype.currencyFormat === undefined) {
String.prototype.currencyFormat = function (fraction) {
fraction = fraction ? fraction : 0;
var value = this * 1;
return value.toFixed(fraction).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
};
}
if (String.prototype.padLeft === undefined) {
String.prototype.padLeft = function (nSize, ch) {
var len = 0;
var s = this ? this : "";
ch = ch ? ch : '0'; // default add 0
len = s.length;
while (len < nSize) {
s = ch + s;
len++;
}
return s;
};
}
if (String.prototype.padRight === undefined) {
String.prototype.padRight = function (nSize, ch) {
var len = 0;
var s = this ? this : "";
ch = ch ? ch : '0'; // default add 0
len = s.length;
while (len < nSize) {
s = s + ch;
len++;
}
return s;
};
}
if (String.prototype.movePointLeft === undefined) {
String.prototype.movePointLeft = function (scale) {
var s, s1, s2, ch, ps, sign;
ch = ".";
sign = '';
s = this ? this : "";
if (scale <= 0) {
return s;
}
ps = s.split('.');
s1 = ps[0] ? ps[0] : "";
s2 = ps[1] ? ps[1] : "";
if (s1.slice(0, 1) == '-') {
s1 = s1.slice(1);
sign = '-';
}
if (s1.length <= scale) {
ch = "0.";
s1 = s1.padLeft(scale);
}
return sign + s1.slice(0, -scale) + ch + s1.slice(-scale) + s2;
};
}
if (String.prototype.movePointRight === undefined) {
String.prototype.movePointRight = function (scale) {
var s, s1, s2, ch, ps, sign;
ch = '.';
s = this ? this : "";
if (scale <= 0) {
return s;
}
ps = s.split('.');
s1 = ps[0] ? ps[0] : "";
s2 = ps[1] ? ps[1] : "";
if (s2.length <= scale) {
ch = '';
s2 = s2.padRight(scale);
}
if (s1.slice(0, 1) == '-') {
s1 = s1.slice(1);
sign = '-';
} else {
sign = '';
}
if (s1 == 0) {
s1 = '';
}
// window.console.log('Return[sign=' + sign + ', s1=' + s1 + ', s2-1=' + s2.slice(0, scale) + ', ch=' + ch + ', s2-2=' + s2.slice(scale, s2.length) + ']');
var integral = (s1 + s2.slice(0, scale)).replace(/^0+/, '');
if (integral.isEmpty()) {
integral = '0';
}
return sign + integral + ch + s2.slice(scale, s2.length);
};
}
},
number: function () {
// if (Number.prototype.currencyFormat === undefined) {
// Number.prototype.currencyFormat = function (fraction) {
// fraction = fraction ? fraction : 0;
// return value.toFixed(fraction).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
// };
// }
},
array: function () {
if (!Array.prototype.find) {
Array.prototype.find = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
}
};
patches.console();
patches.string();
patches.number();
patches.array();
})(window);
// copy from https://github.com/princed/caret
(function ($, window) {
function focus(target) {
if (!document.activeElement || document.activeElement !== target) {
target.focus();
}
}
$.fn.caret = function (pos) {
var target = this[0];
var range, range1, range2, bookmark;
var isContentEditable = target.contentEditable === 'true';
//get
if (arguments.length == 0) {
//HTML5
if (window.getSelection) {
//contenteditable
if (isContentEditable) {
focus(target);
var selection = window.getSelection();
// Opera 12 check
if (!selection.rangeCount) {
return 0;
}
range1 = selection.getRangeAt(0);
range2 = range1.cloneRange();
range2.selectNodeContents(target);
range2.setEnd(range1.endContainer, range1.endOffset);
return range2.toString().length;
}
//textarea
return target.selectionStart;
}
//IE<9
if (document.selection) {
focus(target);
//contenteditable
if (isContentEditable) {
range1 = document.selection.createRange();
range2 = document.body.createTextRange();
range2.moveToElementText(target);
range2.setEndPoint('EndToEnd', range1);
return range2.text.length;
}
//textarea
pos = 0;
range = target.createTextRange();
range2 = document.selection.createRange().duplicate();
bookmark = range2.getBookmark();
range.moveToBookmark(bookmark);
while (range.moveStart('character', -1) !== 0) {
pos++;
}
return pos;
}
//not supported
return 0;
}
//set
if (pos == -1) {
pos = this[isContentEditable ? 'text' : 'val']().length;
}
//HTML5
if (window.getSelection) {
//contenteditable
if (isContentEditable) {
focus(target);
window.getSelection().collapse(target.firstChild, pos);
}
//textarea
else target.setSelectionRange(pos, pos);
}
//IE<9
else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(target);
range.moveStart('character', pos);
range.collapse(true);
range.select();
}
if (!isContentEditable) {
focus(target);
}
return pos;
};
})(jQuery, window);
(function (window) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
// exceptions
/**
* component exception
* @param code {string} exception code
* @param message {string} exception message
*/
var ComponentException = function (code, message) {
this.value = code;
this.message = message;
this.toString = function () {
return this.value + ": " + this.message;
};
};
/**
* create component exception
* @param code {string} exception code
* @param message {string} exception message
* @returns {ComponentException}
*/
$pt.createComponentException = function (code, message) {
return new ComponentException(code, message);
};
// messages
var messages = {};
$pt.messages = messages;
$pt.defineMessage = function (key, message) {
// if (messages[key] != null) {
// window.console.debug('Message[' + key + '=' + messages[key] + '] was replaced by [' + message + ']');
// }
messages[key] = message;
return $pt;
};
$pt.getMessage = function (key) {
var message = messages[key];
return message == null ? null : message;
};
$pt.__messagesDomain = {};
var throwMessageTypeConflictException = function (key, source, target) {
var exp = $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Message [%1] has conflict type in source and target.'.format([key]));
exp.source = source;
exp.target = target;
throw exp;
};
var internalInstallMessages = function (source, target, prefix) {
// console.log('prefix', prefix);
Object.keys(source).forEach(function (key) {
var message = source[key];
var existMessage = target[key];
if (typeof message === 'object' && !Array.isArray(message)) {
if (existMessage) {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
internalInstallMessages(message, existMessage, prefix ? prefix + '.' + key : key);
} else {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
}
} else {
// not exists in target, create a JSON to handle
target[key] = {};
internalInstallMessages(message, target[key], prefix ? prefix + '.' + key : key);
}
} else {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
} else {
if (existMessage) {
console.error('Message [%1] exists in target.'.format([prefix ? prefix + '.' + key : key]));
console.log('source message: ', source);
console.log('target message: ', target);
}
target[key] = message;
}
}
});
};
$pt.installMessages = function (domain, messagesJSON, messageTarget) {
if (typeof messagesJSON !== 'object') {
throw $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Messages must be an JSON object.');
}
if (domain == null || typeof domain !== 'string' || domain.isBlank()) {
throw $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Domain of messages must be a string.');
}
console.log('Start to install messages on domain [' + domain + '].');
var target = messageTarget ? messageTarget : $pt.messages;
internalInstallMessages(messagesJSON, target);
if (!$pt.__messagesDomain[domain]) {
$pt.__messagesDomain[domain] = {
domain: domain,
messages: []
};
}
$pt.__messagesDomain[domain].messages.push({
target: target,
json: messagesJSON
});
console.log('End of install messages on domain [' + domain + '].');
};
var internalUninstallMessages = function (source, target, prefix) {
Object.keys(source).forEach(function (key) {
var message = source[key];
var existMessage = target[key];
if (typeof message === 'object' && !Array.isArray(message)) {
if (existMessage) {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
internalUninstallMessages(message, existMessage, prefix ? prefix + '.' + key : key);
if (Object.keys(existMessage).length == 0) {
// all content removed, delete from target
delete target[key];
}
} else {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
}
}
} else {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
} else {
delete target[key];
}
}
});
};
$pt.uninstallMessages = function (domain, messageTarget) {
if (domain == null || typeof domain !== 'string' || domain.isBlank()) {
throw $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Domain of messages must be a string.');
}
console.log('Start to uninstall messages on domain [' + domain + '].');
var target = messageTarget ? messageTarget : $pt.messages;
var domainMessages = $pt.__messagesDomain[domain];
if (domainMessages) {
domainMessages.messages = domainMessages.messages.map(function (log) {
if (log.target === target) {
internalUninstallMessages(log.json, target);
return null;
} else {
return log;
}
}).filter(function (log) {
return log != null;
});
if (domainMessages.messages.length == 0) {
// all messages uninstalled, remove domain
delete $pt.__messagesDomain[domain];
}
}
console.log('End of uninstall messages on domain [' + domain + '].');
};
// components
$pt.Components = {};
$pt.exposeComponents = function (context) {
Object.keys($pt.Components).forEach(function (component) {
context[component] = $pt.Components[component];
});
};
// component constants
$pt.ComponentConstants = {
// component types
Text: "text",
TextInJSON: { type: 'text', label: true, popover: true, renderError: true, delay: 1000 },
TextArea: 'textarea',
Select: "select",
Check: "check",
ArrayCheck: 'acheck',
Toggle: 'toggle',
Radio: "radio",
Table: { type: "table", label: false, popover: false, renderError: false },
Tree: { type: "tree", label: false, popover: false, renderError: false },
SelectTree: "seltree",
Date: "date",
Search: "search",
Button: { type: "button", label: false, popover: false, renderError: false },
Tab: { type: 'tab', label: false, popover: false, renderError: false },
ArrayTab: { type: 'atab', label: false, popover: false, renderError: false },
Panel: { type: 'panel', label: false, popover: false, renderError: false },
ArrayPanel: { type: 'apanel', label: false, popover: false, renderError: false },
Label: { type: 'label', label: false },
Form: { type: 'form', label: false, popover: false, renderError: false },
ButtonFooter: { type: 'buttonfooter', label: false, popover: false, renderError: false },
File: "file",
Nothing: { type: "nothing", label: false },
// date format
Default_Date_Format: "YYYY/MM/DD HH:mm:ss.SSS", // see momentjs
// code table
CODETABLE_PARENT_VALUE_KEY: 'value',
CODETABLE_SENDER_PROXY: null,
CODETABLE_RECEIVER_PROXY: null,
CODETABLE_REMOTE_PROXY: null,
// error display
ERROR_POPOVER: true,
// exception codes
Err_Unsupported_Component: "PT-00001",
Err_Unuspported_Column_Sort: "PT-00002",
Err_Search_Text_Trigger_Digits_Not_Defined: "PT-00003",
Err_Tab_Index_Out_Of_Bound: "PT-00004",
Err_Incorrect_Messages_Format: 'PT-00005',
// http status
Http_Status: {
"0": "Browser Error",
"400": "Bad Request",
"401": "Unauthorized",
"402": "Payment Required",
"403": "Forbidden",
"404": "Not Found",
"405": "Method Not Allowed",
"406": "Not Acceptable",
"407": "Proxy Authentication Required",
"408": "Request Timeout",
"409": "Conflict",
"410": "Gone",
"411": "Length Required",
"412": "Precondition Failed",
"413": "Request Entity Too Large",
"414": "Request-URI Too Long",
"415": "Unsupported Media Type",
"416": "Requested Range Not Satisfiable",
"417": "Expectation Failed",
"500": "Internal Server Error",
"501": "Not Implemented",
"502": "Bad Gateway",
"503": "Service Unavailable",
"504": "Gateway Timeout",
"505": "HTTP Version Not Supported",
// customize
"506": "Application Exception"
}
};
$pt.parseJSON = function (object) {
if (object == null) {
return null;
}
if (typeof object === 'string') {
return JSON.parse(object);
} else {
return object;
}
};
$pt.isVisibleOnAuth = function (component) {
return true;
};
$pt.markFuncAsWrap = function (func) {
if (func) {
func.wrap = true;
}
return func;
};
/*!
* jQuery Browser Plugin 0.1.0
* https://github.com/gabceb/jquery-browser-plugin
*
* Original jquery-browser code Copyright 2005, 2015 jQuery Foundation, Inc. and other contributors
* http://jquery.org/license
*
* Modifications Copyright 2015 Gabriel Cebrian
* https://github.com/gabceb
*
* Released under the MIT license
*
* Date: 05-07-2015
*/
/*global window: false */
(function () {
"use strict";
function uaMatch(ua) {
// If an UA is not provided, default to the current browser UA.
if (ua === undefined) {
ua = window.navigator.userAgent;
}
ua = ua.toLowerCase();
var match = /(edge)\/([\w.]+)/.exec(ua) || /(opr)[\/]([\w.]+)/.exec(ua) || /(chrome)[ \/]([\w.]+)/.exec(ua) || /(iemobile)[\/]([\w.]+)/.exec(ua) || /(version)(applewebkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+).*(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
var platform_match = /(ipad)/.exec(ua) || /(ipod)/.exec(ua) || /(windows phone)/.exec(ua) || /(iphone)/.exec(ua) || /(kindle)/.exec(ua) || /(silk)/.exec(ua) || /(android)/.exec(ua) || /(win)/.exec(ua) || /(mac)/.exec(ua) || /(linux)/.exec(ua) || /(cros)/.exec(ua) || /(playbook)/.exec(ua) || /(bb)/.exec(ua) || /(blackberry)/.exec(ua) || [];
var browser = {},
matched = {
browser: match[5] || match[3] || match[1] || "",
version: match[2] || match[4] || "0",
versionNumber: match[4] || match[2] || "0",
platform: platform_match[0] || ""
};
if (matched.browser) {
browser[matched.browser] = true;
browser.version = matched.version;
browser.versionNumber = parseInt(matched.versionNumber, 10);
}
if (matched.platform) {
browser[matched.platform] = true;
}
// These are all considered mobile platforms, meaning they run a mobile browser
if (browser.android || browser.bb || browser.blackberry || browser.ipad || browser.iphone || browser.ipod || browser.kindle || browser.playbook || browser.silk || browser["windows phone"]) {
browser.mobile = true;
}
// These are all considered desktop platforms, meaning they run a desktop browser
if (browser.cros || browser.mac || browser.linux || browser.win) {
browser.desktop = true;
}
// Chrome, Opera 15+ and Safari are webkit based browsers
if (browser.chrome || browser.opr || browser.safari) {
browser.webkit = true;
}
// IE11 has a new token so we will assign it msie to avoid breaking changes
if (browser.rv || browser.iemobile) {
var ie = "msie";
matched.browser = ie;
browser[ie] = true;
}
// Edge is officially known as Microsoft Edge, so rewrite the key to match
if (browser.edge) {
delete browser.edge;
var msedge = "msedge";
matched.browser = msedge;
browser[msedge] = true;
}
// Blackberry browsers are marked as Safari on BlackBerry
if (browser.safari && browser.blackberry) {
var blackberry = "blackberry";
matched.browser = blackberry;
browser[blackberry] = true;
}
// Playbook browsers are marked as Safari on Playbook
if (browser.safari && browser.playbook) {
var playbook = "playbook";
matched.browser = playbook;
browser[playbook] = true;
}
// BB10 is a newer OS version of BlackBerry
if (browser.bb) {
var bb = "blackberry";
matched.browser = bb;
browser[bb] = true;
}
// Opera 15+ are identified as opr
if (browser.opr) {
var opera = "opera";
matched.browser = opera;
browser[opera] = true;
}
// Stock Android browsers are marked as Safari on Android.
if (browser.safari && browser.android) {
var android = "android";
matched.browser = android;
browser[android] = true;
}
// Kindle browsers are marked as Safari on Kindle
if (browser.safari && browser.kindle) {
var kindle = "kindle";
matched.browser = kindle;
browser[kindle] = true;
}
// Kindle Silk browsers are marked as Safari on Kindle
if (browser.safari && browser.silk) {
var silk = "silk";
matched.browser = silk;
browser[silk] = true;
}
// Assign the name and platform variable
browser.name = matched.browser;
browser.platform = matched.platform;
return browser;
}
// Run the matching process, also assign the function to the returned object
// for manual, jQuery-free use if desired
window.jQBrowser = uaMatch(window.navigator.userAgent);
window.jQBrowser.uaMatch = uaMatch;
$pt.browser = window.jQBrowser;
})();
var _context = window;
$pt.getService = function (context, serviceName) {
var innerContext = context ? context : _context;
var innerServiceName = serviceName ? serviceName : '$service';
if (!innerContext[innerServiceName]) {
innerContext[innerServiceName] = {};
}
return innerContext[innerServiceName];
};
})(window);
(function (window, $, deparam) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
$pt.AjaxConstants = {
ContentType: {
POST: "application/json; charset=UTF-8",
GET: "application/json; charset=UTF-8",
DELETE: "application/json; charset=UTF-8",
PUT: "application/json; charset=UTF-8"
},
Stringify: {
POST: true,
GET: false,
DELETE: false,
PUT: true
}
};
/**
* submit to server
* @param options {*} same as jquery ajax options, three more properties
* url: string
* done: same as jquery ajax done callback function
* fail: function or json object
* function: same as jquery ajax fail callback function
* json: key is return status, value is function which is same as jquery ajax fail callback function
* @returns {jqXHR}
*/
var submit = function (options) {
var url = options.url;
var done = options.done;
var fail = options.fail;
var quiet = options.quiet;
delete options.url;
delete options.done;
delete options.fail;
// build on request dialog
// show
if (quiet === true) {} else {
$pt.Components.NOnRequestModal.getOnRequestModal().show();
}
var hideOnRequest = function () {
if (quiet === true) {} else {
$pt.Components.NOnRequestModal.getOnRequestModal().hide();
}
};
return $.ajax(url, options).done(function (data, textStatus, jqXHR) {
if (done !== undefined && done !== null) {
try {
done(data, textStatus, jqXHR);
} catch (err) {
console.error(err);
console.error(data);
console.error(textStatus);
console.error(jqXHR);
var message = 'Unknown error occurred, see console for more information.';
message = err ? err.stack ? err.stack : err.toString ? err.toString() : message : message;
$pt.Components.NExceptionModal.getExceptionModal().show('Javascript Error', message);
}
}
}).fail(function (jqXHR, textStatus, errorThrown) {
if (fail !== undefined && fail !== null) {
var callback = null;
if (typeof fail === 'function') {
callback = fail;
} else {
callback = fail["" + jqXHR.status];
}
if (callback != null) {
try {
callback(jqXHR, textStatus, errorThrown);
} catch (err) {
console.error(err);
console.error(data);
console.error(textStatus);
console.error(jqXHR);
var message = 'Unknown error occurred, see console for more information.';
message = err ? err.stack ? err.stack : err.toString ? err.toString() : message : message;
$pt.Components.NExceptionModal.getExceptionModal().show('Javascript Error', message);
}
} else {
$pt.Components.NExceptionModal.getExceptionModal().show("" + jqXHR.status, jqXHR.responseText);
}
} else {
$pt.Components.NExceptionModal.getExceptionModal().show("" + jqXHR.status, jqXHR.responseText);
}
}).always(function () {
// hide
hideOnRequest();
});
};
var needStringify = function (predefine, specific) {
if (specific === true) {
return true;
} else if (specific === false) {
return false;
} else if (predefine === true) {
return true;
} else {
return false;
}
};
/**
* http post
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.internalDoPost = $pt.doPost = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.POST, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "POST",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.POST
}, settings, {
url: url,
// always send string to server side
data: data
}));
};
/**
* http put
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.doPut = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.PUT, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "PUT",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.PUT
}, settings, {
url: url,
data: data
}));
};
/**
* http get
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.doGet = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.GET, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "GET",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.GET
}, settings, {
url: url,
data: data
}));
};
/**
* http delete
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.doDelete = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.DELETE, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "DELETE",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.DELETE
}, settings, {
url: url,
data: data
}));
};
/**
* relocate page, use window.location
* @param url {string}
* @param data {*}
*/
$pt.relocatePage = function (url, data) {
var finalURL = url;
if (data) {
if (finalURL.indexOf('?') != -1) {
finalURL += '&' + $.param(data);
} else {
finalURL += '?' + $.param(data);
}
}
window.location = finalURL;
};
/**
* get data from url parameters
* @returns {*}
*/
$pt.getUrlData = function (params) {
var paramsString = '';
if (params !== undefined) {
if (params == null || params.isBlank()) {
return {};
} else {
paramsString = params;
}
} else {
paramsString = window.location.search;
if (paramsString != null && !paramsString.isBlank() && paramsString.trim() != '?') {
paramsString = paramsString.substring(1);
} else {
return {};
}
}
return $.deparam(paramsString);
};
/**
* mock ajax
* include jquery-mockjax when call this method, or do nothing
* parameters are same as mockjax
*/
$pt.mock = function () {
if (!$.mockjax) {
return;
}
$.mockjax.apply($, arguments);
};
// routes
var routes = {
context: '',
urls: {}
};
$pt.routes = routes;
/**
* define web context
* @param webContext {string}
*/
$pt.defineWebContext = function (webContext) {
routes.context = webContext;
return $pt;
};
/**
* define url with given key
* @param key {string} key of url
* @param urlRelateToWebContext {string} related url with no web context
*/
$pt.defineURL = function (key, urlRelateToWebContext) {
if (routes.urls[key] != null) {
window.console.warn('URL[' + key + '=' + routes.urls[key] + '] was replaced by [' + routes.context + urlRelateToWebContext + ']');
}
routes.urls[key] = urlRelateToWebContext;
return $pt;
};
/**
* get url by given key
* @param key {string} key of url
* @returns {string} return null when not found, or url with web context
*/
$pt.getURL = function (key) {
var url = routes.urls[key];
return url == null ? null : routes.context + url;
};
})(window, jQuery, jQuery.deparam);
(function (window, $, jsface) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
/**
* code table sorter
* @type {class}
*/
var CodeTableSorter = jsface.Class({
constructor: function (otherId) {
this._otherId = otherId;
},
/**
* sort code table element array
* @param codes
*/
sort: function (codes) {
var _this = this;
codes.sort(function (a, b) {
if (_this._otherId) {
if (a.id == _this._otherId) {
return 1;
} else if (b.id == _this._otherId) {
return -1;
}
}
return a.text < b.text ? -1 : a.text > b.text ? 1 : 0;
});
}
});
/**
* code table
* @type {class}
*/
var CodeTable = jsface.Class({
/**
* construct code table
* @param data {{id:string, text:string}[]|{url:string, data:*}}
* array of json object, eg: {id:"1", text:"text"}.
* id is required,
* text is optional when renderer is defined.
* @param renderer {function} optional
* param: element of code table,
* returns: string, put as text property into code table element
* @param sorter {CodeTableSorter} optional
*/
constructor: function (data, renderer, sorter) {
if (data == null || Array.isArray(data)) {
// construct with array, local data
this._local = true;
this._initArray = data;
} else {
// construct with json, remote data
this._local = false;
this._url = data.url;
this._postData = data.data;
this._sendProxy = data.proxy;
this._receiveProxy = data.receiver;
}
this._renderer = renderer;
this._sorter = sorter;
},
isInitialized: function () {
return this._initialized === true;
},
isRemote: function () {
return this._local === false;
},
isRemoteButNotInitialized: function () {
return this.isRemote() && !this.isInitialized();
},
initializeRemote: function () {
if (this.isRemoteButNotInitialized()) {
// return a promise to load remote codes
// since there might be more than one components using the same codetable
// log the first loading promise and returns to all others
if (!this._loading) {
this._loading = this.__loadRemoteCodes(true).always(function () {
delete this._loading;
this._allLoaded = true;
}.bind(this));
}
return this._loading;
} else {
// already initialized, or is local
// return an immediately resolved promise
return $.Deferred(function (deferred) {
deferred.resolve();
}).promise();
}
},
setAsRemoteInitialized: function () {
this._codes = this._codes ? this._codes : [];
this._map = this._map ? this._map : {};
this._initialized = true;
return this;
},
/**
* get renderer of code table
*/
getRenderer: function () {
return this._renderer;
},
/**
* initialize code table element array
* @param codeTableArray {{id:string, text:string}[]}
* array of json object, eg: {id:"1", text:"text"}.
* id is required,
* text is optional when renderer is defined.
* @param renderer {function} optional
* param: element of code table,
* returns: string, put as text property into code table element
* @param sorter {CodeTableSorter} optional
* @private
*/
__initCodesArray: function (codeTableArray, renderer, sorter) {
this._codes = codeTableArray;
if (this._codes == null) {
this._codes = [];
}
var map = {};
// render element
var render = function (code) {
if (renderer) {
code.text = renderer(code);
if (code.children) {
code.children.forEach(render);
}
}
};
this._codes.forEach(function (code) {
map[code.id] = code;
render(code);
});
this._map = map;
// sort elements
if (sorter) {
var sort = function (codes) {
sorter.sort(codes);
codes.forEach(function (code) {
if (code.children) {
sort(code.children);
}
});
};
sort(this._codes);
}
return this;
},
/**
* load remote code table, quiet and synchronized
* @private
*/
__loadRemoteCodes: function (async) {
var _this = this;
var remoteProxy = this.__getSendProxy();
var remoteVisit = remoteProxy ? remoteProxy.call(this, {
url: this._url,
data: this._postData,
quiet: true,
async: async != null ? async : false
}) : $pt.doPost(this._url, this._postData, {
quiet: true,
async: async != null ? async : false
});
return remoteVisit.done(function (data) {
var receiveData = data;
var receiverProxy = _this.__getReceiveProxy();
if (receiverProxy) {
receiveData = receiverProxy.call(_this, receiveData);
}
// init code table element array after get data from remote
_this.__initCodesArray(receiveData, _this._renderer, _this._sorter);
}).fail(function (jqXHR, textStatus, errorThrown) {
// error to console, quiet backend
_this.__initCodesArray(null, _this._renderer, _this._sorter);
window.console.error('Status:' + textStatus + ', error:' + errorThrown);
}).always(function () {
_this._initialized = true;
});
},
__getSendProxy: function () {
return this._sendProxy || $pt.ComponentConstants.CODETABLE_SENDER_PROXY || $pt.ComponentConstants.CODETABLE_REMOTE_PROXY;
},
__getReceiveProxy: function () {
return this._receiveProxy || $pt.ComponentConstants.CODETABLE_RECEIVER_PROXY;
},
/**
* get code table element array
* @returns {{id: string, text: string}[]}
* @private
*/
__getCodes: function () {
if (this._codes === undefined || this._initialized !== true) {
if (this._local) {
// initialize local data
this.__initCodesArray(this._initArray, this._renderer, this._sorter);
// delete the reference
delete this._initArray;
this._initialized = true;
} else {
// initialize remote data
this.__loadRemoteCodes();
}
}
return this._codes;
},
/**
* get code table element map, key is id of element, value is element
* @returns {*}
* @private
*/
__getMap: function () {
this.__getCodes();
return this._map;
},
/**
* filter code table elements by given function or json object.
* when parameter is a json object and data is from remote, check the given value was loaded or not,
* if not loaded, loaded the code table elements by given parameter first and merge into the client elements.
* @param func {function|{value:string, name:string}}
* function: same as function definition Array.filter()
* json:
* value: value to filter code table elements
* name: property name to match the given value
* eg. code table element is
* {id: '1', text: 'Text', parentId: '1'}
* {id: '2', text: 'Text', parentId: '1'}
* {id: '3', text: 'Text', parentId: '2'}
* when the json object is {value: '1', name: 'parentId'},
* then the first and second items returned, third item filtered
* @returns {{id: string, text: string}[]} maybe contain other properties, depends on initial data.
* return empty array when no data
*/
filter: function (func) {
if (typeof func === 'function') {
// parameter is function
return this.__getCodes().filter(func);
} else {
var value = func.value;
this.__loadRemoteCodeSegment(value);
// filter
return this.__getCodes().filter(function (code) {
this.__setParentValueAsLoaded(code[func.name]);
return code[func.name] == value;
}.bind(this));
}
},
isSegmentLoaded: function (parentValue) {
return !this.__needLoadFromRemote(parentValue);
},
loadRemoteCodeSegment: function (parentValue) {
if (this.__needLoadFromRemote(parentValue)) {
// no local data, and loaded keys don't contain current value
// reset post data
this.__rebuildPostData(parentValue);
// store current local data
var existedData = this.__holdExistedCodes();
// get data from server
return this.__loadRemoteCodes(true).done(function () {
this.__setParentValueAsLoaded(parentValue);
}.bind(this)).always(function () {
this.__mergeAll(existedData);
}.bind(this));
} else {
// local or already loaded
// return an immediately resolved promise
return $.Deferred(function (deferred) {
deferred.resolve();
}).promise();
}
},
__needLoadFromRemote: function (parentValue) {
return !this._local && !this._allLoaded && (!this._loadedKeys || this._loadedKeys[parentValue + ''] !== true);
},
__rebuildPostData: function (parentValue) {
var values = {};
values[this.parentValueKey()] = parentValue;
if (this._postData) {
this._postData = $.extend({}, this._postData, values);
} else {
this._postData = values;
}
return this;
},
__holdExistedCodes: function () {
return {
codes: this._codes != null ? this._codes : [],
map: this._map != null ? this._map : {}
};
},
__mergeAll: function (existedData) {
// merge server data and local data
this._codes.push.apply(this._codes, existedData.codes);
this._map = $.extend(existedData.map, this._map);
return this;
},
__setParentValueAsLoaded: function (parentValue) {
// init loaded keys
if (!this._loadedKeys) {
// this._loadedKeys = [];
this._loadedKeys = {};
}
// log the loaded keys
// this._loadedKeys.push(parentValue);
this._loadedKeys[parentValue + ''] = true;
return this;
},
__loadRemoteCodeSegment: function (parentValue) {
if (this.__needLoadFromRemote(parentValue)) {
// no local data, and loaded keys don't contain current value
// reset post data
this.__rebuildPostData(parentValue);
// store current local data
var existedData = this.__holdExistedCodes();
// get data from server
this.__loadRemoteCodes();
this.__setParentValueAsLoaded(parentValue);
this.__mergeAll(existedData);
}
return this;
},
/**
* get element by given code
* @param code {string} code
* @returns {{id:string, text:string}} maybe contain other properties, depends on initial data.
* return null when not found
*/
get: function (code) {
var item = this.__getMap()[code];
return item == null ? null : item;
},
/**
* get element text by given code
* @param code {string} code
* @returns {string} element text, return null when not found
*/
getText: function (code) {
var item = this.get(code);
return item == null ? null : item.text;
},
/**
* get code table element array
* @returns {{id: string, text: string}[]} maybe contain other properties, depends on initial data.
* return empty array when no data
*/
list: function () {
return this.__getCodes();
},
/**
* get code table element array, with hierarchy keys.
* no duplicate id allowed when call this method.
* @returns {{codeId: codeItem}};
*/
listAllChildren: function () {
var items = {};
var fetchItem = function (item) {
items[item.id] = item;
if (item.children) {
item.children.forEach(function (child) {
fetchItem(child);
});
}
};
this.list().forEach(function (item) {
fetchItem(item);
});
return items;
},
/**
* get code table element array, with hierarchy keys
* @param {rootId: string, separtor: string}
* @returns {{codeId: codeItem}};
*/
listWithHierarchyKeys: function (options) {
var separator = options && options.separator ? options.separator : '|';
var rootId = options && options.rootId != null ? options.rootId : '0';
var items = {};
var fetchItem = function (item, parentId) {
items[parentId + separator + item.id] = item;
if (item.children) {
item.children.forEach(function (child) {
fetchItem(child, parentId + separator + item.id);
});
}
};
this.list().forEach(function (item) {
fetchItem(item, rootId);
});
return items;
},
/**
* check code table element by given function
* @param func {function} same as function definition Array.some()
* @returns {boolean}
*/
some: function (func) {
return this.__getCodes().some(func);
},
/**
* run each code table element by given function
* @param func {function} same as function definition Array.map()
* @returns {[]} element of array depends on the parameter
*/
map: function (func) {
return this.__getCodes().map(func);
},
name: function (name) {
if (name) {
this.__name = name;
return this;
} else {
return this.__name;
}
},
parentValueKey: function (key) {
if (key) {
this.__parentValueKey = key;
return this;
} else {
return this.__parentValueKey ? this.__parentValueKey : $pt.ComponentConstants.CODETABLE_PARENT_VALUE_KEY;
}
}
});
/**
* create default code table sorter by given other id
* @param otherId {string} optional. item with this id will be last one when given
* @returns {CodeTableSorter}
*/
$pt.createDefaultCodeTableSorter = function (otherId) {
return new CodeTableSorter(otherId);
};
/**
* construct code table
* @param items {{id:string, text:string}[]|{url:string, data:*}}
* array of json object, eg: {id:"1", text:"text"}.
* id is required,
* text is optional when renderer is defined.
* @param renderer {function} optional
* param: element of code table,
* returns: string, put as text property into code table element
* @param sorter {CodeTableSorter} optional
*/
$pt.createCodeTable = function (items, renderer, sorter) {
return new CodeTable(items, renderer, sorter);
};
/**
* extend code table
* @param {JSON} extendCodeTable extend class definition
* @return {jsface.Class} extend code table class
*/
$pt.extendCodeTable = function (extendCodeTable) {
return jsface.Class(CodeTable, extendCodeTable);
};
})(window, jQuery, jsface);
/**
* depends on jquery, jsface
* depends on parrot-pre-define
*/
(function (window, $, moment, jsface) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
$pt.BUILD_PROPERTY_VISITOR = true;
$pt.PROPERTY_SEPARATOR = '_';
/**
* clone json object
* @param jsonObject {{}}
*/
$pt.cloneJSON = function (jsonObject) {
return $.extend(true, {}, jsonObject);
};
$pt.mergeObject = function (params) {
var deep = params.deep;
var target = params.target ? params.target : {};
var sources = Array.isArray(params.sources) ? params.sources : [params.sources];
// console.log(target);
// console.log(sources);
var source,
propName,
sourceIndex = 0,
sourceCount = sources.length,
targetPropValue,
sourcePropValue,
sourcePropValueIsArray,
destPropValue;
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== "object" && !$.isFunction(target)) {
target = {};
}
for (; sourceIndex < sourceCount; sourceIndex++) {
// Only deal with non-null/undefined values
if ((source = sources[sourceIndex]) != null) {
// Extend the base object
// console.log(source);
for (propName in source) {
// console.log(propName);
targetPropValue = target[propName];
sourcePropValue = source[propName];
// Prevent never-ending loop
if (target === sourcePropValue) {
continue;
}
// Recurse if we're merging plain objects or arrays
if (deep && sourcePropValue && ($.isPlainObject(sourcePropValue) || (sourcePropValueIsArray = $.isArray(sourcePropValue)))) {
if (sourcePropValueIsArray) {
sourcePropValueIsArray = false;
// always create new array, change from jQuery
destPropValue = [];
} else {
destPropValue = targetPropValue && $.isPlainObject(targetPropValue) ? targetPropValue : {};
}
// Never move original objects, clone them
target[propName] = $pt.mergeObject({ deep: deep, target: destPropValue, sources: [sourcePropValue] });
// Don't bring in undefined values
} else if (sourcePropValue !== undefined) {
target[propName] = sourcePropValue;
}
}
}
}
// Return the modified object
return target;
};
/**
* get value from json object
* @param jsonObject {{}}
* @param id {string} key of property, can be linked by underline
* @returns {*}
*/
$pt.getValueFromJSON = function (jsonObject, id) {
if (id.indexOf($pt.PROPERTY_SEPARATOR) != -1) {
// hierarchy id
var ids = id.split($pt.PROPERTY_SEPARATOR);
var parent = jsonObject;
var value = null;
var values = ids.map(function (id) {
if (parent == null) {
return null;
} else {
value = parent[id];
parent = value;
return value;
}
});
return values[values.length - 1];
} else {
return jsonObject[id];
}
};
$pt.setValueIntoJSON = function (jsonObject, id, value) {
if (id.indexOf($pt.PROPERTY_SEPARATOR) == -1) {
jsonObject[id] = value;
} else {
var ids = id.split($pt.PROPERTY_SEPARATOR);
var parent = jsonObject;
for (var index = 0, count = ids.length - 1; index < count; index++) {
if (parent[ids[index]] == null) {
// new object
parent[ids[index]] = {};
}
parent = parent[ids[index]];
}
parent[ids[ids.length - 1]] = value;
}
return $pt;
};
/**
* table validation result
* @type {class}
*/
var TableValidationResult = jsface.Class({
constructor: function () {
this.__errors = {};
this.__models = {};
this.__startKeyIndex = 1;
},
/**
* push error
* @param model {{}} element of array
* @param error {{}} error messages
*/
push: function (model, error) {
this.__errors[this.__startKeyIndex] = error;
this.__models[this.__startKeyIndex] = model;
this.__startKeyIndex++;
return this;
},
/**
* remove error
* @param model {{}} element of array
*/
remove: function (model) {
var _this = this;
Object.keys(this.__models).forEach(function (key) {
var item = _this.__models[key];
if (item == model) {
delete _this.__models[key];
delete _this.__errors[key];
}
});
return this;
},
/**
* has error
* @returns {boolean}
*/
hasError: function () {
return Object.keys(this.__models).length !== 0;
},
/**
* get error
* @param model {{}} element of array
* @returns {{}}
*/
getError: function (model) {
// var keys = Object.keys(this.__models);
// for (var index = 0, count = keys.length; index < count; index++) {
// var item = this.__models[keys[index]];
// if (item == model) {
// return this.__errors[keys[index]];
// }
// }
// return null;
var errors = Object.keys(this.__models).map(function (key) {
return this.__models[key] == model ? this.__errors[key] : null;
}.bind(this)).filter(function (error) {
return error != null;
});
return errors.length === 0 ? null : errors[0];
}
});
/**
* create table validation result
* @returns {