JsUtils

JavaScript Utility functions

目前为 2016-04-25 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/19117/121891/JsUtils.js

  1. /*
  2. ____________________
  3. < What amazing code! >
  4. --------------------
  5. \ ^__^
  6. \ (oo)\_______
  7. (__)\ )\/\
  8. ||----w |
  9. || ||
  10. */
  11. /**
  12. * Some helpful functions I made. all in SEF to make sure object scope is kept.
  13. *
  14. * All objects are static
  15. **/
  16.  
  17. /**********************************/
  18. /* JQUERY FUNCTIONS
  19. /**********************************/
  20. "use strict";
  21. (function ($) { // lets pass this jQuery so we know nothing else has messed with it
  22. /**
  23. * Add functions to jQuery
  24. */
  25. $.fn.extend({
  26. /**
  27. * Sort a given list.
  28. * You may pass in any amount of {String} parameters as you like, passing these in will be ingored by the sorting method.
  29. * The first parameter MUST be a boolean. this specifies if you want the ignored elements to be at the bottom of the list (top by default)
  30. *
  31. *
  32. * for example. sort a list, but ignore "Logout":
  33. * $("#myList").sortList(false,"Logout");
  34. * This will make "Logout" appear first in the list as it has been ignored by the sort function.
  35. *
  36. * to make logout appear last:
  37. * $("#myList").sortList(true,"Logout");
  38. *
  39. * Arguments are optinal and do not need to be supplied (to sort the whole list)
  40. *
  41. * parameters:
  42. * {boolean} pushToBottom
  43. * {String} elements to ignore
  44. */
  45. sortList: function () {
  46. return function () {
  47. if (this.length === 0) {
  48. return;
  49. }
  50. if (!this.is("ul, ol")) {
  51. throw "Error: sortList can only be used on a ul or ol tag!";
  52. }
  53. var args = arguments;
  54. var pushToBottom = false;
  55.  
  56. if (typeof args[0] !== "undefined") {
  57. if (typeof args[0] !== "boolean") {
  58. throw "the first argument must be a boolean";
  59. }
  60. pushToBottom = args[0];
  61. }
  62.  
  63. var excludedResults = []; // keep array of excluded li elements
  64.  
  65. for (var x = 0; x < this.length; x++) {
  66. var currentList = $(this.get(x));
  67.  
  68. var listitems = currentList.children('li').not(function (i, e) {
  69. for (var i = 1; i < args.length; i++) {
  70. var currentArg = args[i];
  71. if ($.trim($(this).text()) === currentArg) {
  72. excludedResults.push($(this));
  73. return true;
  74. } else {
  75. continue;
  76. }
  77. }
  78. return false;
  79. }).get();
  80. listitems.sort(function (a, b) {
  81. return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
  82. });
  83. $.each(listitems, function (k, v) {
  84. currentList.append(v);
  85. });
  86. if (pushToBottom === true) {
  87. $.each(excludedResults, function (k, v) {
  88. $(this).parent().append(this);
  89. });
  90. }
  91. }
  92. return this;
  93. }.apply(this, arguments);
  94. },
  95.  
  96. sortSelect: function () {
  97. if (!this.is("select")) {
  98. throw "SortSelect can only be used on a select node";
  99. }
  100.  
  101. var ignore = [];
  102. $.each(this.children(), function (k, v) {
  103. if (typeof $(this).data("sort-ignore") === "string") {
  104. ignore.push(this);
  105. }
  106. })
  107.  
  108. var options = this.children("option").not(function (el) {
  109. if ($.inArray(this, ignore) >= 0) {
  110. return true;
  111. }
  112. return false;
  113. });
  114. options.sort(function (a, b) {
  115. return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
  116. });
  117.  
  118. for (var i = 0; i < options.length; i++) {
  119. this.append(options[i]);
  120. }
  121.  
  122. return this;
  123. },
  124.  
  125. center: function () {
  126. this.css("position", "absolute");
  127. this.css("top", Math.max(0, (($(window).height() - $(this).outerHeight()) / 2) + $(window).scrollTop()) + "px");
  128. this.css("left", Math.max(0, (($(window).width() - $(this).outerWidth()) / 2) + $(window).scrollLeft()) + "px");
  129. return this;
  130. },
  131.  
  132. /**
  133. * Add sortTable function to jQuery
  134. * Using jQuery for this is highly inefficient, but I do not know any pure JS to do the same thing.
  135. * to use do $("#myTable").sortTable(1);
  136. */
  137. sortTable: function (col) {
  138. typeof col === ("undefined") ? col = -1 : col = col;
  139. if (this.length === 0) {
  140. return;
  141. }
  142. if (typeof col !== "number") {
  143. throw "col must be of type number. Got type" + typeof col + " instead";
  144. }
  145. if (!this.is("table")) {
  146. throw "Error: sortList can only be used on a table!";
  147. }
  148.  
  149. var rows = $(this).find("tbody").children('tr').get();;
  150. if (col === -1) {
  151. rows.sort(function (a, b) {
  152. return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
  153. });
  154. } else {
  155. rows.sort(function (a, b) {
  156. var textA = $(a).children('td').eq(col).text().toUpperCase();
  157. var textB = $(b).children('td').eq(col).text().toUpperCase();
  158. return textA.localeCompare(textB);
  159. });
  160. }
  161. for (var i = 0; i < rows.length; i++) {
  162. $(this).find("tbody").append(rows[i]); // .append() will move them for you
  163. }
  164. return this;
  165. },
  166.  
  167. /**
  168. * Will change the text of anything passed in, even if the node is a text node
  169. */
  170. changetext: function (text) {
  171. if (this.length === 0) {
  172. return;
  173. }
  174. this.contents().filter(function () {
  175. return this.nodeType === 3;
  176. })[0].nodeValue = text;
  177.  
  178. return this;
  179. },
  180.  
  181. /**
  182. * Enables a given anchor or button depending on the specified parameter.
  183. * If true is specified then the button / anchor is enabled, otherwise the button / anchor is disabled
  184. */
  185. enableButton: function (enable) {
  186. if (this.length === 0) {
  187. return;
  188. }
  189.  
  190. if (!this.is("a, button")) {
  191. throw "This function may only be used on a button or anchor tag";
  192. }
  193.  
  194.  
  195. if (typeof enable === "undefined" || enable === null) {
  196. throw "The argument passed to this function must be a boolean";
  197. }
  198. if (enable === true) {
  199. this.prop('disabled', false).removeClass("disabled");
  200. } else {
  201. this.prop('disabled', true).addClass("disabled");
  202. }
  203.  
  204. return this;
  205. }
  206. });
  207. overrideMethods();
  208. customFilters();
  209. customEvents();
  210.  
  211. function overrideMethods() {
  212. hide();
  213. show();
  214.  
  215. function hide() {
  216. var originalHideMethod = jQuery.fn.hide;
  217.  
  218. $.fn.hide = function () {
  219. originalHideMethod.apply(this, arguments);
  220.  
  221. if (!this.is(":hidden")) {
  222. this.addClass("hide");
  223. }
  224. return this;
  225. };
  226. }
  227.  
  228. /**
  229. * Because bootstrap's 3 hide class has an important in the display (display: none !important), any attempt to call the native "show()" in jquery will fail to show the tag
  230. * This will show the tag using the native function, if it is still hidden, it will strip the hide class off the element and apply inline css
  231. */
  232. function show() {
  233. var originalShowMethod = jQuery.fn.show;
  234.  
  235. $.fn.show = function () {
  236. originalShowMethod.apply(this, arguments);
  237.  
  238. var type = getElementDefaultDisplay(this.prop("nodeName"));
  239. if (this.is(":hidden")) { // if still hidden, then bootstrap's hide class was used
  240. this.removeClass("hide").css("display", type);
  241. }
  242. return this;
  243. };
  244. }
  245.  
  246. /**
  247. * Get the default style of a tag (div = block, span = inline, etc...)
  248. * @param {String} tag Tag name
  249. * @returns {String} Default style
  250. */
  251. function getElementDefaultDisplay(tag) {
  252. var cStyle;
  253. var t = document.createElement(tag);
  254. var gcs = "getComputedStyle" in window;
  255.  
  256. document.body.appendChild(t);
  257. cStyle = (gcs ? window.getComputedStyle(t, "") : t.currentStyle).display;
  258. document.body.removeChild(t);
  259.  
  260. return cStyle;
  261. }
  262. }
  263.  
  264. function customFilters() {
  265. offScreen();
  266.  
  267. function offScreen() {
  268. Object.defineProperty(jQuery.expr.filters, "offscreen", {
  269. value: function (_el) {
  270. var el = $(_el);
  271. var win = $(window);
  272.  
  273. var viewport = {
  274. top: win.scrollTop(),
  275. left: win.scrollLeft()
  276. };
  277. viewport.right = viewport.left + win.width();
  278. viewport.bottom = viewport.top + win.height();
  279.  
  280. var bounds = el.offset();
  281. bounds.right = bounds.left + el.outerWidth();
  282. bounds.bottom = bounds.top + el.outerHeight();
  283.  
  284. return (viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom);
  285. }
  286. })
  287. }
  288. }
  289.  
  290. function customEvents() {
  291. classChanged();
  292.  
  293. function classChanged() {
  294. var originalAddClassMethod = jQuery.fn.addClass;
  295.  
  296. jQuery.fn.addClass = function () {
  297. var result = originalAddClassMethod.apply(this, arguments);
  298.  
  299. jQuery(this).trigger('cssClassChanged');
  300.  
  301. return result;
  302. }
  303. };
  304. }
  305.  
  306. }(jQuery));
  307. /**********************************/
  308. /* NON-JQUERY FUNCTIONS
  309. /**********************************/
  310.  
  311.  
  312.  
  313. /**********************************/
  314. /* MODULE FUNCTIONS
  315. /**********************************/
  316.  
  317. /**
  318. * Generic object functions
  319. */
  320. var ObjectUtil = (function () {
  321. "use strict";
  322. /**
  323. * Return true or false if the current object is an instance of jQuery
  324. */
  325. var isjQuery = function (obj) {
  326. if (obj instanceof jQuery || 'jquery' in Object(obj)) {
  327. return true;
  328. } else {
  329. return false;
  330. }
  331. };
  332.  
  333. /**
  334. * Returns a jquery element from a jquery object array
  335. * @throws {TypeError} If the supplied parameters are not the correct type
  336. * @param {Object} elm The Jquery element to use
  337. * @param {Number} index The index to use for the array
  338. * @returns {Object} Jquery object from the array
  339. */
  340. var getElementFromJqueryArray = function (elm, index) {
  341. if (!isjQuery(elm)) {
  342. throw new TypeError("element must be an instance of Jquery");
  343. }
  344. if (typeof index !== "number") {
  345. throw new TypeError("element must be a number");
  346. }
  347.  
  348. return elm.filter(function (i) {
  349. return i === index;
  350. });
  351. };
  352.  
  353. var guid = function () {
  354. function s4() {
  355. return Math.floor((1 + Math.random()) * 0x10000)
  356. .toString(16)
  357. .substring(1);
  358. }
  359. return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
  360. s4() + '-' + s4() + s4() + s4();
  361. };
  362.  
  363. /**
  364. * Given an array of arguments {Strings} this will return whether all of the strings are not null and have a length of greater than zero
  365. * after trimming the leading and trailing whitespace.
  366. * Throws an exception if argument is not of type string
  367. */
  368. var validString = function () {
  369. return _validString.apply(this, arguments);
  370. };
  371.  
  372. var stringContains = function (string, contains) {
  373. return ~string.indexOf(contains) < 0;
  374. };
  375.  
  376. var deepCompare = function deepCompare() {
  377. var i, l, leftChain, rightChain;
  378.  
  379. function compare2Objects(x, y) {
  380. var p;
  381.  
  382. if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
  383. return true;
  384. }
  385.  
  386. if (x === y) {
  387. return true;
  388. }
  389.  
  390. if ((typeof x === 'function' && typeof y === 'function') ||
  391. (x instanceof Date && y instanceof Date) ||
  392. (x instanceof RegExp && y instanceof RegExp) ||
  393. (x instanceof String && y instanceof String) ||
  394. (x instanceof Number && y instanceof Number)) {
  395. return x.toString() === y.toString();
  396. }
  397.  
  398. if (!(x instanceof Object && y instanceof Object)) {
  399. return false;
  400. }
  401.  
  402. if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
  403. return false;
  404. }
  405.  
  406. if (x.constructor !== y.constructor) {
  407. return false;
  408. }
  409.  
  410. if (x.prototype !== y.prototype) {
  411. return false;
  412. }
  413.  
  414. if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
  415. return false;
  416. }
  417.  
  418. for (p in y) {
  419. if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
  420. return false;
  421. } else if (typeof y[p] !== typeof x[p]) {
  422. return false;
  423. }
  424. }
  425.  
  426. for (p in x) {
  427. if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
  428. return false;
  429. } else if (typeof y[p] !== typeof x[p]) {
  430. return false;
  431. }
  432.  
  433. switch (typeof (x[p])) {
  434. case 'object':
  435. case 'function':
  436.  
  437. leftChain.push(x);
  438. rightChain.push(y);
  439.  
  440. if (!compare2Objects(x[p], y[p])) {
  441. return false;
  442. }
  443.  
  444. leftChain.pop();
  445. rightChain.pop();
  446. break;
  447.  
  448. default:
  449. if (x[p] !== y[p]) {
  450. return false;
  451. }
  452. break;
  453. }
  454. }
  455.  
  456. return true;
  457. }
  458.  
  459. if (arguments.length < 1) {
  460. return true;
  461. }
  462.  
  463. for (i = 1, l = arguments.length; i < l; i++) {
  464.  
  465. leftChain = [];
  466. rightChain = [];
  467.  
  468. if (!compare2Objects(arguments[0], arguments[i])) {
  469. return false;
  470. }
  471. }
  472. return true;
  473. };
  474.  
  475.  
  476. /**
  477. * Extend an object from the base object
  478. * @throws {Error} If none of the supplied objects are constructors
  479. * @param {Function} base Constructor of the base object (to extend from)
  480. * @param {Function} sub Constructor of the sub Object (this object will be the one to be extended)
  481. * @returns {Function} Chained constructor of the sub object
  482. */
  483. var extendObj = function (base, sub) {
  484. if (typeof sub !== "function") {
  485. throw new Error("sub must be a Constructor");
  486. }
  487. if (typeof base !== "function") {
  488. throw new Error("base must be a Constructor");
  489. }
  490. sub.prototype = Object.create(base.prototype);
  491. sub.prototype.constructor = sub;
  492. // sub.base = base.prototype;
  493. return sub;
  494. }
  495.  
  496. /**
  497. * PRIVATE
  498. * this is called by the public validString because it takes varargs because apply can't be called on the revealing pattern
  499. */
  500. var _validString = function () {
  501. if (arguments == null || arguments.length === 0) {
  502. return false;
  503. }
  504.  
  505. for (var i = 0; i < arguments.length; i++) {
  506. var currString = arguments[i];
  507.  
  508. if (currString === undefined || currString === null || currString.length === 0) {
  509. return false
  510. }
  511.  
  512.  
  513. if (typeof currString !== "string") {
  514. return false;
  515. }
  516.  
  517.  
  518. if ($.trim(currString).length === 0) {
  519. return false;
  520. }
  521. }
  522. return true;
  523. };
  524.  
  525. /**
  526. * Return an object of public functions
  527. */
  528. return {
  529. isjQuery: isjQuery,
  530. validString: validString,
  531. extendObj: extendObj,
  532. stringContains: stringContains,
  533. getElementFromJqueryArray: getElementFromJqueryArray,
  534. deepCompare: deepCompare,
  535. guid: guid
  536. };
  537. }());
  538.  
  539. var DomUtil = (function DomUtil() {
  540. var injectCss = function (css) {
  541. if (_isUrl(css)) {
  542. $("<link>").prop({
  543. "type": "text/css",
  544. "rel": "stylesheet"
  545. }).attr("href", css).appendTo("head");
  546. } else {
  547. $("<style>").prop("type", "text/css").html(css).appendTo("head");
  548. }
  549. };
  550.  
  551. var _isUrl = function (url) {
  552. var matcher = new RegExp(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/);
  553. return matcher.test(url);
  554. };
  555. return {
  556. injectCss: injectCss
  557. };
  558. }());
  559.  
  560. /**
  561. * Generic XML functions
  562. */
  563. var XmlUtil = (function () {
  564. "use strict";
  565.  
  566. /**
  567. * Encode XML nodes into HTML entities
  568. */
  569. var encodeXml = function (xml) {
  570. if (typeof xml !== "string") {
  571. return;
  572. }
  573. return xml.replace(/&/g, '&amp;')
  574. .replace(/</g, '&lt;')
  575. .replace(/>/g, '&gt;')
  576. .replace(/"/g, '&quot;')
  577. .replace(/'/g, '&apos;');
  578. };
  579.  
  580. /**
  581. * Decode encoded HTML Entities.
  582. * Note: The jQuery method "html" will do this automatically.
  583. */
  584. var decodeXml = function (xml) {
  585. if (typeof xml !== "string") {
  586. return;
  587. }
  588. return xml.replace(/&apos;/g, "'")
  589. .replace(/&quot;/g, '"')
  590. .replace(/&gt;/g, '>')
  591. .replace(/&lt;/g, '<')
  592. .replace(/&amp;/g, '&');
  593. };
  594.  
  595. return {
  596. encodeXml: encodeXml,
  597. decodeXml: decodeXml
  598. };
  599. }());
  600.  
  601. var XssEncoder = (function (_super) {
  602. return _super;
  603. }(XmlUtil || {}));
  604. /**
  605. * static object containing helpful file functions
  606. * functions include:
  607. *
  608. * supportsFileApi
  609. * getFileExt
  610. * getFileNameFromInput
  611. *
  612. */
  613. var fileUtil = (function () {
  614. "use strict";
  615.  
  616. var getFileNameFromInput = function (input) {
  617. var currentFile = null;
  618. // convert jQuery to pure JS
  619. var input = (function () {
  620. if (ObjectUtil.isjQuery(input)) {
  621. if (input.length === 0) {
  622. throw "Input does not exist";
  623. }
  624. return input[0];
  625. } else {
  626. return input;
  627. }
  628. }());
  629.  
  630. if (input === null) {
  631. throw "Input does not exist";
  632. }
  633.  
  634. // if the input is a URL
  635. if (input.getAttribute("type") === "url") {
  636. return _getFileNameFromUrl(input.value);
  637. }
  638.  
  639. // use the old way of getting a file (not guaranteed to be correct)
  640. if (!supportsFileApi()) {
  641. return _getFileNameFromUrl(input.value);
  642. }
  643.  
  644. currentFile = input.files[0];
  645.  
  646. return currentFile.name;
  647. };
  648.  
  649. var supportsFileApi = function () {
  650. if (window.File && window.FileReader && window.FileList && window.Blob) {
  651. return true;
  652. } else {
  653. return false;
  654. }
  655. };
  656.  
  657.  
  658. var _getFileNameFromUrl = function (path) {
  659. if (typeof path !== "string") {
  660. throw "path does not contain a value, this maybe because you did not pass in an object that represents an input";
  661. }
  662. return path.replace(/^C:\\fakepath\\/i, "").split('\\').pop().split('/').pop();
  663. };
  664.  
  665. /**
  666. * PUBLIC
  667. *
  668. * Will return the file extension from a given filename
  669. * @param {String} Name of the file
  670. */
  671. var getFileExt = function (fullFileName) {
  672. if (ObjectUtil.isjQuery(fullFileName)) {
  673. fullFileName = fullFileName.val();
  674. }
  675. var ext = fullFileName.substr(fullFileName.lastIndexOf('.') + 1);
  676. if (ext === "") {
  677. throw "No file extension";
  678. } else {
  679. return ext.toLowerCase();
  680. }
  681. };
  682.  
  683.  
  684. var _getHumanReadableSize = function (bytes, decimals) {
  685. if (bytes == 0) {
  686. return '0 Byte';
  687. }
  688. var k = 1024;
  689. var dm = decimals + 1 || 3;
  690. var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  691. var i = Math.floor(Math.log(bytes) / Math.log(k));
  692. return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i];
  693. };
  694.  
  695.  
  696. var ImageUtils = (function () {
  697. /**
  698. * Get the image info from a file select input
  699. * @param {object} input The Jquery or pure dom element representing the input
  700. * @param {function} callBack The callback function will supply 2 values: the first is an object containing the image infomation and the second is the actual image as a dom element you can use for image preview
  701. * @throws {ImageBoundException} If there was an unsupported filetype
  702. */
  703. var getImageInfoFromInput = function getImageInfoFromInput(input, callBack) {
  704. if (!supportsFileApi()) {
  705. return;
  706. }
  707. var input = (function () {
  708. if (ObjectUtil.isjQuery(input)) {
  709. if (input.length === 0) {
  710. throw "Input does not exist";
  711. }
  712. return input[0];
  713. } else {
  714. return input;
  715. }
  716. }());
  717. if (BrowserUtils.isChrome()) { // chrome does not support createObjectURL on URL but does on webkitURL... Chrome conplains that you use webkitURL
  718. window.URL = window.webkitURL;
  719. }
  720. var useBlob = true && window.URL;
  721. var files = input.files;
  722. if (!files) {
  723. throw new TypeError("File upload not supported by your browser.");
  724. }
  725. if (files.length > 0) {
  726. var file = files[0];
  727. if ((/\.(png|jpg|gif)$/i).test(file.name)) {
  728. var deferred = $.Deferred();
  729. $.when(readImage(file, deferred)).done(function (value, img) {
  730. callBack.call(img, value, img);
  731. });
  732. } else {
  733. throw new ImageBoundException("Unsupported Image extension: '" + getFileExt(file.name) + "' file name: '" + file.name + "'");
  734. }
  735. }
  736.  
  737.  
  738. function readImage(file, defer) {
  739. var reader = new FileReader();
  740. reader.addEventListener("load", function () {
  741. var image = new Image();
  742. image.addEventListener("load", function () {
  743. var returnObj = {};
  744. Object.defineProperties(returnObj, {
  745. "fileName": {
  746. value: file.name
  747. },
  748. "imageWidth": {
  749. value: image.width
  750. },
  751. "imageHeight": {
  752. value: image.height
  753. },
  754. "imageType": {
  755. value: file.type
  756. },
  757. "fileSize": {
  758. value: _getHumanReadableSize(file.size)
  759. },
  760. "base64": {
  761. value: reader.result
  762. }
  763. })
  764. Object.freeze(returnObj); // stop anything from changing anything in this object
  765. defer.resolve(returnObj, this);
  766. });
  767. image.src = useBlob ? window.URL.createObjectURL(file) : reader.result;
  768. if (useBlob) {
  769. window.URL.revokeObjectURL(file);
  770. }
  771. });
  772. reader.readAsDataURL(file);
  773. return defer.promise();
  774. }
  775. }
  776. return {
  777. getImageInfoFromInput: getImageInfoFromInput
  778. };
  779. }());
  780.  
  781. /**
  782. * Get the image info from a file select input
  783. * @param {String} b64Data The base 64 data
  784. * @param {String} contentType The content type of this blob example : (image/png)
  785. * @param {String} sliceSize The slices of the array to use, this is optinal and if omited will default to 512bits per array. this acts as a buffer
  786. */
  787. var b64toBlob = function b64toBlob(b64Data, contentType, sliceSize) {
  788. contentType = contentType || '';
  789. sliceSize = sliceSize || 512;
  790.  
  791. var byteCharacters = atob(b64Data);
  792. var byteArrays = [];
  793.  
  794. for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
  795. var slice = byteCharacters.slice(offset, offset + sliceSize);
  796.  
  797. var byteNumbers = new Array(slice.length);
  798. for (var i = 0; i < slice.length; i++) {
  799. byteNumbers[i] = slice.charCodeAt(i);
  800. }
  801.  
  802. var byteArray = new Uint8Array(byteNumbers);
  803.  
  804. byteArrays.push(byteArray);
  805. }
  806.  
  807. var blob = new Blob(byteArrays, {
  808. type: contentType
  809. });
  810. return blob;
  811. }
  812.  
  813. /**
  814. * Return an object of public functions
  815. */
  816. return {
  817. getFileExt: getFileExt,
  818. getFileNameFromInput: getFileNameFromInput,
  819. supportsFileApi: supportsFileApi,
  820. ImageUtils: ImageUtils,
  821. b64toBlob: b64toBlob
  822. };
  823. }());
  824.  
  825.  
  826. /**
  827. * Drag and drop object
  828. * Extends the fileUtil object
  829. */
  830. var dragDrop = (function (_super, window) {
  831. var canSupportDragAndDrop = function () {
  832. return _super.supportsFileApi();
  833. };
  834. return {
  835. fileUtil: _super,
  836. canSupportDragAndDrop: canSupportDragAndDrop
  837. };
  838. }(fileUtil || {}, window));
  839.  
  840.  
  841. var ArrayUtils = (function () {
  842. "use strict";
  843.  
  844. /**
  845. * DEPRICATED!! Used yourArray._remove_(itemToRemove);
  846. */
  847. var removeFromArray = function (arr, obj) {
  848. if (console) {
  849. console.warn("The use of 'removeFromArray' is depricated, please use 'yourArray._remove_(item)' instead");
  850. }
  851. var i = arr.length;
  852. while (i--) {
  853. if (ObjectUtil.isjQuery(obj)) {
  854. if ($(arr[i]).is(obj)) {
  855. arr.splice(i, 1);
  856. }
  857. } else {
  858. if (arr[i] === obj) {
  859. arr.splice(i, 1);
  860. }
  861. }
  862. }
  863. };
  864.  
  865. var arrayCopy = function (array, deep) {
  866. if (!Array.isArray(array)) {
  867. throw new TypeError("array to copy must be of type 'Array'");
  868. }
  869. deep = typeof deep === "undefined" ? false : deep;
  870. return $.extend(deep, [], array);
  871. };
  872.  
  873. return {
  874. removeFromArray: removeFromArray,
  875. arrayCopy: arrayCopy
  876. };
  877. }());
  878.  
  879. var ColourUtils = (function () {
  880. "use strict";
  881. var h = Math.random();
  882. var golden_ratio_conjugate = 0.618033988749895;
  883.  
  884. //http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
  885. var getRandomColour = function () {
  886. //Used to generate random colors
  887. h += golden_ratio_conjugate
  888. h %= 1
  889.  
  890. function hsv_to_rgb(h, s, v) {
  891. var h_i = parseInt(h * 6);
  892. var f = h * 6 - h_i;
  893. var p = v * (1 - s);
  894. var q = v * (1 - f * s);
  895. var t = v * (1 - (1 - f) * s);
  896. var r, g, b;
  897. if (h_i == 0) {
  898. r = v;
  899. g = t;
  900. b = p;
  901. } else if (h_i == 1) {
  902. r = q;
  903. g = v;
  904. b = p;
  905. } else if (h_i == 2) {
  906. r = p;
  907. g = v;
  908. b = t;
  909. } else if (h_i == 3) {
  910. r = p;
  911. g = q;
  912. b = v;
  913. } else if (h_i == 4) {
  914. r = t;
  915. g = p;
  916. b = v;
  917. } else if (h_i == 5) {
  918. r = v;
  919. g = p;
  920. b = q;
  921. }
  922. return "rgb(" + parseInt(r * 256) + "," + parseInt(g * 256) + "," + parseInt(b * 256) + ")";
  923. }
  924. return hsv_to_rgb(h, 0.7, 0.75);
  925. };
  926.  
  927. return {
  928. getRandomColour: getRandomColour
  929. };
  930. }());
  931.  
  932. var BrowserUtils = (function () {
  933. "use strict";
  934. var detectMobile = function () {
  935. var check = false;
  936. (function (a) {
  937. if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
  938. check = true;
  939. }
  940. })(navigator.userAgent || navigator.vendor || window.opera);
  941. return check;
  942. };
  943.  
  944. var isChrome = function () {
  945. if (window.chrome !== null && window.navigator.vendor === 'Google Inc.') {
  946. return true;
  947. } else {
  948. return false;
  949. }
  950. };
  951.  
  952. var isFireFox = function () {
  953. if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
  954. return true;
  955. } else {
  956. return false;
  957. }
  958. };
  959.  
  960. var isWebkit = function () {
  961. return /webkit/.test(navigator.userAgent.toLowerCase());
  962. };
  963.  
  964. var isEdgeOrIe = function isEdgeOrIe() {
  965. return Edge.isEdge() || IeUtils.isIe();
  966. };
  967.  
  968. var Edge = (function () {
  969. var getVersion = function getVersion() {
  970. var ua = window.navigator.userAgent;
  971. var edge = ua.indexOf('Edge/');
  972. if (edge > 0) {
  973. return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
  974. } else {
  975. return null;
  976. }
  977. };
  978.  
  979. var isEdge = function isEdge() {
  980. return getVersion() !== null;
  981. };
  982. return {
  983. getVersion: getVersion,
  984. isEdge: isEdge
  985. }
  986. }());
  987.  
  988. return {
  989. detectMobile: detectMobile,
  990. isChrome: isChrome,
  991. isFireFox: isFireFox,
  992. isWebkit: isWebkit,
  993. isEdgeOrIe: isEdgeOrIe,
  994. Edge: Edge
  995. };
  996. }());
  997.  
  998.  
  999. /**
  1000. * An object for detecting ie versions
  1001. * Functions available are:
  1002. *
  1003. * isIe
  1004. * getIeVersion
  1005. */
  1006. var IeUtils = (function () {
  1007.  
  1008. "use strict";
  1009.  
  1010. /**
  1011. * returns true or false if using IE10
  1012. */
  1013. var _isIE10 = function () {
  1014. return navigator.appVersion.indexOf("MSIE 10") !== -1;
  1015. };
  1016.  
  1017. /**
  1018. * returns true or false if using IE11
  1019. */
  1020. var _isIE11 = function () {
  1021. return !!navigator.userAgent.match(/Trident.*rv[ :]*11\./);
  1022. };
  1023.  
  1024. var _isIE9 = function () {
  1025. return navigator.appVersion.indexOf("MSIE 9.") != -1;
  1026. };
  1027.  
  1028. /**
  1029. * Returns if using ie
  1030. * returns boolean
  1031. */
  1032. var isIe = function () {
  1033. var ua = window.navigator.userAgent;
  1034. var msie = ua.indexOf("MSIE ");
  1035. if (msie > 0 | _isIE11() || _isIE10()) {
  1036. return true;
  1037. } else {
  1038. return false;
  1039. }
  1040. };
  1041.  
  1042. /**
  1043. * returns the version of IE used by the client
  1044. * returns string or null if not using IE
  1045. */
  1046. var getIeVersion = function () {
  1047. if (!isIe()) {
  1048. return null;
  1049. }
  1050. var actualVersion = "unknown";
  1051. var jscriptMap = {
  1052. "5.5": 5.5,
  1053. "5.6": 6,
  1054. "5.7": 7,
  1055. "5.8": 8,
  1056. "9": 9,
  1057. };
  1058. var jscriptVersion = new Function("/*@cc_on return @_jscript_version; @*/")();
  1059. if (typeof jscriptVersion !== "undefined") {
  1060. actualVersion = jscriptMap[jscriptVersion];
  1061. // Somehow, the script version was retrived but not known (emulation mode can cause this issue)
  1062. if (typeof actualVersion === "undefined") {
  1063. if (_isIE10()) {
  1064. actualVersion = 10;
  1065. } else if (_isIE11()) {
  1066. actualVersion = 11;
  1067. } else if (_isIE9()) {
  1068. actualVersion = 9;
  1069. } else {
  1070. actualVersion = "unknown";
  1071. }
  1072. }
  1073. } else {
  1074. if (_isIE10()) {
  1075. actualVersion = 10;
  1076. } else if (_isIE11()) {
  1077. actualVersion = 11;
  1078. }
  1079. }
  1080. return actualVersion;
  1081. };
  1082.  
  1083. /**
  1084. * Returns an object of public functions
  1085. */
  1086. return {
  1087. isIe: isIe,
  1088. getIeVersion: getIeVersion
  1089. };
  1090. }());
  1091.  
  1092.  
  1093. var VersionUtil = (function () {
  1094. var isHigherVersion = function (version1, version2) {
  1095. if (typeof version1 !== "string" && typeof version2 !== "string") {
  1096. throw "versions must be strings";
  1097. }
  1098. var v1Comps = version1.split(".");
  1099. var v2Comps = version2.split(".");
  1100. var limit = (v1Comps.length > v2Comps.length) ? v2Comps.length : v1Comps.length;
  1101. for (var i = 0; i < limit; i++) {
  1102. var v1part = parseInt(v1Comps[i]);
  1103. var v2part = parseInt(v2Comps[i]);
  1104. if (v1part > v2part) {
  1105. return true;
  1106. }
  1107. if (v2part > v1part) {
  1108. return false;
  1109. }
  1110. }
  1111. return v1Comps.length >= v2Comps.length;
  1112. };
  1113.  
  1114. return {
  1115. isHigherVersion: isHigherVersion
  1116. };
  1117. }());
  1118.  
  1119. var QueryString = (function () {
  1120. var query_string = {};
  1121. var query = window.location.search.substring(1);
  1122. var vars = query.split("&");
  1123. for (var i = 0; i < vars.length; i++) {
  1124. var pair = vars[i].split("=");
  1125. if (typeof query_string[pair[0]] === "undefined") {
  1126. query_string[pair[0]] = pair[1];
  1127. } else if (typeof query_string[pair[0]] === "string") {
  1128. var arr = [query_string[pair[0]], pair[1]];
  1129. query_string[pair[0]] = arr;
  1130. } else {
  1131. query_string[pair[0]].push(pair[1]);
  1132. }
  1133. }
  1134. return query_string;
  1135. }());
  1136.  
  1137.  
  1138. /**********************************/
  1139. /* PROTOTYPE FUNCTIONS
  1140. /**********************************/
  1141.  
  1142. /**
  1143. * Adding sort array of objects to the prototype.
  1144. * To use this. call sortBy on an array of objects. for example given this object:
  1145. * var arr= [
  1146. * {
  1147. * AgencyId:"SDMX",
  1148. * Id:"AGENCIES",
  1149. * Name:"SDMX Agency Scheme"
  1150. * },
  1151. * {
  1152. * AgencyId:"METATECH",
  1153. * Id:"AGENCIES",
  1154. * Name:"Agencies"
  1155. * },
  1156. * ]
  1157. * this is an array of 2 objects.
  1158. * if we want to sort this by the property "Name" we can do this
  1159. * arr.sortBy("Name");
  1160. * if you want to sort by "name" and "Id", you can just do
  1161. * arr.sortBy("Name", "Id");
  1162. * this takes an unlimited amount of args.
  1163. *
  1164. * If you wish to sort Descending, append a "-" to your argument
  1165. *
  1166. * sort by "Name" Descending: arr.sortBy("-Name");
  1167. */
  1168. (function () {
  1169. function _sortByAttr(attr) {
  1170. var sortOrder = 1;
  1171. if (attr[0] == "-") {
  1172. sortOrder = -1;
  1173. attr = attr.substr(1);
  1174. }
  1175. return function (a, b) {
  1176. if (typeof a[attr] === "undefined" || typeof b[attr] === "undefined") {
  1177. throw "There is no property with the value of " + attr.toString() + " inside this object";
  1178. }
  1179. var result = a[attr].toUpperCase().localeCompare(b[attr].toUpperCase());
  1180. return result * sortOrder;
  1181. }
  1182. }
  1183.  
  1184. function _getSortFunc() {
  1185. if (arguments.length == 0) {
  1186. throw "Zero length arguments not allowed for Array.sortBy()";
  1187. }
  1188. var args = arguments;
  1189. return function (a, b) {
  1190. for (var result = 0, i = 0; result == 0 && i < args.length; i++) {
  1191. result = _sortByAttr(args[i])(a, b);
  1192. }
  1193. return result;
  1194. }
  1195. }
  1196. Object.defineProperty(Array.prototype, "sortBy", {
  1197. enumerable: false,
  1198. writable: true,
  1199. value: function () {
  1200. return this.sort(_getSortFunc.apply(null, arguments));
  1201. }
  1202. });
  1203. }());
  1204. (function () {
  1205. if (Array.prototype._remove_) {
  1206. return;
  1207. }
  1208. Object.defineProperty(Array.prototype, "_remove_", {
  1209. enumerable: false,
  1210. /**
  1211. * Removes all occurence of specified item from array
  1212. * @param this Array
  1213. * @param itemToRemove Item to remove from array
  1214. */
  1215. value: function (itemToRemove) {
  1216. var i = this.length;
  1217. try {
  1218. var itemToRemoveStr = null
  1219. itemToRemoveStr = JSON.stringify(itemToRemove);
  1220. } catch (e) {}
  1221. while (i--) {
  1222. var currentItem = this[i];
  1223. var currentItemStr = null;
  1224. if (itemToRemoveStr !== null) {
  1225. try {
  1226. currentItemStr = JSON.stringify(currentItem);
  1227. } catch (e) {}
  1228. }
  1229. if (ObjectUtil.isjQuery(itemToRemove)) {
  1230. if ($(currentItem).is(itemToRemove)) {
  1231. this.splice(i, 1);
  1232. }
  1233. } else {
  1234. if (currentItemStr !== null && itemToRemoveStr !== null) {
  1235. if (currentItemStr === itemToRemoveStr) {
  1236. this.splice(i, 1);
  1237. continue;
  1238. }
  1239. }
  1240. if (ObjectUtil.deepCompare(itemToRemove, currentItem)) {
  1241. this.splice(i, 1);
  1242. continue;
  1243. }
  1244. continue;
  1245. }
  1246. }
  1247. }
  1248. });
  1249.  
  1250. if (Array.prototype.moveItem) {
  1251. return;
  1252. }
  1253. Object.defineProperty(Array.prototype, "moveItem", {
  1254. value: function (old_index, new_index) {
  1255. while (old_index < 0) {
  1256. old_index += this.length;
  1257. }
  1258. while (new_index < 0) {
  1259. new_index += this.length;
  1260. }
  1261. if (new_index >= this.length) {
  1262. var k = new_index - this.length;
  1263. while ((k--) + 1) {
  1264. this.push(undefined);
  1265. }
  1266. }
  1267. this.splice(new_index, 0, this.splice(old_index, 1)[0]);
  1268. }
  1269. })
  1270. }());
  1271. (function () {
  1272. Object.defineProperty(Function.prototype, "getName", {
  1273. value: function () {
  1274. try {
  1275. return /^function\s+([\w\$]+)\s*\(/.exec(this.toString())[1];
  1276. } catch (e) {
  1277. return null;
  1278. }
  1279. }
  1280. })
  1281. }());
  1282. var LinkedArray = (function () {
  1283. var LinkedArray = function LinkedArray(type) {
  1284. if (typeof type === "undefined") {
  1285. throw new TypeError("a type must be supplied");
  1286. }
  1287. var arr = [];
  1288. Object.setPrototypeOf(arr, LinkedArray.prototype); // this is bad. however, this is one of them "if it's last resort" things. becase this truly is last resort, we must edit the __proto__ directly to extend array, object.create will not work
  1289. Object.defineProperty(arr, "_type", {
  1290. value: type
  1291. });
  1292. return arr;
  1293. };
  1294.  
  1295. LinkedArray.prototype = new Array;
  1296.  
  1297. LinkedArray.prototype.push = function push() {
  1298. var argarr = arguments;
  1299. var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
  1300. for (var i = 0; i < args.length; i++) {
  1301. var carrArg = args[i];
  1302. if (Array.isArray(this._type)) {
  1303. if (!Array.isArray(carrArg)) {
  1304. throw new TypeError("this array only accepts instance of arrays");
  1305. }
  1306. } else if (typeof this._type === "object" || typeof this._type === "function") {
  1307. if (typeof carrArg === "undefined") {
  1308. break;
  1309. }
  1310. if (!(carrArg instanceof this._type)) {
  1311. var msg = "This array only accepts instance of {supplied object}";
  1312. if (this._type.name !== "") {
  1313. msg = "This array only accepts instance of " + this._type.name;
  1314. } else {
  1315. try {
  1316. msg = "This array only accepts instance of " + this._type.getName();
  1317. } catch (e) {}
  1318. }
  1319. throw new TypeError(msg);
  1320. }
  1321. } else if (typeof this._type === "number" && typeof carrArg !== "number") {
  1322. throw new TypeError("this array only accepts " + typeof this._type);
  1323. } else if (typeof this._type === "boolean" && typeof carrArg !== "boolean") {
  1324. throw new TypeError("this array only accepts " + typeof this._type);
  1325. } else if (typeof this._type === "string" && typeof carrArg !== "string") {
  1326. throw new TypeError("this array only accepts " + typeof this._type);
  1327. }
  1328. }
  1329. args.forEach(function (element, index, array) {
  1330. Array.prototype.push.call(this, element);
  1331. }, this);
  1332. };
  1333.  
  1334. LinkedArray.prototype.getType = function getType() {
  1335. if (typeof this._type === "function") {
  1336. return new(this._type);
  1337. } else if (typeof this._type === "object") {
  1338. return this._type
  1339. } else {
  1340. return typeof this._type;
  1341. }
  1342.  
  1343. };
  1344.  
  1345. return LinkedArray;
  1346. }());
  1347. /**********************************/
  1348. /* Custom Exceptions
  1349. /**********************************/
  1350. var ImageBoundException = (function () {
  1351. function ImageBoundException(message) {
  1352. this.name = 'ImageBoundException';
  1353. this.message = message || 'An error occured with this image';
  1354. this.stack = (new Error()).stack;
  1355. }
  1356. ImageBoundException.prototype = Object.create(Error.prototype);
  1357. ImageBoundException.prototype.constructor = ImageBoundException;
  1358. return ImageBoundException;
  1359. }());
  1360. /**********************************/
  1361. /* PROTOTYPE FUNCTIONS END
  1362. /**********************************/