UNPKG

nest-parrot

Version:
1,692 lines (1,622 loc) 508 kB
(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 {