1 | /**
|
2 | * @namespace
|
3 | */
|
4 | var Utils = {};
|
5 |
|
6 | /**
|
7 | * @member {object}
|
8 | * @memberof QueryBuilder
|
9 | * @see Utils
|
10 | */
|
11 | QueryBuilder.utils = Utils;
|
12 |
|
13 | /**
|
14 | * @callback Utils#OptionsIteratee
|
15 | * @param {string} key
|
16 | * @param {string} value
|
17 | */
|
18 |
|
19 | /**
|
20 | * Iterates over radio/checkbox/selection options, it accept three formats
|
21 | *
|
22 | * @example
|
23 | * // array of values
|
24 | * options = ['one', 'two', 'three']
|
25 | * @example
|
26 | * // simple key-value map
|
27 | * options = {1: 'one', 2: 'two', 3: 'three'}
|
28 | * @example
|
29 | * // array of 1-element maps
|
30 | * options = [{1: 'one'}, {2: 'two'}, {3: 'three'}]
|
31 | *
|
32 | * @param {object|array} options
|
33 | * @param {Utils#OptionsIteratee} tpl
|
34 | */
|
35 | Utils.iterateOptions = function(options, tpl) {
|
36 | if (options) {
|
37 | if ($.isArray(options)) {
|
38 | options.forEach(function(entry) {
|
39 | // array of one-element maps
|
40 | if ($.isPlainObject(entry)) {
|
41 | $.each(entry, function(key, val) {
|
42 | tpl(key, val);
|
43 | return false; // break after first entry
|
44 | });
|
45 | }
|
46 | // array of values
|
47 | else {
|
48 | tpl(entry, entry);
|
49 | }
|
50 | });
|
51 | }
|
52 | // unordered map
|
53 | else {
|
54 | $.each(options, function(key, val) {
|
55 | tpl(key, val);
|
56 | });
|
57 | }
|
58 | }
|
59 | };
|
60 |
|
61 | /**
|
62 | * Replaces {0}, {1}, ... in a string
|
63 | * @param {string} str
|
64 | * @param {...*} args
|
65 | * @returns {string}
|
66 | */
|
67 | Utils.fmt = function(str, args) {
|
68 | if (!Array.isArray(args)) {
|
69 | args = Array.prototype.slice.call(arguments, 1);
|
70 | }
|
71 |
|
72 | return str.replace(/{([0-9]+)}/g, function(m, i) {
|
73 | return args[parseInt(i)];
|
74 | });
|
75 | };
|
76 |
|
77 | /**
|
78 | * Throws an Error object with custom name or logs an error
|
79 | * @param {boolean} [doThrow=true]
|
80 | * @param {string} type
|
81 | * @param {string} message
|
82 | * @param {...*} args
|
83 | */
|
84 | Utils.error = function() {
|
85 | var i = 0;
|
86 | var doThrow = typeof arguments[i] === 'boolean' ? arguments[i++] : true;
|
87 | var type = arguments[i++];
|
88 | var message = arguments[i++];
|
89 | var args = Array.isArray(arguments[i]) ? arguments[i] : Array.prototype.slice.call(arguments, i);
|
90 |
|
91 | if (doThrow) {
|
92 | var err = new Error(Utils.fmt(message, args));
|
93 | err.name = type + 'Error';
|
94 | err.args = args;
|
95 | throw err;
|
96 | }
|
97 | else {
|
98 | console.error(type + 'Error: ' + Utils.fmt(message, args));
|
99 | }
|
100 | };
|
101 |
|
102 | /**
|
103 | * Changes the type of a value to int, float or bool
|
104 | * @param {*} value
|
105 | * @param {string} type - 'integer', 'double', 'boolean' or anything else (passthrough)
|
106 | * @param {boolean} [boolAsInt=false] - return 0 or 1 for booleans
|
107 | * @returns {*}
|
108 | */
|
109 | Utils.changeType = function(value, type, boolAsInt) {
|
110 | switch (type) {
|
111 | // @formatter:off
|
112 | case 'integer': return parseInt(value);
|
113 | case 'double': return parseFloat(value);
|
114 | case 'boolean':
|
115 | var bool = value.trim().toLowerCase() === 'true' || value.trim() === '1' || value === 1;
|
116 | return boolAsInt ? (bool ? 1 : 0) : bool;
|
117 | default: return value;
|
118 | // @formatter:on
|
119 | }
|
120 | };
|
121 |
|
122 | /**
|
123 | * Escapes a string like PHP's mysql_real_escape_string does
|
124 | * @param {string} value
|
125 | * @returns {string}
|
126 | */
|
127 | Utils.escapeString = function(value) {
|
128 | if (typeof value != 'string') {
|
129 | return value;
|
130 | }
|
131 |
|
132 | return value
|
133 | .replace(/[\0\n\r\b\\\'\"]/g, function(s) {
|
134 | switch (s) {
|
135 | // @formatter:off
|
136 | case '\0': return '\\0';
|
137 | case '\n': return '\\n';
|
138 | case '\r': return '\\r';
|
139 | case '\b': return '\\b';
|
140 | default: return '\\' + s;
|
141 | // @formatter:off
|
142 | }
|
143 | })
|
144 | // uglify compliant
|
145 | .replace(/\t/g, '\\t')
|
146 | .replace(/\x1a/g, '\\Z');
|
147 | };
|
148 |
|
149 | /**
|
150 | * Escapes a string for use in regex
|
151 | * @param {string} str
|
152 | * @returns {string}
|
153 | */
|
154 | Utils.escapeRegExp = function(str) {
|
155 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
156 | };
|
157 |
|
158 | /**
|
159 | * Escapes a string for use in HTML element id
|
160 | * @param {string} str
|
161 | * @returns {string}
|
162 | */
|
163 | Utils.escapeElementId = function(str) {
|
164 | // Regex based on that suggested by:
|
165 | // https://learn.jquery.com/using-jquery-core/faq/how-do-i-select-an-element-by-an-id-that-has-characters-used-in-css-notation/
|
166 | // - escapes : . [ ] ,
|
167 | // - avoids escaping already escaped values
|
168 | return (str) ? str.replace(/(\\)?([:.\[\],])/g,
|
169 | function( $0, $1, $2 ) { return $1 ? $0 : '\\' + $2; }) : str;
|
170 | };
|
171 |
|
172 | /**
|
173 | * Sorts objects by grouping them by `key`, preserving initial order when possible
|
174 | * @param {object[]} items
|
175 | * @param {string} key
|
176 | * @returns {object[]}
|
177 | */
|
178 | Utils.groupSort = function(items, key) {
|
179 | var optgroups = [];
|
180 | var newItems = [];
|
181 |
|
182 | items.forEach(function(item) {
|
183 | var idx;
|
184 |
|
185 | if (item[key]) {
|
186 | idx = optgroups.lastIndexOf(item[key]);
|
187 |
|
188 | if (idx == -1) {
|
189 | idx = optgroups.length;
|
190 | }
|
191 | else {
|
192 | idx++;
|
193 | }
|
194 | }
|
195 | else {
|
196 | idx = optgroups.length;
|
197 | }
|
198 |
|
199 | optgroups.splice(idx, 0, item[key]);
|
200 | newItems.splice(idx, 0, item);
|
201 | });
|
202 |
|
203 | return newItems;
|
204 | };
|
205 |
|
206 | /**
|
207 | * Defines properties on an Node prototype with getter and setter.<br>
|
208 | * Update events are emitted in the setter through root Model (if any).<br>
|
209 | * The object must have a `__` object, non enumerable property to store values.
|
210 | * @param {function} obj
|
211 | * @param {string[]} fields
|
212 | */
|
213 | Utils.defineModelProperties = function(obj, fields) {
|
214 | fields.forEach(function(field) {
|
215 | Object.defineProperty(obj.prototype, field, {
|
216 | enumerable: true,
|
217 | get: function() {
|
218 | return this.__[field];
|
219 | },
|
220 | set: function(value) {
|
221 | var previousValue = (this.__[field] !== null && typeof this.__[field] == 'object') ?
|
222 | $.extend({}, this.__[field]) :
|
223 | this.__[field];
|
224 |
|
225 | this.__[field] = value;
|
226 |
|
227 | if (this.model !== null) {
|
228 | /**
|
229 | * After a value of the model changed
|
230 | * @event model:update
|
231 | * @memberof Model
|
232 | * @param {Node} node
|
233 | * @param {string} field
|
234 | * @param {*} value
|
235 | * @param {*} previousValue
|
236 | */
|
237 | this.model.trigger('update', this, field, value, previousValue);
|
238 | }
|
239 | }
|
240 | });
|
241 | });
|
242 | };
|