root/trunk/modules/utils.jsm

Revision 1047, 6.9 kB (checked in by MaierMan, 5 months ago)
  • Use naturalSort in select
  • Provide Resource Name column (hidden per default)
Line 
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is DownThemAll Utilities module.
15  *
16  * The Initial Developer of the Original Code is Nils Maier
17  * Portions created by the Initial Developer are Copyright (C) 2008
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  *   Nils Maier <MaierMan@web.de>
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */
36
37 const EXPORTED_SYMBOLS = [
38   'atos',
39   'newUUIDString',
40   'range',
41   'hexdigest',
42   'merge',
43   'clone',
44   'formatNumber',
45   'formatTimeDelta',
46   'getTimestamp',
47   'naturalSort',
48 ];
49
50 const Cc = Components.classes;
51 const Ci = Components.interfaces;
52 const Cr = Components.results;
53
54
55
56 /**
57  * returns a new UUID in string representation
58  * @return String UUID
59  * @author Nils
60  */
61 function newUUIDString() {
62   let uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
63  
64   newUUIDString = function() {
65     return uuidgen.generateUUID().toString();
66   }
67   return newUUIDString();
68 }
69
70 /**
71  * Range generator (python style). Difference: step direction is initialized accordingly if corresponding parameter is omitted.
72  * @param start Optional. Start value (default: 0)
73  * @param stop Stop value (exclusive)
74  * @param step Optional. Step value (default: 1/-1)
75  * @author Nils
76  */
77 function range() {
78   if (arguments.length == 0) {
79     throw Components.results.NS_ERROR_INVALID_ARG;
80   }
81   let start = 0, stop = new Number(arguments[0]), step;
82   if (arguments.length >= 2) {
83     start = stop;
84     stop = new Number(arguments[1]);
85   }
86   if (arguments.length >= 3) {
87     step = new Number(arguments[2]);
88   }
89   else {
90     step = stop - start > 0 ? 1 : -1;
91   }
92   if (!isFinite(start) || !isFinite(stop) || !isFinite(step) || step == 0) {
93     throw Cr.NS_ERROR_INVALID_ARG;
94   }
95   if ((stop - start) / step < 0) {
96     // negative range
97     return;
98   }
99   stop += -Math.abs(step) / step;
100   stop += step - ((stop - start) % step);
101   for (; start != stop; start += step) {
102     yield start;
103   }
104
105 }
106
107 /**
108  * Builds the hexdigest of (binary) data
109  * @param {Object} data
110  * @return {String} hexdigest
111  */
112 function hexdigest(data) {
113   data = data.toString();
114   return [('0' + data.charCodeAt(i).toString(16)).slice(-2) for (i in range(data.length))].join(''); 
115 }
116
117 /**
118  * Merges the enumeratable properties of two objects   
119  * @param {Object} me Object that has the properties added the properties
120  * @param {Object} that Object of which the properties are taken
121  */
122 function merge(me, that) {
123   for (let c in that) {
124     me[c] = that[c];
125   }
126 }
127
128 /**
129  * (Almost) Clones an object. Not instanceof safe :p
130  * @param {Object} obj
131  * @return {Object} Copy of obj
132  */
133 function clone(obj) {
134   var rv = {};
135   merge(rv, obj);
136   merge(rv.prototype, this.prototype);
137   rv.constructor = this.constructor;
138   return rv;
139 }
140
141 /**
142  * Cast non-strings to strings (using toSource if required instead of toString()
143  * @param {Object} data
144  */
145 function atos(data) {
146   if (typeof(data) == 'string') {
147     return data;
148   }
149   if (data instanceof String || typeof(data) == 'object') {
150     try {
151       return data.toSource();
152     }
153     catch (ex) {
154       // fall-trough
155     }
156   }
157   return data.toString();
158 }
159
160 /**
161  * Head-Pads a number so that at it contains least "digits" digits.
162  * @param {Object} num The number in question
163  * @param {Object} digits Number of digits the results must contain at least
164  */
165 function formatNumber(num, digits) {
166   let rv = atos(num);
167   digits = Number(digits);
168   if (!isFinite(digits)) {
169     digits = 3;
170   }
171   for (let i = rv.length; i < digits; ++i) {
172     rv = '0' + rv;
173   }
174   return rv;
175 }
176
177 /**
178  * Formats a time delta (seconds)
179  * @param {Number} delta in seconds
180  * @return {String} formatted result
181  */
182 function formatTimeDelta(delta) {
183   let rv = (delta < 0) ? '-' : '';
184
185   delta = Math.abs(delta);
186   let h = Math.floor(delta / 3600);
187   let m = Math.floor((delta % 3600) / 60);
188   let s = Math.floor(delta % 60);
189  
190   if (h) {
191     rv += formatNumber(h, 2) + ':';
192   }
193   return rv + formatNumber(m, 2) + ':' + formatNumber(s, 2);
194 }
195
196 /**
197  * Converts a Datestring into an integer timestamp.
198  * @param {Object} str Datestring or null for current time.
199  */
200 function getTimestamp(str) {
201   if (!str) {
202     return Date.now();
203   }
204   let rv = Date.parse(atos(str));
205   if (!isFinite(rv)) {
206     throw new Error('invalid date');
207   }
208   return rv;
209 }
210
211 function naturalSort(arr, mapper) {
212   if (typeof mapper != 'function' && !(mapper instanceof Function)) {
213     mapper = function(e) e;
214   }
215   let isDigit = function(a, i) {
216     i = a[i];
217     return i >= '0' && i <= '9';
218   };
219   let compare = function(a, b) {
220     return a === b ? 0 : (a < b ? -1 : 1);
221   }
222   arr = arr.map(
223     function(b) {
224       let e = mapper(b);
225       if (e == null || e == undefined || typeof e == 'number') {
226         return {elem: b, chunks: [e]};
227       }
228       let a = e.toString().replace(/\b(?:a|one|the)\b/g, ' ').replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ').toLowerCase();
229       let len = a.length;
230       if (!len) {
231         return {elem: b, chunks: [a]};
232       }
233       let rv = [];
234       let last = isDigit(a, 0);
235       let cur = last;
236       start = 0;
237    
238       for (let i = 0; i < len; ++i) {
239         cur = isDigit(a, i);
240         if (cur != last) {
241           rv.push(cur ? a.substr(start, i - start) : Number(a.substr(start, i - start)));
242           start = i;
243           last = cur;
244         }
245       }
246       if (!rv.length || len - start != 1) {
247         rv.push(cur ? Number(a.substr(start)) : a.substr(start));
248       }
249       return {elem: b, chunks: rv};
250     }
251   );
252   arr.sort(
253     function (a, b) {
254       let ai, bi;
255       [a, b] = [a.chunks, b.chunks];
256       let m = Math.max(a.length, b.length);
257       for (let i = 0; i < m; ++i) {
258         let ai = a[i], bi = b[i];
259         let rv = compare(typeof ai, typeof bi);
260         if (rv) {
261           return rv;
262         }
263         rv = compare(ai, bi);
264         if (rv) {
265           return rv;
266         }
267       }
268       return a.length - b.length;
269     }
270   );
271   return arr.map(function(a) a.elem);
272 }
Note: See TracBrowser for help on using the browser.