UNPKG

6.58 kBJavaScriptView Raw
1/**
2 * @namespace
3 */
4var Utils = {};
5
6/**
7 * @member {object}
8 * @memberof QueryBuilder
9 * @see Utils
10 */
11QueryBuilder.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 */
35Utils.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 */
67Utils.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 */
84Utils.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 */
109Utils.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 */
127Utils.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 */
154Utils.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 */
163Utils.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 */
178Utils.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 */
213Utils.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};