bitwrench 1.2.13 → 1.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/codeql-analysis.yml +72 -0
- package/bitwrench.css +1 -1
- package/bitwrench.js +5 -1
- package/bitwrench.min.js +1 -1
- package/bitwrench_ESM.js +3202 -0
- package/examples/example10.html +84 -0
- package/examples/example8.html +27 -0
- package/examples/example9.html +102 -0
- package/package.json +6 -6
- package/tools/umd2ModuleHack.js +32 -0
package/bitwrench_ESM.js
ADDED
|
@@ -0,0 +1,3202 @@
|
|
|
1
|
+
export let bw = (()=>{
|
|
2
|
+
|
|
3
|
+
var bw = {};
|
|
4
|
+
bw.exportName = "bw"; //
|
|
5
|
+
bw.exportModuleType = "AMD"; //default export see UMD wrapper above for more info this. it allows the consumer to know how this was loaded.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// ===================================================================================
|
|
9
|
+
bw.choice = function (x,choices,def) {
|
|
10
|
+
/**
|
|
11
|
+
bw.choice(x,choices-dictionary, default)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Allows a dictionary to be used as a switch statement, including functions.
|
|
15
|
+
|
|
16
|
+
example:
|
|
17
|
+
colors = {"red": 1, "blue": 2, "aqua" : function(z){return z+"marine"}};
|
|
18
|
+
bw.choice("red",colors,"0") ==> "1"
|
|
19
|
+
bw.choice("shiny",colors,"0") ==> "0"
|
|
20
|
+
bw.choice("aqua",colors) ==> "aquamarine"
|
|
21
|
+
*/
|
|
22
|
+
var z = (x in choices) ? choices[x] : def;
|
|
23
|
+
return _to(z) == "function" ? z(x) : z;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
// ===================================================================================
|
|
28
|
+
bw.jsonClone = function (x) {
|
|
29
|
+
/**
|
|
30
|
+
bw.jsonClone(object)
|
|
31
|
+
|
|
32
|
+
crude deep copy by value of an object as long as no js dates or functions
|
|
33
|
+
*/
|
|
34
|
+
return JSON.parse(JSON.stringify(x));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// ===================================================================================
|
|
39
|
+
bw.typeOf = function (x, baseTypeOnly) {
|
|
40
|
+
/**
|
|
41
|
+
_to(x, baseTypeOnly) returns a useful typeOf the object.
|
|
42
|
+
|
|
43
|
+
_to(2) // "number"
|
|
44
|
+
bw.typeof( function(){}) // "function"
|
|
45
|
+
|
|
46
|
+
function Car(make, model, year) {
|
|
47
|
+
this.make = make;
|
|
48
|
+
this.model = model;
|
|
49
|
+
this.year = year;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
x = new Car("Ford", "Escape", 2009);
|
|
53
|
+
|
|
54
|
+
_to(Car) // "function"
|
|
55
|
+
_to(x) // "Car" ---> returns correct object type
|
|
56
|
+
_to(x,true) // "object" ---> returns base object type
|
|
57
|
+
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
//A useable typeof operator. See this fantastic reference for a starter
|
|
61
|
+
//https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
|
|
62
|
+
|
|
63
|
+
if (x === null)
|
|
64
|
+
return "null";
|
|
65
|
+
|
|
66
|
+
var y = (typeof x == "undefined") ? "undefined" : (({ /*empty*/}).toString.call(x).match(/\s([a-zA-Z]+)/)[1].toLocaleLowerCase());
|
|
67
|
+
|
|
68
|
+
if ((y != "object") && (y != "function"))
|
|
69
|
+
return y;
|
|
70
|
+
if (baseTypeOnly == true) // so if undefind or anything but true
|
|
71
|
+
return y;
|
|
72
|
+
|
|
73
|
+
var r = y;
|
|
74
|
+
try {
|
|
75
|
+
r = (x.constructor.name.toLocaleLowerCase() == y.toLocaleLowerCase()) ? y : x.constructor.name; // return object's name e.g.
|
|
76
|
+
}
|
|
77
|
+
catch (e) {/*empty*/}
|
|
78
|
+
if (r == "object") {
|
|
79
|
+
if (x["_is_BW_HTMLNode"] == true )
|
|
80
|
+
r="BW_HTMLNode";
|
|
81
|
+
}
|
|
82
|
+
return r;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
var _to = bw.typeOf;
|
|
86
|
+
bw.to = _to;
|
|
87
|
+
//===============================================
|
|
88
|
+
// internally used type check and assign function
|
|
89
|
+
bw.typeAssign = function (a, typeString, trueValue, falseValue) {
|
|
90
|
+
/**
|
|
91
|
+
bw.typeAssign(variable, typeString, trueValue, falseValue)
|
|
92
|
+
typeAssign is used to see if the argument a is of type typeString as defined by _to().
|
|
93
|
+
if it is then trueValue is returned else falseValue.
|
|
94
|
+
|
|
95
|
+
bw.typeAssign("23","number","is a number!", "not a number!") ==> "is a number!"
|
|
96
|
+
bw.typeAssign([23],"number","is a number!", "not a number!") ==> "not a number!" // is an array of length 1
|
|
97
|
+
|
|
98
|
+
can also supply list of types
|
|
99
|
+
bw.typeAssign(23,["string","number"], "string or num", "something else") ==> "string or num"
|
|
100
|
+
bw.typeAssign(true,["string","number"], "string or num", "something else") ==> "something else"
|
|
101
|
+
*/
|
|
102
|
+
if (["string","array"].indexOf(_to(typeString)) == -1) // typeString must be a string or an arrag or strings
|
|
103
|
+
typeString = "notValidType";
|
|
104
|
+
|
|
105
|
+
if (_to(typeString) == "string")
|
|
106
|
+
typeString = [typeString];
|
|
107
|
+
|
|
108
|
+
return (typeString.indexOf(_to(a)) >= 0) ? trueValue : falseValue;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
var _toa = bw.toa = bw.typeAssign; // eslint-disable-line no-unused-vars
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
//===============================================
|
|
115
|
+
// internally used type check and assign function with functional support (trueValue or falseValue can be functions which are passed the param a)
|
|
116
|
+
|
|
117
|
+
bw.typeConvert = function (a, typeString, trueValue, falseValue) {
|
|
118
|
+
/**
|
|
119
|
+
bw.typeConvert(variable, typeString, trueValue, falseValue)
|
|
120
|
+
typeConvert is used to see if the argument a is of type typeString as defined by _to().
|
|
121
|
+
if it is then trueValue is returned else falseValue.
|
|
122
|
+
|
|
123
|
+
bw.typeConvert("23","number","is a number!", "not a number!") ==> "is a number!"
|
|
124
|
+
bw.typeConvert([23],"number","is a number!", "not a number!") ==> "not a number!" // is an array of length 1
|
|
125
|
+
|
|
126
|
+
can also supply list of types
|
|
127
|
+
bw.typeConvert(23,["string","number"], "string or num", "something else") ==> "string or num"
|
|
128
|
+
bw.typeConvert(true,["string","number"], "string or num", "something else") ==> "something else"
|
|
129
|
+
|
|
130
|
+
bw.typeConvert(23,["string","number"],function(x){return x+1},function(){return function(x){return x+2}})}) ==> 24
|
|
131
|
+
|
|
132
|
+
bw.typeConvert(23,["string"],function(x){return x+1},function(){return function(x){return x+2;}})}) ==> function(x){return x+2;}
|
|
133
|
+
|
|
134
|
+
however typeConvert also allows functions (as apposed to typeAssign)
|
|
135
|
+
*/
|
|
136
|
+
if (["string","array"].indexOf(_to(typeString)) == -1) // typeString must be a string or an arrag or strings
|
|
137
|
+
typeString = "notValidType";
|
|
138
|
+
|
|
139
|
+
if (_to(typeString) == "string")
|
|
140
|
+
typeString = [typeString];
|
|
141
|
+
|
|
142
|
+
return (typeString.indexOf(_to(a)) >= 0) ? (_to(trueValue) == "function") ? trueValue(a) : trueValue : ( _to(falseValue) == "function") ? falseValue(a): falseValue;
|
|
143
|
+
};
|
|
144
|
+
//var _tc = bw.typeConvert;
|
|
145
|
+
var _toc = bw.tc = bw.typeConvert; // eslint-disable-line no-unused-vars
|
|
146
|
+
//===============================================
|
|
147
|
+
// internally used function for options copy
|
|
148
|
+
// keys in opts are copied to dopts (or overwrite options in dopts)
|
|
149
|
+
/* istanbul ignore next */
|
|
150
|
+
var optsCopy = function(dopts,opts) {
|
|
151
|
+
/* istanbul ignore next */
|
|
152
|
+
if ((_to(opts) == "object") && (_to(dopts)=="object")) {
|
|
153
|
+
var i;
|
|
154
|
+
for (i in opts) {
|
|
155
|
+
if ((_to(opts[i]) == "object" )||(_to(dopts[i])=="object")) {
|
|
156
|
+
var j;
|
|
157
|
+
for (j in opts[i])
|
|
158
|
+
dopts[i][j] = opts[i][j];
|
|
159
|
+
}
|
|
160
|
+
else
|
|
161
|
+
dopts[i] = opts[i];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return dopts;
|
|
165
|
+
};
|
|
166
|
+
bw._oc = optsCopy;
|
|
167
|
+
// ===================================================================================
|
|
168
|
+
bw.arrayUniq = function (x){
|
|
169
|
+
/**
|
|
170
|
+
arrayUniq(x)
|
|
171
|
+
returns uniq elements of simple array x.
|
|
172
|
+
*/
|
|
173
|
+
if (_to(x) != "array")
|
|
174
|
+
return [];
|
|
175
|
+
return x.filter (function (v, i, arr) {return (arr.indexOf(v)==i);});
|
|
176
|
+
};
|
|
177
|
+
// ===================================================================================
|
|
178
|
+
bw.arrayBinA = function (a,b) {
|
|
179
|
+
/**
|
|
180
|
+
arrayBinA(x)
|
|
181
|
+
returns intersection elements of to simple arrays a and b
|
|
182
|
+
*/
|
|
183
|
+
if ((_to(a)!="array") || (_to(b)!== "array"))
|
|
184
|
+
return [];
|
|
185
|
+
return bw.arrayUniq(a.filter(function(n) { return b.indexOf(n) !== -1;}));
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
bw.arrayBNotInA = function (a,b) {
|
|
189
|
+
/**
|
|
190
|
+
arrayBNotinA(x)
|
|
191
|
+
returns elements of b not present in a
|
|
192
|
+
*/
|
|
193
|
+
if ((_to(a)!="array") || (_to(b)!== "array"))
|
|
194
|
+
return [];
|
|
195
|
+
return bw.arrayUniq(b.filter(function(n) { return a.indexOf(n) < 0;}));
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
//===============================================
|
|
199
|
+
/* istanbul ignore next */
|
|
200
|
+
bw.DOMIsElement = function(el) {
|
|
201
|
+
/**
|
|
202
|
+
@method bw.DOMIsElement() - returns whether a supplied element is a HTML DOM element. only useful in browser,
|
|
203
|
+
*/
|
|
204
|
+
var r = false;
|
|
205
|
+
try {
|
|
206
|
+
if(_to(el)== "undefined")
|
|
207
|
+
return r;
|
|
208
|
+
if ((bw.isNodeJS() == false) || (typeof Element == "function"))
|
|
209
|
+
r = el instanceof Element;
|
|
210
|
+
|
|
211
|
+
}
|
|
212
|
+
catch(e) {
|
|
213
|
+
r = (typeof HTMLElement === "object" ? el instanceof HTMLElement : //DOM2
|
|
214
|
+
el && (typeof el === "object") && (el !== null) && (el.nodeType === 1) && (typeof el.nodeName==="string")
|
|
215
|
+
);
|
|
216
|
+
bw.logd(e.toString());
|
|
217
|
+
}
|
|
218
|
+
return r;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
var _isEl = bw.DOMIsElement;
|
|
222
|
+
|
|
223
|
+
//===============================================
|
|
224
|
+
/* istanbul ignore next */
|
|
225
|
+
bw.DOMGetElements = function (el, type) {
|
|
226
|
+
/**
|
|
227
|
+
@method DOMGetElements(el, type) returns an array of DOM elements (if running in browser)
|
|
228
|
+
|
|
229
|
+
@param {string | DOM_node} el - if string uses CSS selector other wise if already a DOM element returns itself
|
|
230
|
+
@return an js array of zero or more matching DOM nodes
|
|
231
|
+
|
|
232
|
+
DOMGetElements always looks in the root.
|
|
233
|
+
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/*
|
|
237
|
+
TODO:
|
|
238
|
+
var container = document.querySelector("#test");
|
|
239
|
+
var matches = container.querySelectorAll("div.highlighted > p");
|
|
240
|
+
|
|
241
|
+
*/
|
|
242
|
+
var r=[],a=[],i;
|
|
243
|
+
|
|
244
|
+
if (bw.isNodeJS() == false)
|
|
245
|
+
{ // we're running in a browser
|
|
246
|
+
if (_isEl(el))
|
|
247
|
+
return [el];
|
|
248
|
+
if (_to(el) == "string") { // now its a string so we have choices..
|
|
249
|
+
type = _toa(type,"string",type,"auto"); // auto means detect whether has a # or . in front of it
|
|
250
|
+
el.trim();
|
|
251
|
+
if (type == "auto")
|
|
252
|
+
type = bw.choice(el[0],{".":"className", "#":"id"},"tagName");
|
|
253
|
+
type=type.toLowerCase();
|
|
254
|
+
switch (type) {
|
|
255
|
+
case "id" : //get Element by ID
|
|
256
|
+
el = (el[0]=="#") ? el.substring(1,el.length) : el;
|
|
257
|
+
a = document.getElementById(el);
|
|
258
|
+
a = _toa(a,"null",[],[a]);
|
|
259
|
+
break;
|
|
260
|
+
case "classname": // get Elements by class name
|
|
261
|
+
el = (el[0]==".") ? el.substring(1,el.length) : el;
|
|
262
|
+
a = document.getElementsByClassName(el);
|
|
263
|
+
break;
|
|
264
|
+
case "tagname" : // get Elements by tag name
|
|
265
|
+
a = document.getElementsByTagName(el);
|
|
266
|
+
break;
|
|
267
|
+
case "name":
|
|
268
|
+
a = document.getElementsByName(el);
|
|
269
|
+
break;
|
|
270
|
+
case "css" :
|
|
271
|
+
a = document.querySelectorAll(el);
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
a = document.querySelectorAll(el);
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
for (i in a)
|
|
278
|
+
r.push(a[i]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return r.filter(function(x){return _isEl(x);});
|
|
283
|
+
};
|
|
284
|
+
//var _els = bw.DOMGetElements;
|
|
285
|
+
|
|
286
|
+
// =============================================================================================
|
|
287
|
+
/* istanbul ignore next */
|
|
288
|
+
bw.DOMSetElements = function(domElement,param) {
|
|
289
|
+
/**
|
|
290
|
+
@method DOMSetElements(domElement, param) sets DOM elements with the supplied (optional) params
|
|
291
|
+
|
|
292
|
+
@param {string | array | dict |function} - params to set on DOMElements
|
|
293
|
+
@return an js array of zero or more matching DOM nodes
|
|
294
|
+
*/
|
|
295
|
+
|
|
296
|
+
var els = bw.DOMGetElements(domElement);
|
|
297
|
+
if (els==[])
|
|
298
|
+
bw.log("dom element not found");
|
|
299
|
+
|
|
300
|
+
var i,l,e, ef = function(x,p){bw.log(x,p);};
|
|
301
|
+
for (l=0; l<els.length; l++) {
|
|
302
|
+
e = els[l];
|
|
303
|
+
switch(_to(param)) {
|
|
304
|
+
case "array":
|
|
305
|
+
try{
|
|
306
|
+
for (i=0; i<param.length; i++) e[param[i][0]] = param[i][1];
|
|
307
|
+
}
|
|
308
|
+
catch(d) {ef(d,param);}
|
|
309
|
+
break;
|
|
310
|
+
case "object":
|
|
311
|
+
try {
|
|
312
|
+
for (i in param) e[i] = param[i];
|
|
313
|
+
}
|
|
314
|
+
catch(d) {ef(d,param);}
|
|
315
|
+
break;
|
|
316
|
+
case "string":
|
|
317
|
+
try {
|
|
318
|
+
e.innerHTML = param;
|
|
319
|
+
}
|
|
320
|
+
catch(d) {ef(d,param);}
|
|
321
|
+
break;
|
|
322
|
+
case "function":
|
|
323
|
+
try {
|
|
324
|
+
param(e); // apply a function to e
|
|
325
|
+
}
|
|
326
|
+
catch(d) {ef(d,param);}
|
|
327
|
+
break;
|
|
328
|
+
default: break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return els;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
bw.DOM = bw.DOMSetElements; //short hand
|
|
336
|
+
//================================================================================
|
|
337
|
+
bw.DOMInsertElement = function (parentEl, htmldata, putFirst) {
|
|
338
|
+
/**
|
|
339
|
+
DOMInsertElement (attachEl, html , putFirst)
|
|
340
|
+
creates an HTML element (browser only). If an attachment element is provided it will attach the new element to the attachElement.
|
|
341
|
+
if putFirst == true it is made the first child of the attachEl else it is the lastChild of the attachEl
|
|
342
|
+
*/
|
|
343
|
+
var el = null;
|
|
344
|
+
if (bw.isNodeJS() == false) {
|
|
345
|
+
if (bw.DOMIsElement(htmldata))
|
|
346
|
+
el = htmldata;
|
|
347
|
+
else {
|
|
348
|
+
el = document.createElement("div"); //outer wrapper
|
|
349
|
+
el.innerHTML = bw.html(htmldata);
|
|
350
|
+
el = el.firstChild; // get our element back
|
|
351
|
+
}
|
|
352
|
+
if (parentEl) {
|
|
353
|
+
parentEl = bw.DOM(parentEl)[0];
|
|
354
|
+
if (putFirst ) {
|
|
355
|
+
parentEl.insertBefore(el, parentEl.firstChild); // put it first
|
|
356
|
+
}
|
|
357
|
+
else
|
|
358
|
+
parentEl.appendChild(el); // put it last
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return el;
|
|
362
|
+
};
|
|
363
|
+
bw.DOMIns = bw.DOMInsertElement;
|
|
364
|
+
|
|
365
|
+
// =============================================================================================
|
|
366
|
+
bw.htmlToElement = function (htmldata) {
|
|
367
|
+
var el=null;
|
|
368
|
+
if (bw.isNodeJS() == false) {
|
|
369
|
+
if (bw.DOMIsElement(htmldata))
|
|
370
|
+
el = htmldata;
|
|
371
|
+
else {
|
|
372
|
+
el = document.createElement("div"); //outer wrapper
|
|
373
|
+
el.innerHTML = bw.html(htmldata);
|
|
374
|
+
el = el.firstChild; // get our element back
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return el;
|
|
378
|
+
};
|
|
379
|
+
// =============================================================================================
|
|
380
|
+
|
|
381
|
+
bw.DOMReplaceElement = function(oldEl, newEl) {
|
|
382
|
+
if (bw.isNodeJS() == false) {
|
|
383
|
+
var e = bw.DOM(oldEl)[0];
|
|
384
|
+
return e.parentNode.replaceChild(bw.htmlToElement(newEl), e);
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
};
|
|
388
|
+
// =============================================================================================
|
|
389
|
+
/**
|
|
390
|
+
bitwrench: color functions (used for theming and interpolations)
|
|
391
|
+
|
|
392
|
+
bitwrench color functons operate using this internal color representation model:
|
|
393
|
+
[c0, c1, c2, alpha, model]
|
|
394
|
+
where c0, c1, c2 are model dependant
|
|
395
|
+
alpha represents the transperancy
|
|
396
|
+
model is a color model string (lowercase) "rgb", or "hsl" (compatible with HTML/CSS colors)
|
|
397
|
+
|
|
398
|
+
colorParse() ==> take an input color of anymodel and output a bw [c0,c1,c2,a,m] array
|
|
399
|
+
*/
|
|
400
|
+
bw.colorInterp = function(x, in0, in1, colors, stretch) {
|
|
401
|
+
/**
|
|
402
|
+
@method colorInterp (x, lo, hi, colors[], stretch) - interpolate between and array of colors.
|
|
403
|
+
x is a number between the numbers in0 <= x <= in1
|
|
404
|
+
colors is an array of colors supplied in rgb format e.g. ["#123", "#234"]
|
|
405
|
+
colors can be anylength
|
|
406
|
+
*/
|
|
407
|
+
var c = _toa(colors,"array",colors,["#000","#fff"]); // make sure we have an array of colors
|
|
408
|
+
c = c.length == 0 ? ["#000","#fff"] : c; // no colors provide .. interp grayscale is default
|
|
409
|
+
if (c.length == 1)
|
|
410
|
+
return c[0];
|
|
411
|
+
//ok now we we have an array of atleast length 2 which hopefully contains colors.
|
|
412
|
+
c = c.map(function(x){return bw.colorParse(x);}); // all colors will now be converted to bw RGB format
|
|
413
|
+
var a = bw.mapScale(x,in0,in1,0,c.length-1,{clip: true, expScale: stretch});
|
|
414
|
+
var i = bw.clip(Math.floor(a),0,c.length-2);
|
|
415
|
+
var r = a-i;
|
|
416
|
+
var _f = function(x) {return bw.mapScale(r,0,1, c[i][x],c[i+1][x],{clip:true});};
|
|
417
|
+
return [_f(0), _f(1), _f(2),_f(3),"rgb"];
|
|
418
|
+
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
// =============================================================================================
|
|
423
|
+
bw.colorHslToRgb = function (h, s, l, a, rnd){
|
|
424
|
+
/**
|
|
425
|
+
@method colorHslToRgb
|
|
426
|
+
Converts an HSL color value to RGB. Conversion formula
|
|
427
|
+
|
|
428
|
+
Assumes h is [0..360] , s, and l are contained in the set [0 .. 100].
|
|
429
|
+
returns r, g, and b in the set [0, 255].
|
|
430
|
+
|
|
431
|
+
@param {number} h The hue [0..360]
|
|
432
|
+
@param {number} s The saturation [0..100]
|
|
433
|
+
@param {number} l The lightness [0..100]
|
|
434
|
+
|
|
435
|
+
OR...
|
|
436
|
+
|
|
437
|
+
pass the colors as a bitwrench color array as a single parameter:
|
|
438
|
+
|
|
439
|
+
colorHslToRgb([h,s,l,a,"hsl"])
|
|
440
|
+
|
|
441
|
+
@return {Array} The RGB representation as [r, g, b, alpha, "rgb"]
|
|
442
|
+
|
|
443
|
+
last parameter rnd rounds the results to 0..255. set to false to eliminate rounding. This can be useful for chained calcs
|
|
444
|
+
|
|
445
|
+
see : adapted from http://hsl2rgb.nichabi.com/javascript-function.php
|
|
446
|
+
|
|
447
|
+
*/
|
|
448
|
+
if (_to(h)=="array") { // handles colors of [h,s,l,a,"hsl"]
|
|
449
|
+
s=h[1];
|
|
450
|
+
l=h[2];
|
|
451
|
+
a=h[3];
|
|
452
|
+
h=h[0]; //do this last so it doesn't overwrite iself
|
|
453
|
+
}
|
|
454
|
+
var _fn = rnd == false ? function(x){return x;} : function(x){return bw.clip(Math.round(x),0,255);} ;
|
|
455
|
+
|
|
456
|
+
var r,g,b,c,x,m;
|
|
457
|
+
h = (h+360)%360;
|
|
458
|
+
h /= 60;
|
|
459
|
+
if (h < 0) h = 6 - (-h % 6);
|
|
460
|
+
h %= 6;
|
|
461
|
+
|
|
462
|
+
s = Math.max(0, Math.min(1, s / 100));
|
|
463
|
+
l = Math.max(0, Math.min(1, l / 100));
|
|
464
|
+
|
|
465
|
+
c = (1 - Math.abs((2 * l) - 1)) * s;
|
|
466
|
+
x = c * (1 - Math.abs((h % 2) - 1));
|
|
467
|
+
|
|
468
|
+
if (h < 1) {
|
|
469
|
+
r = c; g = x; b = 0;
|
|
470
|
+
} else if (h < 2) {
|
|
471
|
+
r = x; g = c; b = 0;
|
|
472
|
+
} else if (h < 3) {
|
|
473
|
+
r = 0; g = c; b = x;
|
|
474
|
+
} else if (h < 4) {
|
|
475
|
+
r = 0; g = x; b = c;
|
|
476
|
+
} else if (h < 5) {
|
|
477
|
+
r = x; g = 0; b = c;
|
|
478
|
+
} else {
|
|
479
|
+
r = c; g = 0; b = x;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
m = l - c / 2;
|
|
483
|
+
r = (r + m) * 255;
|
|
484
|
+
g = (g + m) * 255;
|
|
485
|
+
b = (b + m) * 255;
|
|
486
|
+
return [_fn(r),_fn(g),_fn(b),a,"rgb"];
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// =============================================================================================
|
|
490
|
+
bw.colorRgbToHsl = function (r, g, b, a, rnd) {
|
|
491
|
+
/**
|
|
492
|
+
Converts an RGB color value to HSL. Conversion formula
|
|
493
|
+
adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
|
494
|
+
Assumes r, g, and b are contained in the set [0, 255] and
|
|
495
|
+
returns h as [0..360] s, and l in the set [0 .. 100].
|
|
496
|
+
|
|
497
|
+
@param {number} r The red color value
|
|
498
|
+
@param {number} g The green color value
|
|
499
|
+
@param {number} b The blue color value
|
|
500
|
+
|
|
501
|
+
pass the colors as a bitwrench color array as a single parameter:
|
|
502
|
+
colorRgbToHsl([h,s,l,a,"rgb"])
|
|
503
|
+
|
|
504
|
+
last parameter rnd rounds the results to 0..255. set to false to eliminate rounding. This can be useful for chained calcs
|
|
505
|
+
@return {Array} The HSL representation
|
|
506
|
+
*/
|
|
507
|
+
if (_to(r)=="array") { // handles colors of [h,s,l,a,"hsl"]
|
|
508
|
+
g=r[1];
|
|
509
|
+
b=r[2];
|
|
510
|
+
a=r[3];
|
|
511
|
+
r=r[0]; //do this last so it doesn't overwrite iself
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
r /= 255, g /= 255, b /= 255;
|
|
515
|
+
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
516
|
+
var h, s, l = (max + min) / 2;
|
|
517
|
+
|
|
518
|
+
if(max == min){
|
|
519
|
+
h = s = 0; // achromatic
|
|
520
|
+
}else{
|
|
521
|
+
var d = max - min;
|
|
522
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
523
|
+
switch(max){
|
|
524
|
+
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|
525
|
+
case g: h = (b - r) / d + 2; break;
|
|
526
|
+
case b: h = (r - g) / d + 4; break;
|
|
527
|
+
}
|
|
528
|
+
h /= 6;
|
|
529
|
+
}
|
|
530
|
+
var _fn = rnd == false ? function (x){return x;} : function(x){return Math.round(x);} ;
|
|
531
|
+
return [_fn(h*360), _fn(s*100), _fn(l*100), a, "hsl"];
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// =============================================================================================
|
|
535
|
+
|
|
536
|
+
bw.colorParse = function(s,defAlpha) {
|
|
537
|
+
/**
|
|
538
|
+
@method bw.colorParse(s)
|
|
539
|
+
|
|
540
|
+
@description take a valid CSS style color string: #rgb | #rgba | #rrggbb | #rrggbbaa | rgb(r,g,b) | rgb(r,g,b,a) | hsl(h,s,l) | hsla(h,s,l,a )
|
|
541
|
+
... and return array [c0,c1,c2,a,model] where model is one of "rgb", "hsl"
|
|
542
|
+
*/
|
|
543
|
+
defAlpha = _toa(defAlpha,"number",defAlpha,255);
|
|
544
|
+
var r = [0,0,0,defAlpha,"rgb"]; // always return a valid type
|
|
545
|
+
if (_to(s)=="array"){ // it could be a bwcolor type [c0,c1,c2,a,model]
|
|
546
|
+
var p,df = [0,0,0,255,"rgb"];
|
|
547
|
+
for (p=0; p< s.length; p++)
|
|
548
|
+
df[p]=s[p];
|
|
549
|
+
s= String(df[4])+"("+String(df[0])+","+String(df[1])+","+String(df[2])+","+String(df[3])+")"; //could use slice..join(",")
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
s = String(s).replace(/\s/g,"");
|
|
553
|
+
var reT = /\s*(#|hsl|rgb|yuv|hsv){1}([a-f|A-F|0-9|,().\t ]*)/img;
|
|
554
|
+
var i,j=0,x = reT.exec(s);
|
|
555
|
+
if (_to(x)=="array" && (x.length >= 3)) {
|
|
556
|
+
r[4]= x[1] == "#" ? "rgb" : x;
|
|
557
|
+
if (x[1] == "#") { //parse one of these #rgb #rgba #rrggbb #rrggbbaa
|
|
558
|
+
switch (x[2].length) {
|
|
559
|
+
case 3: //#rgb
|
|
560
|
+
case 4: //#rgba
|
|
561
|
+
for (i=0; i< x[2].length; i++)
|
|
562
|
+
r[i] = parseInt(x[2][i]+x[2][i],16);
|
|
563
|
+
break;
|
|
564
|
+
case 6: //#rrggbb
|
|
565
|
+
case 8: //#rrggbbaa
|
|
566
|
+
for (i=0; i< x[2].length; i+=2)
|
|
567
|
+
r[j++] = parseInt(x[2][i]+x[2][i+1],16);
|
|
568
|
+
break;
|
|
569
|
+
default:
|
|
570
|
+
bw.logd("bw.parseColor bad input "+ s);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
else { // its should be of form (c0,c1,c2) or (c0,c1,c2,alpha)
|
|
574
|
+
r[4] = x[1].toLocaleLowerCase();
|
|
575
|
+
if ((x[2][0] == "(") && (x[2][x[2].length-1] == ")")) { // parans are present
|
|
576
|
+
var v = x[2].substring(1,x[2].length-1);
|
|
577
|
+
v = v.split(",");
|
|
578
|
+
switch(v.length){ // valid entries are 3 or 4 components
|
|
579
|
+
case 3:
|
|
580
|
+
case 4:
|
|
581
|
+
for (i=0; i< v.length; i++)
|
|
582
|
+
r[i] = Number(v[i]);
|
|
583
|
+
break;
|
|
584
|
+
default:
|
|
585
|
+
bw.logd("bw.parseColor bad input : " + s);
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
bw.logd("bw.parseColor bad input : " + s);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return r;
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// =============================================================================================
|
|
596
|
+
bw.colorToRGBHex = function(c, format) {
|
|
597
|
+
/**
|
|
598
|
+
@method bw.colorToRGBHex(color)
|
|
599
|
+
@description take a color of the form a string or [c0,c1,c2,alpha,model] ==> convert to #rrggbbaa format
|
|
600
|
+
format (optional) can be set to auto in which case alpha is ommitted if set to 255
|
|
601
|
+
*/
|
|
602
|
+
var r = "#00000000";
|
|
603
|
+
var ph = function(x){var y=(bw.clip(Math.round(x),0,255)).toString(16); return (y.length==1)?"0"+y:y;}; // pad hex
|
|
604
|
+
c = bw.colorParse(c); // converts color to bw color vector format
|
|
605
|
+
switch(c[4]) {
|
|
606
|
+
case "rgb":
|
|
607
|
+
r = "#"+ph(c[0])+ph(c[1])+ph(c[2]);
|
|
608
|
+
if (!((format == "auto") && (c[3]==255)))
|
|
609
|
+
r += ph(c[3]);
|
|
610
|
+
break;
|
|
611
|
+
case "hsl":
|
|
612
|
+
r= bw.colorToRGBHex(bw.colorHslToRgb(c));
|
|
613
|
+
break;
|
|
614
|
+
default:
|
|
615
|
+
bw.logd("colorToRGBHex : unsupported format" + c[4]);
|
|
616
|
+
}
|
|
617
|
+
return r; // default
|
|
618
|
+
};
|
|
619
|
+
// =============================================================================================
|
|
620
|
+
bw.colorConvertColorSpace = function(c, space, rnd) {
|
|
621
|
+
/**
|
|
622
|
+
@method bw.colorConvertColorSpace(color, spaceToConvertTo)
|
|
623
|
+
@description take a color and convert it to the destination color space ("rgb" | "hsl")
|
|
624
|
+
color can be any valid color type ("#abc" | "hs(...)" or [r,g,b,a,"rgb"] etc)
|
|
625
|
+
|
|
626
|
+
optional 3rd param rnd if set to false will suppress rounding in calcs to allow chained color conversion w/o loss of precision
|
|
627
|
+
|
|
628
|
+
*/
|
|
629
|
+
c = bw.colorParse(c);
|
|
630
|
+
if (space == c[4])
|
|
631
|
+
return c;
|
|
632
|
+
|
|
633
|
+
switch(c[4]) {
|
|
634
|
+
case "rgb":
|
|
635
|
+
break;
|
|
636
|
+
case "hsl":
|
|
637
|
+
c = bw.colorHslToRgb(c[0],c[1],c[2],c[3],rnd); // turns off rounding
|
|
638
|
+
break;
|
|
639
|
+
default:
|
|
640
|
+
bw.logd("colorConvertColorSpace: unsupported color format");
|
|
641
|
+
}
|
|
642
|
+
//now c is in the rgb space
|
|
643
|
+
|
|
644
|
+
switch(space) {
|
|
645
|
+
case "rgb":
|
|
646
|
+
break;
|
|
647
|
+
case "hsl":
|
|
648
|
+
c = bw.colorRgbToHsl(c[0],c[1],c[2],c[3],rnd); // turns off rounding
|
|
649
|
+
break;
|
|
650
|
+
default:
|
|
651
|
+
bw.logd("colorConvertColorSpace: unsupported color format");
|
|
652
|
+
}
|
|
653
|
+
return c;
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// =============================================================================================
|
|
657
|
+
var _logdata=[];
|
|
658
|
+
|
|
659
|
+
bw.log = function (value,msg,opts) {
|
|
660
|
+
/**
|
|
661
|
+
bw.log(value, message, options)
|
|
662
|
+
write a value to the in-memory log
|
|
663
|
+
options {
|
|
664
|
+
clear: false | true | "clear-only"
|
|
665
|
+
false : normal write
|
|
666
|
+
true : clear log and add 1st entry
|
|
667
|
+
clear-only - only clear don't write, value, msg
|
|
668
|
+
saveMethod: "raw" | "JSON" // raw is default, save object as passed, JSON saves stringified version (useful for exporting or saving state)
|
|
669
|
+
}
|
|
670
|
+
*/
|
|
671
|
+
var dopts = {
|
|
672
|
+
clear : false, // values fales, true, "clear-only"
|
|
673
|
+
saveMethod: "raw" // else "JSON"
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
dopts = optsCopy(dopts,opts);
|
|
677
|
+
if ((dopts["clear"] == true) || (dopts["clear"] == "clear-only")) {
|
|
678
|
+
_logdata = [["Time-stamp (ms)"," Value "," Message "]];
|
|
679
|
+
var ct = (new Date());
|
|
680
|
+
_logdata.push ([0, ct.getTime()," log started at " + ct.toString()]);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
msg = _toa(msg,"undefined","",String(msg));
|
|
684
|
+
value = (dopts["saveMethod"]=="raw") ? value : JSON.stringify(value);
|
|
685
|
+
|
|
686
|
+
if ((_to(value) != "undefined") && (dopts["clear"] != "clear-only"))
|
|
687
|
+
_logdata.push([(new Date()).getTime()-_logdata[1][1], value, msg]);
|
|
688
|
+
|
|
689
|
+
return _logdata.length -1;
|
|
690
|
+
};
|
|
691
|
+
bw.log("","",{clear:"clear-only"}); // initialize
|
|
692
|
+
|
|
693
|
+
// =============================================================================================
|
|
694
|
+
bw.logd = function() {
|
|
695
|
+
/**
|
|
696
|
+
@method bw.logd()
|
|
697
|
+
@description: bw.logd is a log funciton which behaves similar to console.log() however instread of outputting to console,
|
|
698
|
+
it writes to bw.log() function with the following differences:
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
*/
|
|
702
|
+
/*
|
|
703
|
+
todo: comma seperated items; ? done
|
|
704
|
+
console ==> also (attempt) to output to console.log ? would need to set a bw.state variable..
|
|
705
|
+
bwdbg ==> log bw catches / errors (else silent)
|
|
706
|
+
none ==> no output (of any kind)
|
|
707
|
+
stringify ==> takes bw.logd args and strinigyfies before writing to bw.log
|
|
708
|
+
example:
|
|
709
|
+
logd=console,bwlogd
|
|
710
|
+
|
|
711
|
+
*/
|
|
712
|
+
var logdargs = ("bwlogd" in bw.bwargs) ? bw.bwargs["bwlogd"].split(",") : [];
|
|
713
|
+
|
|
714
|
+
if (logdargs.indexOf("none") < 0) {
|
|
715
|
+
var i=0;
|
|
716
|
+
var _a = [];
|
|
717
|
+
for (i=0; i< arguments.length; i++)
|
|
718
|
+
_a.push(arguments[i]); //arguments, a reserved javascript keyword, is not a true array
|
|
719
|
+
bw.log(_a,"bw.logd: "+bw.bwargs["bwlogd"]); // message
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
// =============================================================================================
|
|
723
|
+
bw.logExport = function(opts) {
|
|
724
|
+
/**
|
|
725
|
+
bw.logExport(options)
|
|
726
|
+
export the built in log.
|
|
727
|
+
default is "raw" which is an array of values:
|
|
728
|
+
[timestamp, <value logged>, <optional message from the event>]
|
|
729
|
+
[ .. , .. , .. ]
|
|
730
|
+
|
|
731
|
+
also can be exported as an HTML table.
|
|
732
|
+
bw.logExport({"format":"HTML"})
|
|
733
|
+
|
|
734
|
+
or as a simple text file:
|
|
735
|
+
bw.logExport("format" : "text"})
|
|
736
|
+
|
|
737
|
+
see bw.saveClientFile(fname) for saving the log as a file
|
|
738
|
+
*/
|
|
739
|
+
var dopts = {
|
|
740
|
+
"format" : "raw" // can also be HTML table if set to "HTML"
|
|
741
|
+
};
|
|
742
|
+
dopts = optsCopy(dopts,opts);
|
|
743
|
+
|
|
744
|
+
var _ld = _logdata;
|
|
745
|
+
|
|
746
|
+
if (dopts["format"] == "HTML") {
|
|
747
|
+
return bw.makeHTMLTableStr(_ld,{sortable:true});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (dopts["format"] == "text") {
|
|
751
|
+
return _ld.map(function(x){return x.map(function(y){return bw.padString(y.toString(),16,"left");}).join("\t");}).join("\n");
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return _ld;
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
// ===================================================================================
|
|
759
|
+
bw.setCookie = function (cname, cvalue, exdays) {
|
|
760
|
+
/**
|
|
761
|
+
@method bw.setCookie(cookieName, value, expireDays) set a client side cookie. (browser only)
|
|
762
|
+
@param cname : a string for the name of the cookie
|
|
763
|
+
@param cvalue : a string for the value of the cookie
|
|
764
|
+
@param expdays : cookie expiration date in days
|
|
765
|
+
*/
|
|
766
|
+
var d = new Date();
|
|
767
|
+
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
|
768
|
+
var expires = "expires="+d.toUTCString();
|
|
769
|
+
document.cookie = cname + "=" + cvalue + "; " + expires;
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
// ===================================================================================
|
|
773
|
+
bw.getCookie = function (cname, defaultValue) {
|
|
774
|
+
/**
|
|
775
|
+
@method bw.getCookie: bw.getCookie(cookieName, defaultValueIfNotFound) (browser only)
|
|
776
|
+
get a client side cookie, if it is set. returns defaultValue if cookie could not be found
|
|
777
|
+
*/
|
|
778
|
+
var name = cname + "=";
|
|
779
|
+
var ca = document.cookie.split(";");
|
|
780
|
+
for(var i=0; i<ca.length; i++) {
|
|
781
|
+
var c = ca[i];
|
|
782
|
+
while (c.charAt(0)==" ") c = c.substring(1);
|
|
783
|
+
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
|
|
784
|
+
}
|
|
785
|
+
return defaultValue;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
// ===================================================================================
|
|
790
|
+
bw.getURLParam = function (key, defaultValue) {
|
|
791
|
+
/**
|
|
792
|
+
@method bw.getURLParam(key,defaultValueIfNotFound)
|
|
793
|
+
read the URL (e.g. http://example.com/my/page?this=that&foo=123&bub&x=123) and parse the URL paraemeters
|
|
794
|
+
|
|
795
|
+
x = bw.getURLParam() ==> returns entire dict of url params ==? {this:"that",foo:"123",bub:true,x:"123"}
|
|
796
|
+
x = bw.getURLParam("foo","whatever") ==> returns "123"
|
|
797
|
+
x = bw.getURLParam("bar","whatever") ==> returns "whatever" since bar isn't set
|
|
798
|
+
x = bw.getURLParam("bub","whatever") ==> returns true since bub doesn't have a value (note boolean true not "true")
|
|
799
|
+
|
|
800
|
+
*/
|
|
801
|
+
if ((bw.isNodeJS()== true) || (typeof window != "object"))
|
|
802
|
+
return defaultValue;
|
|
803
|
+
try {
|
|
804
|
+
if (window.location.href) {
|
|
805
|
+
return bw.URLParamParse(window.location.href,key,defaultValue);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
catch (e) {
|
|
809
|
+
bw.log(e);
|
|
810
|
+
}
|
|
811
|
+
return defaultValue;
|
|
812
|
+
|
|
813
|
+
};
|
|
814
|
+
bw.URLHash = function (url,defValue) {
|
|
815
|
+
/**
|
|
816
|
+
@method bw.URLHash(url,defValue) - returns the hash portion of a URL (if present) else return defValue
|
|
817
|
+
*/
|
|
818
|
+
if (_to(url)=="undefined")
|
|
819
|
+
url = typeof window == "object" ? window.location.href : "";
|
|
820
|
+
|
|
821
|
+
var r = url.split(/#+/);
|
|
822
|
+
return url.includes("#") ? r[r.length-1] : defValue;
|
|
823
|
+
};
|
|
824
|
+
//=================================================
|
|
825
|
+
bw.URLParamParse = function (url,key,defValue,allowHash) {
|
|
826
|
+
/**
|
|
827
|
+
|
|
828
|
+
@method bw.URLParamParse(urlString, key, defaultValue)
|
|
829
|
+
|
|
830
|
+
decode a URL encoded string in to a javascript dictionary. Other params (http, port, path) are not handled
|
|
831
|
+
|
|
832
|
+
if key is present than only that value is returned (as a string ) else defValue is returned.
|
|
833
|
+
|
|
834
|
+
examples:
|
|
835
|
+
x = URLParamParse("http://example.com?a=123&b=345") ==> {a:"123", b:"456"}
|
|
836
|
+
x = URLParamParse("http://example.com?a=123&b=345","a") ==> "123"
|
|
837
|
+
x = URLParamParse("http://example.com?a=123&b=345","c","otherValue") ==> {a:"123", b:"456"} ==> "otherValue"
|
|
838
|
+
*/
|
|
839
|
+
|
|
840
|
+
try {
|
|
841
|
+
var hs=function(u){var x = u.split(/^.*\?+/); return x.length==2 ? x[1] : "";};
|
|
842
|
+
var sh=function(u,b){return (b==true) ? u : u.split(/#+/)[0];};
|
|
843
|
+
var params={}, parts = sh(hs(url),allowHash).split("&");
|
|
844
|
+
for (var i = 0; i < parts.length; i++) {
|
|
845
|
+
var e = parts[i].split("=");
|
|
846
|
+
if (!e[0])
|
|
847
|
+
continue;
|
|
848
|
+
params[decodeURIComponent(e[0])] = _to(e[1])=="string" ? decodeURIComponent(e[1].replace("#","%23")) : true;
|
|
849
|
+
}
|
|
850
|
+
if (_to(key)=="undefined")
|
|
851
|
+
return params;
|
|
852
|
+
return params.hasOwnProperty(key) ? params[key] : defValue;
|
|
853
|
+
}
|
|
854
|
+
catch (e) {
|
|
855
|
+
bw.log(e);
|
|
856
|
+
return defValue;
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
//=================================================
|
|
860
|
+
bw.URLParamPack = function (simpleDict,inclQuestion) {
|
|
861
|
+
/**
|
|
862
|
+
@method bw.URLParamPack(simpleDict, inclQuestion) : packs a simple dict in to URL encoded format
|
|
863
|
+
@param simpleDict(object) - dictionary of simple key value pairs (not nested) if "deep" JSON needs to be packed then stringify that first
|
|
864
|
+
@param InclQuestion(boolean) - if true adds "?" to string otherwise ommitted.
|
|
865
|
+
|
|
866
|
+
see also URLParamParse.
|
|
867
|
+
note if using bw.URLParamParse besure to include "?"" ==> bw.URLParamParse(bw.URLParamPack({a:1,b;2},"true"))
|
|
868
|
+
*/
|
|
869
|
+
var k,s=[];
|
|
870
|
+
if (_to(simpleDict) == "object") {
|
|
871
|
+
for (k in simpleDict) {
|
|
872
|
+
s.push([encodeURIComponent(k)+"="+encodeURIComponent(simpleDict[k].toString())]);
|
|
873
|
+
}
|
|
874
|
+
s = s.join("&");
|
|
875
|
+
}
|
|
876
|
+
else
|
|
877
|
+
s="";
|
|
878
|
+
|
|
879
|
+
return (inclQuestion ? "?" : "") + s;
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
// ===================================================================================
|
|
885
|
+
bw.htmlSafeStr = function (str) {
|
|
886
|
+
/**
|
|
887
|
+
bw.htmlSageString(str)
|
|
888
|
+
Replace non valid HTML characters with HTML escaped equivalents.
|
|
889
|
+
*/
|
|
890
|
+
//generic way..
|
|
891
|
+
//var x = function(x){return "&#"+x.toString().charCodeAt(0)+";";}
|
|
892
|
+
//return (str.toString()).replace(/[<>&\\#]/gm,x).replace(/[\n]/gm,"<br>");
|
|
893
|
+
|
|
894
|
+
//old way is "pretty", tabs are issued 4 spaces..
|
|
895
|
+
var c = {"<":"<", ">":">", "&":"&", "\"":""", "'":"'","#":"#","\\\\":"","\n":"<br>","\t":" "};
|
|
896
|
+
return (str.toString()).replace(new RegExp("["+Object.keys(c).join("")+"]","gm"),function(s){return c[s];});
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
// ===================================================================================
|
|
900
|
+
bw.htmlFavicon = function(iconStr,color) {
|
|
901
|
+
/**
|
|
902
|
+
create and HTML favicon from a string. Can use any unicode char including emoticons.
|
|
903
|
+
*/
|
|
904
|
+
iconStr = iconStr ? iconStr : "🔧";// ♪ ❤ ♡ 🦉 🔧 🌴 ♫
|
|
905
|
+
var c = bw.to(color)=="string" ? color : "black";
|
|
906
|
+
return bw.html( {t:"link", a: {href: "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='0.9em' font-size='90' style='fill:"+ c+"'>"+iconStr+"</text></svg>", rel: "icon"} } );
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
// ===================================================================================
|
|
910
|
+
bw.htmlJSON=function (json,pwrap) {
|
|
911
|
+
/**
|
|
912
|
+
@method bw.htmlJSON(object, styles)
|
|
913
|
+
pretty print any javascript object as displayable HTML.
|
|
914
|
+
e.g.
|
|
915
|
+
document.getElementById("myPlaceToDisplay").innerHTML = bw.htmlJSON(...any object ....)
|
|
916
|
+
*/
|
|
917
|
+
//TODO make style dict as a param
|
|
918
|
+
function f(json) {
|
|
919
|
+
json = JSON.stringify(json, undefined, 2);
|
|
920
|
+
if (typeof json != "string") { json = JSON.stringify(json, undefined, 2);}
|
|
921
|
+
|
|
922
|
+
json = json.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); //html safe chars
|
|
923
|
+
//json = bw.htmlSafeStr(json);
|
|
924
|
+
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g, function (match) {
|
|
925
|
+
var sty = "color: darkorange;";
|
|
926
|
+
if (/^"/.test(match)) {
|
|
927
|
+
if (/:$/.test(match)) {
|
|
928
|
+
sty = "color:red";
|
|
929
|
+
} else {
|
|
930
|
+
sty = "color:purple";
|
|
931
|
+
}
|
|
932
|
+
} else if (/true|false/.test(match)) {
|
|
933
|
+
sty = "color:grey";
|
|
934
|
+
} else if (/null/.test(match)) {
|
|
935
|
+
sty = "color:black";
|
|
936
|
+
} else
|
|
937
|
+
sty = "color:green";
|
|
938
|
+
return "<span style=\"" + sty + "\">" + match + "</span>";
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
pwrap = _toa(pwrap,"undefined","white-space:pre-wrap;","");
|
|
942
|
+
|
|
943
|
+
return "<pre style='"+pwrap+"'>"+f(json)+"</pre>";
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
// ===================================================================================
|
|
947
|
+
bw.makeCSS = function (cssData, options) {
|
|
948
|
+
/**
|
|
949
|
+
bw.makeCSS(cssData, options)
|
|
950
|
+
|
|
951
|
+
cssData = "h2 {color:blue;}" // string as full rule (all correctness is on the caller)
|
|
952
|
+
cssData = ["h2 {color:blue;}"] // array entry but single string
|
|
953
|
+
cssData = ["h2 {color:blue}", "div {width:30px}"] // 2 entries, both strings
|
|
954
|
+
cssData = [["h2","color:blue"]] // array of rules (here length 1 rule)
|
|
955
|
+
cssData = [["h2","color:blue"], ["h3", "font-color:red"]] // array of rules
|
|
956
|
+
cssData = [
|
|
957
|
+
[["h2","h4"], "color:blue"], // array or selectors, string for rule
|
|
958
|
+
["h3", "color:red"] // string for selector, string for rule
|
|
959
|
+
]
|
|
960
|
+
cssData = [
|
|
961
|
+
[["h1","div p"],["color:blue","display:block"]], ==> h1, div p {color: blue; diplay:block;}
|
|
962
|
+
"h3 {color:red;}", ==> h3 {color: red;}
|
|
963
|
+
[["div",".myClass"],"color : red"], ==> div,.myClass {color: red;}
|
|
964
|
+
["p > .myclass", ["color:red","display:block"]] ==> p > .myClass {color: red; display:block;}
|
|
965
|
+
]
|
|
966
|
+
cssData = [
|
|
967
|
+
[str, {}]
|
|
968
|
+
]
|
|
969
|
+
cssData = [
|
|
970
|
+
[[selectors], { dict }]
|
|
971
|
+
]
|
|
972
|
+
|
|
973
|
+
dicts not used at root because css can have multiple redundant selectors with different rules
|
|
974
|
+
|
|
975
|
+
*/
|
|
976
|
+
var dopts = {
|
|
977
|
+
emitStyleTag: false,
|
|
978
|
+
atr: {},
|
|
979
|
+
pretty : false
|
|
980
|
+
};
|
|
981
|
+
dopts = optsCopy(dopts,options);
|
|
982
|
+
|
|
983
|
+
var s="\n";
|
|
984
|
+
var tb = function (a) {a =(String(a)).trim(); a=(a[0]=="{"?" ":" {")+a; a+=(a[a.length]=="}"?"":"}")+"\n"; return a;};
|
|
985
|
+
//var rl = "";
|
|
986
|
+
try {
|
|
987
|
+
switch (_to(cssData)) {
|
|
988
|
+
case "string":
|
|
989
|
+
s += cssData +"\n";
|
|
990
|
+
break;
|
|
991
|
+
case "array":
|
|
992
|
+
var i;
|
|
993
|
+
for (i=0; i<cssData.length; i++) {
|
|
994
|
+
var j = cssData[i];
|
|
995
|
+
switch (_to(j)) {
|
|
996
|
+
case "string": // this means we assume correcly formatted style is being passed in and we're just letting it through e.g. ".myclass {color:red}"
|
|
997
|
+
s+= j+"\n";
|
|
998
|
+
break;
|
|
999
|
+
case "array" : //expects length 2 array for each entry, though 2nd member can be dict or array
|
|
1000
|
+
// ==>[str, str], [[str,str,str],str] , [str, {}], [[str,str,str],{}]
|
|
1001
|
+
if ((j.length == 1) && (_to(j[0])=="string")) {
|
|
1002
|
+
s+= j[0]+"\n";
|
|
1003
|
+
break;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (j.length == 2) {
|
|
1007
|
+
var _name = j[0], _rule = j[1], _ruleOutput="";
|
|
1008
|
+
if (_to(_name)=="array") {
|
|
1009
|
+
s+= _name.join(", ");
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
s+= String(_name);
|
|
1013
|
+
}
|
|
1014
|
+
// now we have the names e.g. ("h2" or "h2,.myClass") done we need to emit the rules
|
|
1015
|
+
switch( _to(_rule)) {
|
|
1016
|
+
case "array" : // ["h2", ["color: black","left:20%"]] or [["h2",".myClass"], ["color: black","left:20%"]]
|
|
1017
|
+
_ruleOutput = _rule.join("; ")+";";
|
|
1018
|
+
break;
|
|
1019
|
+
case "object" : // ["h2", {color: "black", left:"20%"}] or [["h2",".myClass"], {color:black, left:"20%"}]
|
|
1020
|
+
{
|
|
1021
|
+
var x;
|
|
1022
|
+
for (x in _rule) { _ruleOutput += (x + ": " + _rule[x]+"; ");}
|
|
1023
|
+
//_ruleOutput = bw.makeCSSRule([_name,_rule],{pretty:opts.pretty});
|
|
1024
|
+
}
|
|
1025
|
+
break;
|
|
1026
|
+
case "string": // ["h2", "color: black"] or [["h2",".myClass"], "color:black"]
|
|
1027
|
+
default:
|
|
1028
|
+
_ruleOutput=_rule;
|
|
1029
|
+
}
|
|
1030
|
+
s+= tb(_ruleOutput)+"\n";
|
|
1031
|
+
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
break;
|
|
1035
|
+
default:
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
break;
|
|
1040
|
+
default:
|
|
1041
|
+
s="";
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
catch (e) {bw.logd(e);} // eslint-disable-line no-empty
|
|
1045
|
+
if (dopts["emitStyleTag"]) {
|
|
1046
|
+
s = bw.html(["style",dopts["atr"],s]);
|
|
1047
|
+
}
|
|
1048
|
+
s.replace(/\n+/g,"\n").replace(/s+/g," ");
|
|
1049
|
+
return s;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
// ===================================================================================
|
|
1053
|
+
bw.makeCSSRule = function (cssData, options) {
|
|
1054
|
+
/**
|
|
1055
|
+
@method bw.makeCSSRule(cssData, options)
|
|
1056
|
+
|
|
1057
|
+
expects this form:
|
|
1058
|
+
[str, {k,v}]
|
|
1059
|
+
or
|
|
1060
|
+
[[array of rules str], {k,v}]
|
|
1061
|
+
|
|
1062
|
+
e,g, [".myClass", {"color": "red", "font-weight" : "700 !important!"}]
|
|
1063
|
+
or
|
|
1064
|
+
[[".myClass","div > p"], {"color": "red", "font-weight" : "700 !important!"}]
|
|
1065
|
+
|
|
1066
|
+
*/
|
|
1067
|
+
var dopts = {
|
|
1068
|
+
emitStyleTag: false,
|
|
1069
|
+
atr: {},
|
|
1070
|
+
pretty: true // make it pretty otherwise compact form generated
|
|
1071
|
+
};
|
|
1072
|
+
dopts = optsCopy(dopts,options);
|
|
1073
|
+
|
|
1074
|
+
var k,d,v=[],s="",sp=dopts.pretty?" ":"", cr=dopts.pretty?"\n":"";
|
|
1075
|
+
|
|
1076
|
+
try {
|
|
1077
|
+
if (_to(cssData)== "array") {
|
|
1078
|
+
k=cssData[0], d=cssData[1];
|
|
1079
|
+
s +=_toa(k,"array",k,[k.toString()]).join(","+sp)+cr;
|
|
1080
|
+
for (k in d) {
|
|
1081
|
+
v.push([sp+sp+k+":"+sp+cssData[1][k]+";"+sp+cr]);
|
|
1082
|
+
}
|
|
1083
|
+
s+= "{"+cr+v.join("")+"}"+cr;
|
|
1084
|
+
}
|
|
1085
|
+
} catch(e) {
|
|
1086
|
+
bw.logd(e);
|
|
1087
|
+
}
|
|
1088
|
+
return s;
|
|
1089
|
+
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
// ===================================================================================
|
|
1093
|
+
/*
|
|
1094
|
+
bw.htmlPage = function (head, body, options) {
|
|
1095
|
+
/**
|
|
1096
|
+
TBD finish (include bw params, handling meta w/o close tags)
|
|
1097
|
+
bw.makeHTMLDoc(head,body,options)
|
|
1098
|
+
make a simple HTML document.
|
|
1099
|
+
|
|
1100
|
+
inline-bw-css --> emit bw default styles as inline css (include globals option)
|
|
1101
|
+
* /
|
|
1102
|
+
var dopts = {
|
|
1103
|
+
docType : "<!DOCTYPE html>",
|
|
1104
|
+
htmlParams : {lang: "en"},
|
|
1105
|
+
headDefaultContent : [
|
|
1106
|
+
["meta", {"http-equiv":"Content-Type", "content":"text/html", "charset":"utf-8"}, ""]
|
|
1107
|
+
//["title", {}, "bw doc"]
|
|
1108
|
+
],
|
|
1109
|
+
headIncludeBitWrenchJS : false, // false : don't include, "embed" or "path-string"
|
|
1110
|
+
headIncludeBitWrenchCSS : false, // exports bitwrench css classes in <style> section in head
|
|
1111
|
+
headFavicon : "" //<link rel="icon" type="image/x-icon" href="../images/favicon-32x32.png" />
|
|
1112
|
+
};
|
|
1113
|
+
dopts = optsCopy(dopts,options);
|
|
1114
|
+
|
|
1115
|
+
var s = dopts["docType"]+"\n";
|
|
1116
|
+
s += bw.html(["html",dopts["htmlParams"],[
|
|
1117
|
+
"\n",
|
|
1118
|
+
["head", {}, [ "\n",dopts["headDefaultContent"].map(function(x){return bw.html(x);}).join("\n"),head,"\n"]],
|
|
1119
|
+
"\n",
|
|
1120
|
+
["body", {}, [ "\n",body,"\n"]],
|
|
1121
|
+
"\n"
|
|
1122
|
+
]]);
|
|
1123
|
+
return s;
|
|
1124
|
+
};
|
|
1125
|
+
*/
|
|
1126
|
+
// ===================================================================================
|
|
1127
|
+
/**
|
|
1128
|
+
htmlIsVoidTag(tagString) returns true if the supplied string is a html void tag (e.g. meta or br) which doesn't require a closing bracket else false
|
|
1129
|
+
*/
|
|
1130
|
+
bw.htmlIsVoidTag = function(tag) {
|
|
1131
|
+
return " area base br col command embed hr img input keygen link meta param source track wbr ".search(" "+String(tag).trim().toLowerCase()+" ") >=0;
|
|
1132
|
+
};
|
|
1133
|
+
// ===================================================================================
|
|
1134
|
+
bw.htmlNode = function(x,opts) {
|
|
1135
|
+
/**
|
|
1136
|
+
|
|
1137
|
+
bw.htmlNode - converts acceptable data contructs into htmlEmit() compatible form,
|
|
1138
|
+
{ t: <tag>, a: {attribs}, c: [content], o: {options} }
|
|
1139
|
+
or
|
|
1140
|
+
a string e.g. "my html content" note that this can include html as well.
|
|
1141
|
+
or
|
|
1142
|
+
a function to be evaluated later
|
|
1143
|
+
f(){return htmlNode compatible data structure} ==> when bw.HTMLEmit encounters this it will evaluate the the function contents. Useful for live templating.
|
|
1144
|
+
|
|
1145
|
+
How htmlNode handles different objects:
|
|
1146
|
+
|
|
1147
|
+
"object"
|
|
1148
|
+
accepted keys below, other keys ignored (e.g. if your objects looks like this: {tag:"div", c:["this is my content"], data: [ ....] } the key called data will be ignored by htmlNode
|
|
1149
|
+
t: String | Number | Date() ==> tag function==> f().toString()
|
|
1150
|
+
a: {} ==> key : value ==> num | str | Date | [] ==> [].join(dopts.a_join) ,
|
|
1151
|
+
c: [] || String | Number | Date ==> each_item : str | {html_dict}
|
|
1152
|
+
o: {} ==> options (note inherit / copy) => if not supplied uses previous levels options
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
also accepts: "tag", "attrib", "content", "options" as keys instead of t,a,c,o,s
|
|
1156
|
+
|
|
1157
|
+
if any of t,a,c,o are a function it will be invoked immediatly w no params ==> t:myFunc ===> t:myFunc() <==
|
|
1158
|
+
|
|
1159
|
+
defaults:
|
|
1160
|
+
t ==> "div"
|
|
1161
|
+
a ==> {}
|
|
1162
|
+
c ==> []
|
|
1163
|
+
o ==> {}
|
|
1164
|
+
|
|
1165
|
+
s ==> {level:0, nodes: 0}
|
|
1166
|
+
|
|
1167
|
+
"string" | "number" | Date() ==> {}
|
|
1168
|
+
t ==> "div"
|
|
1169
|
+
a ==> {}
|
|
1170
|
+
c ==> .toString()
|
|
1171
|
+
o ==> {}
|
|
1172
|
+
|
|
1173
|
+
s ==> {}
|
|
1174
|
+
|
|
1175
|
+
"array" type objects are mapped BW_HTMLNodes as follows:
|
|
1176
|
+
[ ] ==> {t:"span", a: {}, c: "", o: {}}
|
|
1177
|
+
[c ] ==> {t:"span", a: {}, c: c , o: {}}
|
|
1178
|
+
[t,c ] ==> {t:t, a: {}, c: c , o: {}}
|
|
1179
|
+
[t,a,c ] ==> {t:t, a: a, c: c}
|
|
1180
|
+
[t,a,c,o ] ==> {}
|
|
1181
|
+
[ 5+ ] ==> {} // uses, first 4 entries, others ignored
|
|
1182
|
+
|
|
1183
|
+
// this dict repreesnts the mapping
|
|
1184
|
+
{
|
|
1185
|
+
0 : { }
|
|
1186
|
+
1 : {c : 0},
|
|
1187
|
+
2 : {t : 0, c : 1},
|
|
1188
|
+
3 : {t : 0, a : 1, c : 2}
|
|
1189
|
+
4 : {t : 0, a : 1, c : 3, o : 4}
|
|
1190
|
+
5 : {t : 0, a : 1, c : 3, o : 4, s : 5}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// this array contruct implements the above dict mapping more compactly
|
|
1194
|
+
var i,idx = [[],["c"], ["t","c"], ["t","a","c"],["t","a","c","o"],["t","a","c","o","s"]];
|
|
1195
|
+
for (i=0; i< x.length; i++)
|
|
1196
|
+
hd[idx[x.length]][i] = x[i];
|
|
1197
|
+
|
|
1198
|
+
returns [BWHTMLNode object,errorInfoString]
|
|
1199
|
+
|
|
1200
|
+
*/
|
|
1201
|
+
|
|
1202
|
+
var err="",dopts = {
|
|
1203
|
+
functionExec : true, // if this node data is a function, execute it with no params eg x ====> x()
|
|
1204
|
+
atomic2span : false // convert atomic strings to a span element
|
|
1205
|
+
};
|
|
1206
|
+
|
|
1207
|
+
dopts = optsCopy(dopts,opts);
|
|
1208
|
+
var isv = bw.htmlIsVoidTag;
|
|
1209
|
+
var isnu = function(x) {return bw.toa(x,["null","undefined"],true,false);}; // is x null or undefined
|
|
1210
|
+
var HTMLNode = function BW_HTMLNode() {this.t="div"; this.a={}; this.c=[]; this.o={tagClose:"auto"}; this._is_BW_HTMLNode=true;}; //isBWHTMLNode is for IE compatiblity
|
|
1211
|
+
//function bwError (v,x) {this.value=v; this.msg = (typeof x == "undefined") ? "error" : x;}
|
|
1212
|
+
|
|
1213
|
+
var i,n = new HTMLNode(); // default html dict format
|
|
1214
|
+
switch (bw.to(x)) {
|
|
1215
|
+
case "null" :
|
|
1216
|
+
case "undefined" :
|
|
1217
|
+
n = "";
|
|
1218
|
+
err = "error: html node content is " + bw.to(x);
|
|
1219
|
+
break;
|
|
1220
|
+
case "object":
|
|
1221
|
+
[["tag","t"],["attrib","a"],["content","c"],["options","o"]].forEach(function(z){ n[z[1]]= z[0] in x ? x[z[0]] : n[z[1]];});
|
|
1222
|
+
for (i in n) { // we only copy those fields we care about..
|
|
1223
|
+
n[i] = (i in x) ? x[i] : n[i]; // need to handle complicated types: t:"", a:{}, c:"" | []
|
|
1224
|
+
if (isnu(n[i])) {
|
|
1225
|
+
n = ""; // force entire object to be null or undefined
|
|
1226
|
+
err = ("Error HTMLNode : a field is null or undefined");
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
break;
|
|
1231
|
+
case "BW_HTMLNode" :
|
|
1232
|
+
for ( i in x) { n[i] = x[i];}
|
|
1233
|
+
break;
|
|
1234
|
+
case "array":
|
|
1235
|
+
var idx = [[],["c"], ["t","c"], ["t","a","c"],["t","a","c","o"]];
|
|
1236
|
+
var m = (x.length > 4) ? 4 : x.length;
|
|
1237
|
+
for (i=0; i< m; i++) {
|
|
1238
|
+
// console.log(idx[m][i] + ":" + x[i]);
|
|
1239
|
+
n[idx[m][i]] = x[i];
|
|
1240
|
+
}
|
|
1241
|
+
for (i in n)
|
|
1242
|
+
if (isnu(n[i])) {
|
|
1243
|
+
n = "";
|
|
1244
|
+
err = "Error HTMLNode : bad array array input";
|
|
1245
|
+
break;
|
|
1246
|
+
}
|
|
1247
|
+
//n.c = _toa(n.c,"array",n.c,[n.c]);
|
|
1248
|
+
break;
|
|
1249
|
+
case "function": // this whole node is a function
|
|
1250
|
+
var h;
|
|
1251
|
+
if (dopts.functionExec) {
|
|
1252
|
+
h = bw.htmlNode(x(),dopts);
|
|
1253
|
+
n=h.node;
|
|
1254
|
+
err = h.error;
|
|
1255
|
+
}
|
|
1256
|
+
else
|
|
1257
|
+
n=h;
|
|
1258
|
+
break;
|
|
1259
|
+
default: // string, number, Date, bool, Regex ==> will be come just plain rendered content later
|
|
1260
|
+
if (dopts.atomic2span) {
|
|
1261
|
+
n.c =[x.toString()];
|
|
1262
|
+
n.t = "span";
|
|
1263
|
+
}
|
|
1264
|
+
else
|
|
1265
|
+
n = x.toString();
|
|
1266
|
+
}
|
|
1267
|
+
var r= {
|
|
1268
|
+
node : n,
|
|
1269
|
+
ntype : bw.typeOf(n), // BW_HTMLNode | string | function
|
|
1270
|
+
error : err,
|
|
1271
|
+
isVoidTag : (bw.typeOf(n)=="BW_HTMLNode")? isv(n.t) : false
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
return r;
|
|
1275
|
+
};
|
|
1276
|
+
// ===================================================================================
|
|
1277
|
+
bw.htmlEmit = function(htmlData, opts, state) {
|
|
1278
|
+
|
|
1279
|
+
var dopts = {
|
|
1280
|
+
tagClose : "inherit",
|
|
1281
|
+
htmlEscContent : false // change spaces, /n /t to html equivalents
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
state = bw.toa(state,["undefined","null"], {
|
|
1285
|
+
nodesCnt : 0,
|
|
1286
|
+
levelCnt : 0,
|
|
1287
|
+
levelMax : 0
|
|
1288
|
+
},state);
|
|
1289
|
+
|
|
1290
|
+
dopts = optsCopy(dopts,opts);
|
|
1291
|
+
|
|
1292
|
+
var _atr = function(n){
|
|
1293
|
+
// handle "smart" attributes ==>
|
|
1294
|
+
// class : ["class1", "class2"] ==> style : bw.makeCSS(),
|
|
1295
|
+
// functions e.g. onclick:function(){} // html on... events can be encoded auto e.g. onclick=function() ==> onclick=bw.registerfunction(... )
|
|
1296
|
+
|
|
1297
|
+
var as = [], k,v,vr;
|
|
1298
|
+
|
|
1299
|
+
for (k in n.a) {
|
|
1300
|
+
v = n.a[k]; // now we have k, v
|
|
1301
|
+
if (v == null) {
|
|
1302
|
+
as.push(k); //null values are ignored. e.g.g {t:"div", a:{checked:null, c:...} ==> <div checked /> ...
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
if (k.search(/^on/) >=0 ) { // its a on... hanlder
|
|
1306
|
+
if (bw.to(v) == "function") {
|
|
1307
|
+
if (n.o["atrOnEventRegister"] == false) {
|
|
1308
|
+
vr = String(v()); // its a function but execute it and return a string value
|
|
1309
|
+
}
|
|
1310
|
+
else { // register it
|
|
1311
|
+
vr = bw.funcGetDispatchStr(bw.funcRegister(v),"this"); //
|
|
1312
|
+
}
|
|
1313
|
+
}else { // not a function but some other type
|
|
1314
|
+
vr = "";
|
|
1315
|
+
try {
|
|
1316
|
+
vr = v.toString();
|
|
1317
|
+
} catch (e) { vr = String(v);}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
else // not an "on" handler
|
|
1321
|
+
{
|
|
1322
|
+
|
|
1323
|
+
switch(k) {
|
|
1324
|
+
case "style" :
|
|
1325
|
+
if (_to(v) == "string")
|
|
1326
|
+
vr=v.toString();
|
|
1327
|
+
else
|
|
1328
|
+
vr = bw.makeCSSRule(["",v],{pretty:false}).trim().replace(/^{/,"").replace(/}$/,"").trim();
|
|
1329
|
+
break;
|
|
1330
|
+
default :
|
|
1331
|
+
if (bw.to(v)=="array")
|
|
1332
|
+
vr = v.join(" ");
|
|
1333
|
+
vr = v.toString();
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
as.push(k+"="+"\""+vr.replace("\"","\\\"")+"\"");
|
|
1337
|
+
}
|
|
1338
|
+
as = as.join(" ");
|
|
1339
|
+
return (as.length > 0 ? " ": "") + as + (as.length > 0 ? " ": "");
|
|
1340
|
+
};
|
|
1341
|
+
|
|
1342
|
+
var h=[],n= bw.htmlNode(htmlData);
|
|
1343
|
+
|
|
1344
|
+
if (_to(n.node) == "function") {
|
|
1345
|
+
n = bw.htmlNode(n.node());
|
|
1346
|
+
n = _toa(n.ntype,["BW_HTMLNode","string"],n,""); // if its still not a string or BW_HTMLNode we just need to punt it.
|
|
1347
|
+
|
|
1348
|
+
}
|
|
1349
|
+
state.nodesCnt++;
|
|
1350
|
+
if (n.ntype != "BW_HTMLNode") {
|
|
1351
|
+
h.push( dopts.htmlEscContent ? bw.htmlSafeStr(n.node.toString()) : n.node.toString() );
|
|
1352
|
+
}
|
|
1353
|
+
else { // bw_HTMLNode
|
|
1354
|
+
|
|
1355
|
+
h.push("<",n.node.t, _atr(n.node));
|
|
1356
|
+
|
|
1357
|
+
var tagClose = dopts.tagClose != "inherit" ? dopts.tagClose : n.node.o.tagClose;
|
|
1358
|
+
switch(tagClose) {
|
|
1359
|
+
/*eslint no-fallthrough: [0, { "commentPattern": "break[\\s\\w]*omitted" }]*/
|
|
1360
|
+
case "auto":
|
|
1361
|
+
if (n.isVoidTag){
|
|
1362
|
+
// <tag a{} /> # content is not rendered for void tags # ["<",n.t , a{}, "/>" ]
|
|
1363
|
+
h.push( "/>");
|
|
1364
|
+
break;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
case "all":
|
|
1368
|
+
default:
|
|
1369
|
+
//<tag a{}> .... </tag> # h=["<",n.t , a{}, crend() , "</", n.t, ">"]
|
|
1370
|
+
h.push(">");
|
|
1371
|
+
var i,x;
|
|
1372
|
+
if (bw.typeOf (n.node.c) != "array") {
|
|
1373
|
+
state.levelCnt++;
|
|
1374
|
+
state.levelMax = state.levelCnt > state.levelMax ? state.levelCnt : state.levelMax;
|
|
1375
|
+
x = bw.htmlEmit(n.node.c,dopts,state);
|
|
1376
|
+
state.levelCnt--;
|
|
1377
|
+
h.push(x.html);
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
for (i in n.node.c) {
|
|
1381
|
+
state.levelCnt++;
|
|
1382
|
+
state.levelMax = state.levelCnt > state.levelMax ? state.levelCnt : state.levelMax;
|
|
1383
|
+
x= bw.htmlEmit(n.node.c[i],dopts,state);
|
|
1384
|
+
state.levelCnt--;
|
|
1385
|
+
h.push(x.html);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
if ( tagClose != "none" )
|
|
1389
|
+
h.push( "</",n.node.t,">");
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
}
|
|
1393
|
+
var html = h.join("");
|
|
1394
|
+
return {html: html, state: state};
|
|
1395
|
+
};
|
|
1396
|
+
// ===================================================================================
|
|
1397
|
+
bw.html = function(data,options) {
|
|
1398
|
+
return bw.htmlEmit(data,options).html;
|
|
1399
|
+
};
|
|
1400
|
+
// ===================================================================================
|
|
1401
|
+
bw.htmla = function (listData,options) {
|
|
1402
|
+
/**
|
|
1403
|
+
bw.htmla(listData,options)
|
|
1404
|
+
|
|
1405
|
+
listData is a single dim array of bw.html() compatible cnostructs
|
|
1406
|
+
|
|
1407
|
+
*/
|
|
1408
|
+
if (_to(listData) != "array")
|
|
1409
|
+
return bw.html(listData,options);
|
|
1410
|
+
|
|
1411
|
+
return listData.map( function(x) {return bw.html(x,options);}).join("");
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
// ===================================================================================
|
|
1415
|
+
bw.htmlList = function (listData, listType, atr, atri) {
|
|
1416
|
+
/**
|
|
1417
|
+
bw.makeHTMLList (listData, listType, attribute{}, attribute_for_each_items {})
|
|
1418
|
+
|
|
1419
|
+
listType = "ul" | "ol"
|
|
1420
|
+
listData = [ item1, item2, item3, .. ]
|
|
1421
|
+
|
|
1422
|
+
*/
|
|
1423
|
+
if (_to(listData) != "array")
|
|
1424
|
+
return "";
|
|
1425
|
+
|
|
1426
|
+
if (listData.length < 1)
|
|
1427
|
+
return "";
|
|
1428
|
+
|
|
1429
|
+
atr = _toa(atr,"object",atr,{});
|
|
1430
|
+
atri = _toa(atr,"object",atr,{});
|
|
1431
|
+
|
|
1432
|
+
var lc = listData.map(function(x){return bw.html(["li",atri,x]);});
|
|
1433
|
+
listType = ["ul","ol"].indexOf(listType)== -1 ? "ol" : listType;
|
|
1434
|
+
return bw.html ({t:listType,a:atr,c:lc});
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
// ===================================================================================
|
|
1438
|
+
bw.openFullScreen = function () {
|
|
1439
|
+
/**
|
|
1440
|
+
bw.openFullScreen() attempt to open the document full screen (usefull for signs, banners)
|
|
1441
|
+
*/
|
|
1442
|
+
var elem = document.documentElement;
|
|
1443
|
+
if (elem.requestFullscreen) {
|
|
1444
|
+
elem.requestFullscreen();
|
|
1445
|
+
} else if (elem.mozRequestFullScreen) { /* Firefox */
|
|
1446
|
+
elem.mozRequestFullScreen();
|
|
1447
|
+
} else if (elem.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
|
|
1448
|
+
elem.webkitRequestFullscreen();
|
|
1449
|
+
} else if (elem.msRequestFullscreen) { /* IE/Edge */
|
|
1450
|
+
elem.msRequestFullscreen();
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
// ===================================================================================
|
|
1455
|
+
bw.classStrAddDel = function (classData,classesToAdd,classesToDel) {
|
|
1456
|
+
/**
|
|
1457
|
+
classStrAddDel (classData, classesToAdd, classesToDel)
|
|
1458
|
+
for CSS classes
|
|
1459
|
+
|
|
1460
|
+
takes a valid classData string e.g. "myclass1 myclass2" etc
|
|
1461
|
+
|
|
1462
|
+
and adds/del classes from classesToAdd string if they are not already present in classData
|
|
1463
|
+
|
|
1464
|
+
classStrAddDel("class1 class2", "class3") ==> "class1 class2 class3"
|
|
1465
|
+
classStrAddDel("class1 class2", "class3 class4") ==> "class1 class2 class3 class4"
|
|
1466
|
+
classStrAddDel("class1 class2", "class2 class3") ==> "class1 class2 class3" // doesn't add class2 again
|
|
1467
|
+
|
|
1468
|
+
classStrAddDel("class1 class2", "class 2 class3",class1) ==> "class2 class3" // doesn't add class2 again. removes class1
|
|
1469
|
+
classStrAddDel("class1 class2", "",class1) ==> "class2" // removes class1
|
|
1470
|
+
|
|
1471
|
+
classData, classesToAdd, classesToDel may be strings (space delimited) or arrays of strings (["c1", "c2"], ["c3", "c4"], ["c1"])
|
|
1472
|
+
*/
|
|
1473
|
+
|
|
1474
|
+
var tnorm = function(x){x=bw.toa(x,"undefined",[],x); return (bw.to(x)=="array")? x : x.toString().trim().split(/\s+/ig);};
|
|
1475
|
+
var c = tnorm(classData);
|
|
1476
|
+
var ca = tnorm(classesToAdd);
|
|
1477
|
+
var cd = tnorm(classesToDel);
|
|
1478
|
+
return bw.arrayBNotInA(cd,c.concat(ca)).join(" ").trim().replace(/\s+/ig," ");
|
|
1479
|
+
|
|
1480
|
+
};
|
|
1481
|
+
// ===================================================================================
|
|
1482
|
+
bw.classStrToggle = function (classData, classesToToggle) {
|
|
1483
|
+
/**
|
|
1484
|
+
classStrToggle (classData, classesToToggle)
|
|
1485
|
+
|
|
1486
|
+
toggles classes listed in classesToToggle
|
|
1487
|
+
|
|
1488
|
+
takes a valid classData string e.g. "myclass1 myclass2" etc
|
|
1489
|
+
*/
|
|
1490
|
+
var tnorma = function(x){x=bw.toa(x,"undefined",[],x); return (bw.to(x)=="array")? x : x.toString().trim().split(/\s+/ig);};
|
|
1491
|
+
var c = tnorma(classData);
|
|
1492
|
+
var t = tnorma(classesToToggle);
|
|
1493
|
+
return bw.classStrAddDel(classData,bw.arrayBNotInA(c,t),bw.arrayBinA(c,t));
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
// ===================================================================================
|
|
1497
|
+
bw.htmlTabs = function(tabData, opts) {
|
|
1498
|
+
/**
|
|
1499
|
+
bw.makeHTMLTabs(tabData, atr)
|
|
1500
|
+
tabData = [[tab1Title,tab1-content], [tab2Title,tab2-content], [tab3Title,tab3-content]]
|
|
1501
|
+
*/
|
|
1502
|
+
if (_to(tabData) != "array")
|
|
1503
|
+
return "";
|
|
1504
|
+
if (tabData.length < 1)
|
|
1505
|
+
return "";
|
|
1506
|
+
|
|
1507
|
+
var dopts = {
|
|
1508
|
+
atr : {"class":""}, //container {}
|
|
1509
|
+
tab_atr : {"class":""}, //attributs for each tab container
|
|
1510
|
+
tabc_atr: {"class":""}, //attributes for each tab-content area container
|
|
1511
|
+
indent : "", //indent string for pretty printing
|
|
1512
|
+
pretty : false
|
|
1513
|
+
};
|
|
1514
|
+
dopts = optsCopy(dopts,opts);
|
|
1515
|
+
|
|
1516
|
+
var ti = tabData.map(function(x){return ["li",{"class":"bw-tab-item", "onclick":"bw.selectTabContent(this)"},x[0]];});
|
|
1517
|
+
var tc = tabData.map(function(x){return ["div",{"class":"bw-tab-content"},x[1]];});
|
|
1518
|
+
|
|
1519
|
+
ti[0][1]["class"] = bw.classStrAddDel(ti[0][1]["class"], "bw-tab-active");
|
|
1520
|
+
tc[0][1]["class"] = bw.classStrAddDel(tc[0][1]["class"], "bw-show");
|
|
1521
|
+
|
|
1522
|
+
dopts["atr" ]["class"] = bw.classStrAddDel (dopts["atr" ]["class"],"bw-tab-container");
|
|
1523
|
+
dopts["tab_atr" ]["class"] = bw.classStrAddDel (dopts["tab_atr" ]["class"],"bw-tab-item-list");
|
|
1524
|
+
dopts["tabc_atr"]["class"] = bw.classStrAddDel (dopts["tabc_atr"]["class"],"bw-tab-content-list");
|
|
1525
|
+
|
|
1526
|
+
return bw.html({t:"div",a: dopts["atr"],c:[["ul",dopts["tab_atr"],ti],["div",dopts["tabc_atr"],tc]]});
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
|
|
1530
|
+
// ===================================================================================
|
|
1531
|
+
|
|
1532
|
+
bw.htmlTable = function(data,opts) {
|
|
1533
|
+
/**
|
|
1534
|
+
bw.makeHTMLTableStr (data, options)
|
|
1535
|
+
|
|
1536
|
+
Creates an HTML table element (as a string) from raw array data.
|
|
1537
|
+
|
|
1538
|
+
var table1 =
|
|
1539
|
+
[["this", "that", "the", "other"],[,6,4,0,4],[3,5,1,4],[1,2,4,5],["2u30","23",function(){return 834},23]];
|
|
1540
|
+
document.getElementById("myTableDiv") = bw.makeHTMLTableStr(table1); // displays simple table.
|
|
1541
|
+
|
|
1542
|
+
var options = {
|
|
1543
|
+
useFirstRowAsHeaders:false, // first row is data
|
|
1544
|
+
caption:"Important Table" // caption
|
|
1545
|
+
sortable: false | true | function // make table sortable (false is default, if true uses bw built-in sort, else supply function)
|
|
1546
|
+
}
|
|
1547
|
+
document.getElementById("myTableDiv") = bw.htmlTable(table1, options);
|
|
1548
|
+
|
|
1549
|
+
Options:
|
|
1550
|
+
useFirstRowAsHeaders : true; //
|
|
1551
|
+
*/
|
|
1552
|
+
if ((_to(data) != "array") || (data.length < 1))
|
|
1553
|
+
return "";
|
|
1554
|
+
|
|
1555
|
+
//default options
|
|
1556
|
+
var dopts = {
|
|
1557
|
+
useFirstRowAsHeaders : true,
|
|
1558
|
+
useDefaultStyle : true, // for
|
|
1559
|
+
atr : {}, // attributes for table object can use function() for dynamic
|
|
1560
|
+
thead_atr : {}, // attributes for table head section
|
|
1561
|
+
th_atr : {}, // attributes for header cells,
|
|
1562
|
+
tbody_atr : {}, // atttributs for table body section
|
|
1563
|
+
tr_atr : {}, // attributes for rows
|
|
1564
|
+
td_atr : {}, // attributes for cells
|
|
1565
|
+
caption : "", // optional table caption (can be HTML, or function, bw.buildHTMLObjString compatible data)
|
|
1566
|
+
sortable : false// make table sortable. if true, uses default sort, otherwise pass function to sort table. f(a,b,optionalColumnNumber)
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
var i=0,head="",body="",r,_hs=bw.html;
|
|
1570
|
+
dopts = optsCopy(dopts,opts);
|
|
1571
|
+
|
|
1572
|
+
if (dopts.useDefaultStyle) {
|
|
1573
|
+
// dopts.atr["class"] = "bw-table-stripe bw-table-col0-bold bw-table-compact bw-table-border-round bw-table-head bw-table-cellpad";
|
|
1574
|
+
dopts.atr["class"] = "bw-table bw-table-stripe";
|
|
1575
|
+
}
|
|
1576
|
+
if (dopts.sortable == true) {
|
|
1577
|
+
dopts.th_atr["onclick"] = "bw.sortTableDispatch(this)";
|
|
1578
|
+
if ("class" in dopts.th_atr)
|
|
1579
|
+
dopts.th_atr["class"] += dopts.th_atr["class"].split(/[ ]+/).indexOf("bw-table-sort-xxa") <0 ? " bw-table-sort-xxa" : "";
|
|
1580
|
+
else
|
|
1581
|
+
dopts.th_atr["class"] = "bw-table-sort-xxa";
|
|
1582
|
+
}
|
|
1583
|
+
else {
|
|
1584
|
+
if (_to(dopts.sortable) == "function") {
|
|
1585
|
+
var sfid = bw.funcRegister(dopts.sortable);
|
|
1586
|
+
dopts.th_atr["onclick"] = bw.funcGetDispatchStr(sfid,"this");
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (dopts["useFirstRowAsHeaders"]) {
|
|
1591
|
+
head=data[0].map(function(x){return _hs({t:"th",a:dopts.th_atr,c:x});}).join("");
|
|
1592
|
+
head= _hs({t:"tr",a:dopts.tr_atr,c:head});
|
|
1593
|
+
i=1;
|
|
1594
|
+
}
|
|
1595
|
+
else
|
|
1596
|
+
i=0;
|
|
1597
|
+
head = bw.html({t:"thead",a:dopts.thead_atr,c:head});
|
|
1598
|
+
|
|
1599
|
+
for (; i<data.length; i++) {
|
|
1600
|
+
r = data[i].map(function(x){return _hs({t:"td",a:dopts.td_atr,c:x});}).join("");
|
|
1601
|
+
body+= _hs({t:"tr",a:dopts.tr_atr,c:r});
|
|
1602
|
+
}
|
|
1603
|
+
body = bw.html({t:"tbody",a:dopts.tbody_atr,c:body});
|
|
1604
|
+
//console.log(head,'\n',body);
|
|
1605
|
+
dopts.caption = dopts.caption == "" ? "" : _hs({t:"caption",a:{},c:dopts.caption});
|
|
1606
|
+
return _hs({t:"table",a:dopts.atr,c:[dopts.caption,head,body]});
|
|
1607
|
+
};
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
bw.htmlAccordian = function (data, opts) {
|
|
1611
|
+
/**
|
|
1612
|
+
htmlAccordian
|
|
1613
|
+
|
|
1614
|
+
[[data-title, data-to-show, {show: true|false}], // show determines whether the default content is visible
|
|
1615
|
+
[...]]
|
|
1616
|
+
|
|
1617
|
+
data-title and data-to-show can be strings or any valid bw.html() constructs
|
|
1618
|
+
*/
|
|
1619
|
+
var s = "";
|
|
1620
|
+
if (_to(data) !== "array")
|
|
1621
|
+
return s;
|
|
1622
|
+
|
|
1623
|
+
var dopts = {
|
|
1624
|
+
"atr" : { "class": "bw-accordian-container"}, // div for overall accordian
|
|
1625
|
+
"atr_h" : { "onclick":"bw.DOMClassToggle(this.nextSibling,'bw-hide')", "class" : "bw-thm-light"}, // div wrapping each header
|
|
1626
|
+
"atr_c" : { "class":"bw-hide"} // div wrapping each content
|
|
1627
|
+
};
|
|
1628
|
+
dopts = optsCopy(dopts,opts);
|
|
1629
|
+
dopts.atr_h["onclick"]="bw.DOMClassToggle(this.nextSibling,'bw-hide')";
|
|
1630
|
+
//dopts.atr_h["class"]="bw-thm-light";
|
|
1631
|
+
//console.log(dopts);
|
|
1632
|
+
s = data.map(function(x){
|
|
1633
|
+
var a=dopts["atr_c"],show;
|
|
1634
|
+
show = ( (x.length > 2) && (x[2].show==true));
|
|
1635
|
+
|
|
1636
|
+
|
|
1637
|
+
if (a["class"]) {
|
|
1638
|
+
a["class"] = show ? bw.classStrAddDel(a["class"],"","bw-hide"):bw.classStrAddDel(a["class"],"bw-hide") ;
|
|
1639
|
+
}
|
|
1640
|
+
else a["class"]=show ? "":"bw-hide";
|
|
1641
|
+
|
|
1642
|
+
return bw.html({t:"div",a:dopts["atr_h"],c:[x[0]]})+bw.html({t:"div",a:a,c:[x[1]]} );
|
|
1643
|
+
}).join("");
|
|
1644
|
+
s = bw.html({t:"div",a:dopts["atr"],c:[s]});
|
|
1645
|
+
return s;
|
|
1646
|
+
};
|
|
1647
|
+
// ===================================================================================
|
|
1648
|
+
bw.htmlSign = function (content, opts) {
|
|
1649
|
+
/**
|
|
1650
|
+
htmlSign("my content",options)
|
|
1651
|
+
create a centered banner / billboard
|
|
1652
|
+
*/
|
|
1653
|
+
var dopts = {
|
|
1654
|
+
atr : {style:{"font-weight":"700", "font-size":"7em"}},
|
|
1655
|
+
escContent : false,
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
dopts = optsCopy(dopts,opts);
|
|
1659
|
+
content = dopts.escContent!=false ? bw.htmlSafeStr(content) : content;
|
|
1660
|
+
var c = {a:{class:"bw-sign" },c:[{c:{a:dopts.atr, c:[content]}}]};
|
|
1661
|
+
//{a:{class:"bw-jumbo"},c:[{c:{ c:"foo"}}]}
|
|
1662
|
+
return bw.html(c);
|
|
1663
|
+
};
|
|
1664
|
+
// ===================================================================================
|
|
1665
|
+
bw.getFile = function (fname,callback_fn, options) {
|
|
1666
|
+
/**
|
|
1667
|
+
bw.getFile(filename,callback)
|
|
1668
|
+
Attempt to load a file.
|
|
1669
|
+
Works both client side and i nodejs.
|
|
1670
|
+
*/
|
|
1671
|
+
var dops = {
|
|
1672
|
+
parser : "raw" // valid types are "raw", "JSON", future "CSV", "TSV" or parserFunction
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
dops = optsCopy(dops,options);
|
|
1676
|
+
|
|
1677
|
+
if (_to(fname) != "string") {
|
|
1678
|
+
return "invalid filename";
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
var prs = (dops["parser"]=="JSON") ? JSON.parse : function(s){return s;};
|
|
1682
|
+
|
|
1683
|
+
|
|
1684
|
+
if (bw.isNodeJS() ==true) {
|
|
1685
|
+
var fs = require("fs");
|
|
1686
|
+
fs.readFile(fname, "utf8", function (err, data) { if (err) throw err; callback_fn(prs(data)); });
|
|
1687
|
+
}
|
|
1688
|
+
else // running in a browser
|
|
1689
|
+
{
|
|
1690
|
+
var x = new XMLHttpRequest();
|
|
1691
|
+
x.overrideMimeType("application/json");
|
|
1692
|
+
x.open("GET", fname, true);
|
|
1693
|
+
x.onreadystatechange =
|
|
1694
|
+
function () {if (x.readyState == 4 && x.status == "200") {callback_fn(prs(x.responseText));}};
|
|
1695
|
+
x.send(null);
|
|
1696
|
+
}
|
|
1697
|
+
return "BW_OK";
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
bw.getJSONFile = function (fname,callback_fn) { return bw.getFile(fname,callback_fn,{"parser":"JSON"});};
|
|
1701
|
+
|
|
1702
|
+
bw.copyToClipboard = function(data) {
|
|
1703
|
+
/**
|
|
1704
|
+
bw.copyToClipboard
|
|
1705
|
+
simple copy content to clipboard. (browser only)
|
|
1706
|
+
*/
|
|
1707
|
+
|
|
1708
|
+
/*
|
|
1709
|
+
var temp = document.createElement("input");
|
|
1710
|
+
var b = document.getElementsByTagName("body")[0];
|
|
1711
|
+
b.appendChild(temp);
|
|
1712
|
+
|
|
1713
|
+
temp.innerText = data;
|
|
1714
|
+
temp.select();
|
|
1715
|
+
document.execCommand("copy");
|
|
1716
|
+
temp.remove();
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
|
|
1720
|
+
var temp = document.createElement("input");
|
|
1721
|
+
document.getElementsByTagName("body")[0].append(temp);
|
|
1722
|
+
temp.innerHTML = data;
|
|
1723
|
+
//temp.val(data).select();
|
|
1724
|
+
|
|
1725
|
+
//var temp = document.createElement("input");
|
|
1726
|
+
//var b = document.getElementsByTagName("body")[0];
|
|
1727
|
+
//b.appendChild(temp);
|
|
1728
|
+
//temp.innerText = data;
|
|
1729
|
+
temp.select();
|
|
1730
|
+
document.execCommand("copy");
|
|
1731
|
+
temp.remove();
|
|
1732
|
+
*/
|
|
1733
|
+
if (bw.isNodeJS())
|
|
1734
|
+
return;
|
|
1735
|
+
var listener = function (e) {
|
|
1736
|
+
e.clipboardData.setData("text/html", data);
|
|
1737
|
+
e.clipboardData.setData("text/plain", data);
|
|
1738
|
+
e.preventDefault();
|
|
1739
|
+
};
|
|
1740
|
+
document.addEventListener("copy", listener);
|
|
1741
|
+
document.execCommand("copy");
|
|
1742
|
+
document.removeEventListener("copy", listener);
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
// ===================================================================================
|
|
1746
|
+
bw.saveClientFile = function(fname,data) {
|
|
1747
|
+
/**
|
|
1748
|
+
bw.saveClientFile(fname,data) saves data the program the client environtmnet
|
|
1749
|
+
fname is filename to save as
|
|
1750
|
+
data is data to save.
|
|
1751
|
+
|
|
1752
|
+
works both in node and browser.
|
|
1753
|
+
*/
|
|
1754
|
+
if (bw.isNodeJS()) {
|
|
1755
|
+
var fs = require("fs");
|
|
1756
|
+
fs.writeFile(fname, data, function (err) {
|
|
1757
|
+
if (err) return bw.log(err);
|
|
1758
|
+
bw.log("error saving ",fname,data);
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
else { // we're in a browser
|
|
1762
|
+
|
|
1763
|
+
var saveData = (function () {
|
|
1764
|
+
var a = document.createElement("a");
|
|
1765
|
+
document.body.appendChild(a);
|
|
1766
|
+
a.style = "display: none";
|
|
1767
|
+
return function (data, fname) {
|
|
1768
|
+
var json = JSON.stringify(data),
|
|
1769
|
+
blob = new Blob([json], {type: "octet/stream"}),
|
|
1770
|
+
url = window.URL.createObjectURL(blob);
|
|
1771
|
+
a.href = url;
|
|
1772
|
+
a.download = fname;
|
|
1773
|
+
a.click();
|
|
1774
|
+
window.URL.revokeObjectURL(url);
|
|
1775
|
+
};
|
|
1776
|
+
}());
|
|
1777
|
+
saveData(data,fname);
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
// ===================================================================================
|
|
1782
|
+
//Timers ... clear / read / fixed number of events
|
|
1783
|
+
|
|
1784
|
+
// ===================================================================================
|
|
1785
|
+
// crude performance measurements
|
|
1786
|
+
var gBWTime = (new Date()).getTime(); //global closure for time. 'cause we always want a gbw gbw time :)
|
|
1787
|
+
|
|
1788
|
+
// ===================================================================================
|
|
1789
|
+
bw.clearTimer = function (message) {
|
|
1790
|
+
/**
|
|
1791
|
+
bw.clearTimer("message")
|
|
1792
|
+
When bitwrench loads its starts a timer which can be checked at any time as a ref running (see bw.readTimer()).
|
|
1793
|
+
bw.clearTimer() clears the timer with optional message.
|
|
1794
|
+
*/
|
|
1795
|
+
gBWTime = (new Date()).getTime();
|
|
1796
|
+
if (_to(message) != "undefined")
|
|
1797
|
+
bw.logd(String(message));
|
|
1798
|
+
return gBWTime;
|
|
1799
|
+
};
|
|
1800
|
+
|
|
1801
|
+
// ===================================================================================
|
|
1802
|
+
bw.readTimer = function (message) {
|
|
1803
|
+
/**
|
|
1804
|
+
bw.readTimer("message")
|
|
1805
|
+
When bitwrench loads its starts a page timer which can be checked for how long the page as been running.
|
|
1806
|
+
*/
|
|
1807
|
+
var ct = (new Date()).getTime();
|
|
1808
|
+
if (_to(message) != "undefined")
|
|
1809
|
+
bw.logd(String(message));
|
|
1810
|
+
return ct-gBWTime;
|
|
1811
|
+
};
|
|
1812
|
+
bw.clearTimer(); //when bw is loaded, we start the timer.
|
|
1813
|
+
|
|
1814
|
+
// ===================================================================================
|
|
1815
|
+
bw.setIntervalX = function (callback, delay, number_of_repetitions) {
|
|
1816
|
+
/**
|
|
1817
|
+
bw.setIntervalX(callbackFn, delayBtwCalls, repetitions)
|
|
1818
|
+
set a javascript timer to only run a max of N repetions.
|
|
1819
|
+
|
|
1820
|
+
Example:
|
|
1821
|
+
bw.setIntervalX(function(x){console.log(x)},100,5)
|
|
1822
|
+
this will the function 5 times 100ms apart
|
|
1823
|
+
*/
|
|
1824
|
+
var x = 0;
|
|
1825
|
+
var intervalID = setInterval(function () {
|
|
1826
|
+
callback(x);
|
|
1827
|
+
|
|
1828
|
+
if (++x >= number_of_repetitions) {
|
|
1829
|
+
clearInterval(intervalID);
|
|
1830
|
+
}
|
|
1831
|
+
}, delay);
|
|
1832
|
+
};
|
|
1833
|
+
|
|
1834
|
+
// ===================================================================================
|
|
1835
|
+
bw.repeatUntil = function (testFn, successFn, failFn, delay, maxReps, lastFn) {
|
|
1836
|
+
/**
|
|
1837
|
+
bw.repeatUntil()
|
|
1838
|
+
repeatUntil runs the supplied testFn every delay milliseconds up until a maxReps number of times.
|
|
1839
|
+
if the test function returns true it runs the successFn and stops the iterations.
|
|
1840
|
+
then the lastFn is called with the params (true, number_of_attempts).
|
|
1841
|
+
lastFn is optional.
|
|
1842
|
+
|
|
1843
|
+
for each time the testFn is called and fails, the failFn() is called.
|
|
1844
|
+
|
|
1845
|
+
After the last rep has been completed the lastFn is called with (with the last testFn result and
|
|
1846
|
+
with the current iteration).
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
lastFn is optional.
|
|
1850
|
+
failFn is optional
|
|
1851
|
+
|
|
1852
|
+
Example:
|
|
1853
|
+
bw.repeatUntil( myLibsAndDataAreLoaded_fn, renderMyChart, null, 250, 10, null); // attempts to wait until mylib is loaded 10 times before giving up
|
|
1854
|
+
|
|
1855
|
+
*/
|
|
1856
|
+
var _count = 0;
|
|
1857
|
+
if (typeof testFn != "function")
|
|
1858
|
+
return "err";
|
|
1859
|
+
if (typeof delay != "number")
|
|
1860
|
+
delay = 250; // 250ms
|
|
1861
|
+
if (typeof maxReps != "number")
|
|
1862
|
+
maxReps = 1; // run 1 time.
|
|
1863
|
+
|
|
1864
|
+
var _testFn = testFn;
|
|
1865
|
+
var _successFn = (typeof successFn == "function") ? successFn : function () {};
|
|
1866
|
+
var _failFn = (typeof failFn == "function") ? failFn : function () {};
|
|
1867
|
+
var _lastFn = (typeof lastFn == "function") ? lastFn : function () {};
|
|
1868
|
+
|
|
1869
|
+
var _f = function () {
|
|
1870
|
+
var success = _testFn();
|
|
1871
|
+
if (true == success) {
|
|
1872
|
+
_successFn();
|
|
1873
|
+
_lastFn(true, _count);
|
|
1874
|
+
}
|
|
1875
|
+
else {
|
|
1876
|
+
_failFn();
|
|
1877
|
+
|
|
1878
|
+
if (_count >= maxReps) {
|
|
1879
|
+
_lastFn(success, _count);
|
|
1880
|
+
}
|
|
1881
|
+
else {
|
|
1882
|
+
_count++;
|
|
1883
|
+
window.setTimeout(_f, delay);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
_f();
|
|
1888
|
+
};
|
|
1889
|
+
// =============================================================================================
|
|
1890
|
+
/*
|
|
1891
|
+
bw.htmlDataToImg = function(data, opts) {
|
|
1892
|
+
/**
|
|
1893
|
+
htmlDataToImg(data, opts) // takes a 2D array of numbers and render as an image
|
|
1894
|
+
each data point must evaluate to a Number or be a function which will be called with its positional arguments and must return a number.
|
|
1895
|
+
|
|
1896
|
+
OR
|
|
1897
|
+
|
|
1898
|
+
function can be a string as long as it returns a valud HTML color prefixed with "#"
|
|
1899
|
+
|
|
1900
|
+
e.g.
|
|
1901
|
+
"#123"
|
|
1902
|
+
"#112233"
|
|
1903
|
+
|
|
1904
|
+
e.g.:
|
|
1905
|
+
function (return 23)
|
|
1906
|
+
function(x,y) { return x+y;}
|
|
1907
|
+
|
|
1908
|
+
* /
|
|
1909
|
+
var dopts = {
|
|
1910
|
+
outputType : "canvas" , // "table" | "divs" | "svg"
|
|
1911
|
+
colorMode : "auto", // use greyscale map
|
|
1912
|
+
colorStretch: 1.0
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
dopts = optsCopy(dopts,opts);
|
|
1916
|
+
// if (_to(dopts["colorMapFn"]) != "function")
|
|
1917
|
+
// dopts["colorMapFn"] = function(x){var c= mapScale(x,0,255,0,255,true).}
|
|
1918
|
+
|
|
1919
|
+
|
|
1920
|
+
|
|
1921
|
+
}
|
|
1922
|
+
*/
|
|
1923
|
+
// =============================================================================================
|
|
1924
|
+
bw.naturalCompare = function (as, bs){
|
|
1925
|
+
/**
|
|
1926
|
+
bw.naturalCompare(a,b) {
|
|
1927
|
+
bw.naturalCompare() is a function which can be passed to an array sort to provide natural sorting of mixed array elements.
|
|
1928
|
+
|
|
1929
|
+
[3,4,2,1,"10","111","foo","bar","01","this123","This123", "848"].sort()
|
|
1930
|
+
vs
|
|
1931
|
+
[3,4,2,1,"10","111","foo","bar","01","this123","This123", "848"].sort(bw.naturalCompare)
|
|
1932
|
+
|
|
1933
|
+
it is the default sort for bw.sortHTMLTable()
|
|
1934
|
+
|
|
1935
|
+
*/
|
|
1936
|
+
//https://www.webdeveloper.com/forum/d/254726-sorting-alphanumeric-array (taken from here) see also
|
|
1937
|
+
//using .localCompare() in newer versions of JS
|
|
1938
|
+
|
|
1939
|
+
var a, b, a1, b1, i= 0, L, rx= /(\d+)|(\D+)/g, rd= /\d/;
|
|
1940
|
+
if(isFinite(as) && isFinite(bs)) return Math.sign(as - bs);
|
|
1941
|
+
a= String(as).toLocaleLowerCase();
|
|
1942
|
+
b= String(bs).toLocaleLowerCase();
|
|
1943
|
+
if(a=== b) return (as > bs) ? 1 : 0;
|
|
1944
|
+
if(!(rd.test(a) && rd.test(b))) return a> b? 1:-1;
|
|
1945
|
+
a= a.match(rx);
|
|
1946
|
+
b= b.match(rx);
|
|
1947
|
+
L= a.length> b.length? b.length:a.length;
|
|
1948
|
+
while(i<L){
|
|
1949
|
+
a1= a[i];
|
|
1950
|
+
b1= b[i++];
|
|
1951
|
+
if(a1!== b1){
|
|
1952
|
+
if(isFinite(a1) && isFinite(b1)){
|
|
1953
|
+
if(a1.charAt(0)=== "0") a1= "." + a1;
|
|
1954
|
+
if(b1.charAt(0)=== "0") b1= "." + b1;
|
|
1955
|
+
return a1 - b1;
|
|
1956
|
+
}
|
|
1957
|
+
else return a1> b1? 1:-1;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return Math.sign(a.length - b.length);
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
// =============================================================================================
|
|
1964
|
+
bw.sortHTMLTable = function (table, col, dir, sortFunction) {
|
|
1965
|
+
/**
|
|
1966
|
+
bw.sortHTMLTable(table, column, optionalSortFunction).
|
|
1967
|
+
|
|
1968
|
+
sort any HTML table active in the DOM
|
|
1969
|
+
table must be a valid DOM table element or CSS selector (first element is used)
|
|
1970
|
+
|
|
1971
|
+
default uses string compare. but can pass in a function
|
|
1972
|
+
sortFunc(a,b,col) // a and b are the cells to compare, col is optional info on what column this is
|
|
1973
|
+
*/
|
|
1974
|
+
|
|
1975
|
+
var rows, switching, i, x, y, shouldSwitch;
|
|
1976
|
+
var sortF = _to(sortFunction) == "function" ? sortFunction : bw.naturalCompare;
|
|
1977
|
+
|
|
1978
|
+
table = bw.DOM(table)[0];
|
|
1979
|
+
|
|
1980
|
+
dir = (dir==true) || (dir=="up") ? true : false;
|
|
1981
|
+
|
|
1982
|
+
switching = true;
|
|
1983
|
+
col = _to(col) == "number" ? col : 0; //default sort on left most column
|
|
1984
|
+
|
|
1985
|
+
//Make a loop that will continue until no switching has been done
|
|
1986
|
+
while (switching) {
|
|
1987
|
+
//start by saying: no switching is done:
|
|
1988
|
+
switching = 0;
|
|
1989
|
+
rows = table.getElementsByTagName("TR");
|
|
1990
|
+
/*Loop through all table rows (except the first, which contains table headers):*/
|
|
1991
|
+
for (i = 1; i < (rows.length - 1); i++) {
|
|
1992
|
+
//start by saying there should be no switching:
|
|
1993
|
+
shouldSwitch = 0;
|
|
1994
|
+
/*Get the two elements you want to compare,
|
|
1995
|
+
one from current row and one from the next:*/
|
|
1996
|
+
x = rows[i].getElementsByTagName("TD")[col].innerHTML;
|
|
1997
|
+
y = rows[i + 1].getElementsByTagName("TD")[col].innerHTML;
|
|
1998
|
+
|
|
1999
|
+
//check if the two rows should switch place:
|
|
2000
|
+
|
|
2001
|
+
shouldSwitch = (dir) ? sortF(x,y,col) > 0 : sortF(x,y,col) < 0;
|
|
2002
|
+
if (shouldSwitch)
|
|
2003
|
+
break;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
if (shouldSwitch) {
|
|
2007
|
+
//If a switch has been marked, make the switch and mark that a switch has been done:
|
|
2008
|
+
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
|
2009
|
+
switching = true;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
// =============================================================================================
|
|
2015
|
+
bw.sortTableDispatch = function (item,fn) {
|
|
2016
|
+
/**
|
|
2017
|
+
bw.sortTableDispatch(el) is used to bind sorting functions to tables generated by bw.htmlTable(....)
|
|
2018
|
+
item must be a valid DOM element or id.
|
|
2019
|
+
*/
|
|
2020
|
+
var i;
|
|
2021
|
+
|
|
2022
|
+
item = bw.DOM(item)[0];
|
|
2023
|
+
|
|
2024
|
+
if (_to(item).substr(0,4) != "html")
|
|
2025
|
+
return false; //something not right about this table element
|
|
2026
|
+
|
|
2027
|
+
var index=0,dir;
|
|
2028
|
+
var cols = item.parentElement.getElementsByTagName("th");
|
|
2029
|
+
//update which tab selected
|
|
2030
|
+
for (i=0; i< cols.length; i++) {
|
|
2031
|
+
if (cols[i] == item) { // selected tab logic
|
|
2032
|
+
index = i;
|
|
2033
|
+
dir = bw.DOMClass(cols[i],"bw-table-sort-upa") ; // ifthe current col is already up..
|
|
2034
|
+
if (dir) {
|
|
2035
|
+
bw.DOMClass(cols[i],"bw-table-sort-upa", "bw-table-sort-dna" );
|
|
2036
|
+
}
|
|
2037
|
+
else { //dna or xxa
|
|
2038
|
+
if (bw.DOMClass(cols[i],"bw-table-sort-dna")) {
|
|
2039
|
+
bw.DOMClass(cols[i],"bw-table-sort-dna", "bw-table-sort-upa" );
|
|
2040
|
+
} else
|
|
2041
|
+
bw.DOMClass(cols[i],"bw-table-sort-xxa", "bw-table-sort-upa" );
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
else{ // its not the selected column so we clear the up / down arrow
|
|
2045
|
+
bw.DOMClass(cols[i],"bw-table-sort-upa","");
|
|
2046
|
+
bw.DOMClass(cols[i],"bw-table-sort-dna","");
|
|
2047
|
+
bw.DOMClass(cols[i],"bw-table-sort-xxa","bw-table-sort-xxa");
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
}
|
|
2051
|
+
bw.sortHTMLTable(item.parentElement.parentElement.parentElement,index,dir,fn);
|
|
2052
|
+
};
|
|
2053
|
+
// ===================================================================================
|
|
2054
|
+
/**
|
|
2055
|
+
bw.function dispatch for DOM elements..
|
|
2056
|
+
|
|
2057
|
+
the bw.fnRegistry{} is a dict of user supplied functions are assigned IDs by bitwrench. Using these IDs one can call the functions which is useful in DOM string contexts such as makeHTMLTable() or buildHTMLObjStr().
|
|
2058
|
+
*/
|
|
2059
|
+
var _fnRegistry = {};
|
|
2060
|
+
var _fnIDCounter = 0;
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
bw.funcRegister = function (fn, forceName) {
|
|
2064
|
+
/**
|
|
2065
|
+
bw.funcRegister()
|
|
2066
|
+
register a function to be called by iD.
|
|
2067
|
+
fn is any function or can be anonymous function.
|
|
2068
|
+
(optional) forceName forces the returned ID used to be forceName. forceName must be only alpha and numeric chars.
|
|
2069
|
+
forceName is useful when declaring static HTML content and one wants to use the bwFunctionDispatch but before bitwrench has been loaded or run.
|
|
2070
|
+
|
|
2071
|
+
In this case in the static code call like this:
|
|
2072
|
+
|
|
2073
|
+
<div class="..." onclick="bw.funcGetById('myFnName')(this)"> regular html content goes here </div>
|
|
2074
|
+
<script ..>
|
|
2075
|
+
function superDuperFunctionCode (a) { .... code for my function ... };
|
|
2076
|
+
bw.funcRegister(superDuperFunctionCode,"myFnName"); //now when the element is clicked on superDuperFunctionCode() will be called.
|
|
2077
|
+
</script>
|
|
2078
|
+
|
|
2079
|
+
*/
|
|
2080
|
+
var fnID = "class_bwfn_" + _fnIDCounter;
|
|
2081
|
+
_fnIDCounter++;
|
|
2082
|
+
fnID = _to(forceName) == "string" ? forceName : fnID;
|
|
2083
|
+
fnID.trim();
|
|
2084
|
+
_fnRegistry[fnID] = fn;
|
|
2085
|
+
return fnID;
|
|
2086
|
+
};
|
|
2087
|
+
|
|
2088
|
+
bw.funcUnregister = function (fnID) {
|
|
2089
|
+
/**
|
|
2090
|
+
bw.funcUnregister(fnID)
|
|
2091
|
+
remove a function from the bitwrench dispatch registry
|
|
2092
|
+
*/
|
|
2093
|
+
if (fnID in _fnRegistry)
|
|
2094
|
+
delete _fnRegistry[fnID];
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
bw.funcGetById = function(fnID,errFn) {
|
|
2098
|
+
/**
|
|
2099
|
+
bw.funcGetById(fnId, errFn)
|
|
2100
|
+
allows a function to be exectued by its bw function ID.
|
|
2101
|
+
bw.funcGetById(myId)(... args ...)
|
|
2102
|
+
|
|
2103
|
+
errFn is optional function to call if fnID is not found.
|
|
2104
|
+
|
|
2105
|
+
example:
|
|
2106
|
+
var myFunc = bw.getFuncById("myFuncID"); // function must already be registered.
|
|
2107
|
+
*/
|
|
2108
|
+
fnID = String(fnID);
|
|
2109
|
+
if (fnID in _fnRegistry)
|
|
2110
|
+
return _fnRegistry[fnID];
|
|
2111
|
+
else {
|
|
2112
|
+
var _id = fnID;
|
|
2113
|
+
return (_to(errFn) == "function") ? errFn : function(){bw.log(_id,"bw.funcGetById(): unregistered fn error");} ;
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
2116
|
+
|
|
2117
|
+
bw.funcGetDispatchStr = function (fnID, argstring) {
|
|
2118
|
+
/**
|
|
2119
|
+
bw.funcGetDispatchStr(fnID, argString)
|
|
2120
|
+
create a string suitble for use in DOM element dispatch. note argstring is a literal so variables must be reduce to their values.
|
|
2121
|
+
see bw.funcRegister() for getting valid IDs for user supplied functions.
|
|
2122
|
+
|
|
2123
|
+
example: bw.funcGetDispatchStr("myFuncID","param1,param2")
|
|
2124
|
+
*/
|
|
2125
|
+
|
|
2126
|
+
switch (_to(argstring)) {
|
|
2127
|
+
case "string" :
|
|
2128
|
+
case "number" :
|
|
2129
|
+
argstring = String(argstring);
|
|
2130
|
+
break;
|
|
2131
|
+
case "array" :
|
|
2132
|
+
argstring = argstring.join(",");
|
|
2133
|
+
break;
|
|
2134
|
+
case "function":
|
|
2135
|
+
argstring = argstring();
|
|
2136
|
+
break;
|
|
2137
|
+
default:
|
|
2138
|
+
argstring = "";
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
return "bw.funcGetById('"+fnID+"')("+argstring+")";
|
|
2142
|
+
};
|
|
2143
|
+
|
|
2144
|
+
bw.funcGetRegistry = function() {
|
|
2145
|
+
return _fnRegistry;
|
|
2146
|
+
};
|
|
2147
|
+
|
|
2148
|
+
// =============================================================================================
|
|
2149
|
+
bw.loremIpsum = function (numChars, startSpot, startWithCapitalLetter) {
|
|
2150
|
+
/**
|
|
2151
|
+
bw.loremIpsum(numChars, startSpot)
|
|
2152
|
+
|
|
2153
|
+
generate a simple string of Lorem Ipsum text (sample typographer's text) of numChars in length.
|
|
2154
|
+
|
|
2155
|
+
if startSpot is supplied, it starts the string at the supplied index e.g. bw.loremIpsum(200, 50)
|
|
2156
|
+
will supply 200 chars of loremIpsum starting at index 50 of the Lorem Ipsum sample text.
|
|
2157
|
+
|
|
2158
|
+
if startWithCapitalLetter == true then the function will capitlize the first character or inject a capital letter if ihe first character isn't a capital letter.
|
|
2159
|
+
default is true;
|
|
2160
|
+
|
|
2161
|
+
Default is a paragraph of lorem ipsum (446 chars)
|
|
2162
|
+
*/
|
|
2163
|
+
|
|
2164
|
+
startSpot = _to(startSpot) != "number" ? 0 : Math.round(startSpot);
|
|
2165
|
+
|
|
2166
|
+
var l = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
|
|
2167
|
+
startSpot = startSpot % l.length;
|
|
2168
|
+
l= l.substring(startSpot, l.length) + l.substring(0,startSpot);
|
|
2169
|
+
|
|
2170
|
+
if (_to(numChars) != "number")
|
|
2171
|
+
numChars = l.length;
|
|
2172
|
+
|
|
2173
|
+
var i=numChars, s="";
|
|
2174
|
+
|
|
2175
|
+
|
|
2176
|
+
while (i>0) {
|
|
2177
|
+
s+= (i < l.length) ? l.substring(0,i) : l;
|
|
2178
|
+
i-= l.length;
|
|
2179
|
+
}
|
|
2180
|
+
if (s[s.length-1] == " ")
|
|
2181
|
+
s= s.substring(0,s.length-1) + "."; // always end on non-whitespace. "." was chosen arbitrarily.
|
|
2182
|
+
if (startWithCapitalLetter != false) {
|
|
2183
|
+
var c = s[0].toUpperCase();
|
|
2184
|
+
c = c.match(/[A-Z]/) ? c:"M";
|
|
2185
|
+
s = c+s.substring(1,s.length);
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
return s;
|
|
2189
|
+
|
|
2190
|
+
};
|
|
2191
|
+
|
|
2192
|
+
bw.docString = function (s, options) {
|
|
2193
|
+
/**
|
|
2194
|
+
bw.docString(functionAsString, options)
|
|
2195
|
+
returns array of valid docStrings embedded in a string
|
|
2196
|
+
|
|
2197
|
+
@param docType{string} : "jsdoc" | "python" | "custom" (python means triplequote (") 3 times), "custom" means supply delims
|
|
2198
|
+
@param options {delims:[string,string]} : start, stop delimiters (only used if docType set to "custom")
|
|
2199
|
+
|
|
2200
|
+
@return array{strings} : array of captured params
|
|
2201
|
+
|
|
2202
|
+
*/
|
|
2203
|
+
|
|
2204
|
+
var dopts = {
|
|
2205
|
+
docType : "jsdoc", // "js doc", "python", "other" (jsdoc is default)
|
|
2206
|
+
delims : ["/**","*/"],
|
|
2207
|
+
parseJSDocParams : false,
|
|
2208
|
+
dropLeadin : false // removes lead-in whitespace or floating single * on each line e.g. " * @mycomment" ==> "@mycomment"
|
|
2209
|
+
};
|
|
2210
|
+
dopts = optsCopy(dopts,options);
|
|
2211
|
+
|
|
2212
|
+
var _es = function (str) {return str.replace(/(?=[\\^$*+?.()|{}[\]])/g, "\\");}; // do escape of regex chars
|
|
2213
|
+
|
|
2214
|
+
dopts["delims"] = bw.choice(dopts["docType"],{
|
|
2215
|
+
"jsdoc" : ["/**","*/"],
|
|
2216
|
+
"python": ["\"\"\"","\"\"\""], // old regex: /["]{3}([\s\S]*?)["]{3}/ig
|
|
2217
|
+
"jspy" : ["/**\"\"\"","\"\"\"*/"] // js && python
|
|
2218
|
+
},dopts["delims"]);
|
|
2219
|
+
|
|
2220
|
+
var c = (_to(s)=="function") ? s.toString() : String(s);
|
|
2221
|
+
var r = [];
|
|
2222
|
+
|
|
2223
|
+
try {
|
|
2224
|
+
var re = (new RegExp( _es(dopts["delims"][0])+ "\\s*\\n*([^\\*]|(\\*(?!\\/)))*" +_es(dopts["delims"][1]),"ig")); // "([\\s\\S]*?)"
|
|
2225
|
+
r = c.match(re);
|
|
2226
|
+
}
|
|
2227
|
+
catch (e) {bw.log(String(e));}
|
|
2228
|
+
|
|
2229
|
+
if (_to(r)=="array") {
|
|
2230
|
+
r = r.map(function(x){return x.substring(dopts["delims"][0].length, x.length-dopts["delims"][1].length);}); // this is an array of the contents of docStrings which can still be multiline in thier own right
|
|
2231
|
+
r = (dopts["dropLeadin"]==true) ? r.map(function(x){return x.split(/[\n\r]/).map(function(y){return bw.trim(y,"left")+"\n";});}) : r; // need to hanlde multiline stuff here
|
|
2232
|
+
}
|
|
2233
|
+
else
|
|
2234
|
+
r=[];
|
|
2235
|
+
|
|
2236
|
+
return r;
|
|
2237
|
+
};
|
|
2238
|
+
// =============================================================================================
|
|
2239
|
+
bw.docStringParseLine = function(s) {
|
|
2240
|
+
/**
|
|
2241
|
+
Parse a single line of a jsdoc string.
|
|
2242
|
+
@param {string} s - line of docstring to parse
|
|
2243
|
+
@return - dict of line contents {source: s, field:string, name:string, description: string, types: type1,type2 }
|
|
2244
|
+
if not a valid doc string line then returns source string only
|
|
2245
|
+
*/
|
|
2246
|
+
var r={"source":s, "field" : "", "types":"", "name" :"", "description" : ""};
|
|
2247
|
+
var a = s.replace(/^\s*(\/\*\*?)?|(\*\/)?\s*$/ig,""); // remove the comment markers if still there "/** my comment */"" ==> "my comment"
|
|
2248
|
+
a = a.replace(/^\s*\**\s*/,""); // remove any cruft at beginning of line " * @myParam {}....." ==> "@myParam {}....."
|
|
2249
|
+
if (a.charAt(0) == "@") { // if we have hit a @fieldname parameter we start parsing.
|
|
2250
|
+
// ([str, regex, fieldStr, result{}]) ==> ([str, regex, fieldStr, result{}]) ::> ([str,result{},fieldStr,regex])
|
|
2251
|
+
/*
|
|
2252
|
+
var _tok = function(x){
|
|
2253
|
+
var m = x[0].match(x[1]);
|
|
2254
|
+
if (m != null) {x[4][3]=m[1];}
|
|
2255
|
+
x[0] = x[0].replace(x[1],"");
|
|
2256
|
+
return x;
|
|
2257
|
+
}
|
|
2258
|
+
*/
|
|
2259
|
+
//r = [[e,f],[e,f],[e,f],[e,f]].reduce(,_tok);
|
|
2260
|
+
|
|
2261
|
+
var e,x;
|
|
2262
|
+
var t = bw.trim;
|
|
2263
|
+
e =/^@([A-Za-z0-9_<>[\]]*)/i;
|
|
2264
|
+
x = a.match(e);
|
|
2265
|
+
if (x != null) {r["field"] = t(x[1]);} else return r; // didn't match... opt out here
|
|
2266
|
+
a = a.replace(e,"");
|
|
2267
|
+
|
|
2268
|
+
e = /^\s*\{([A-Za-z0-9_|\s,.\-+!@#$%^&*()=[\]]*)\}/i;
|
|
2269
|
+
x = a.match(e);
|
|
2270
|
+
if (x != null) {r["types"]=t(x[1]);} // types is optional..
|
|
2271
|
+
a = a.replace(e,"");
|
|
2272
|
+
|
|
2273
|
+
e = /^\s*([\S]*)/i;
|
|
2274
|
+
x = a.match(e);
|
|
2275
|
+
if (x != null) {r["description"]=t(x[1]);} //
|
|
2276
|
+
a = a.replace(e,"");
|
|
2277
|
+
|
|
2278
|
+
e = /^\s*([\S]*)/i;
|
|
2279
|
+
x = a.match(e);
|
|
2280
|
+
if (x != null) {r["name"]=t(x[1]);} //
|
|
2281
|
+
a = a.replace(e,"");
|
|
2282
|
+
|
|
2283
|
+
// descrpition ==> name: "" description : "description"
|
|
2284
|
+
// description we we ==> name: "" description : "description we we "
|
|
2285
|
+
// name - description we we ==> name: "name" descrpition : "description we we"
|
|
2286
|
+
// - description we we ==> name: "" description : "description we we"
|
|
2287
|
+
if (r["name"].match(/^\s*-+\s*/) != null) {
|
|
2288
|
+
r["name"] = r["description"];
|
|
2289
|
+
r["description"] = t(a);
|
|
2290
|
+
} else {
|
|
2291
|
+
r["description"] = r["description"]+" "+r["name"]+" "+t(a);
|
|
2292
|
+
r["name"] ="";
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return r;
|
|
2296
|
+
};
|
|
2297
|
+
// =============================================================================================
|
|
2298
|
+
bw.docStringParse = function(s) {
|
|
2299
|
+
/**
|
|
2300
|
+
@method bw.parseJsDocString() parse and extract info from a jsdoc style comment. expects there to be only a single docString comment
|
|
2301
|
+
@description docStringParse parses a jsdoc string
|
|
2302
|
+
and returns the paramters as an array which can be formatted for display or interrogtion.
|
|
2303
|
+
@param{string} - a valid js docstring
|
|
2304
|
+
|
|
2305
|
+
@returns An array of triplets [@param, {types}, comment info]
|
|
2306
|
+
*/
|
|
2307
|
+
|
|
2308
|
+
/*
|
|
2309
|
+
implementation notes:
|
|
2310
|
+
the parser splits the candidate doc string in to lines.
|
|
2311
|
+
|
|
2312
|
+
Examples:
|
|
2313
|
+
|
|
2314
|
+
* Assign the project to an employee.
|
|
2315
|
+
* @param {Object} - The employee who is responsible for the project. ==> ["@param","object","", "The employee who is resposnsible for the project"]
|
|
2316
|
+
* @param {string} employee.name - The name of the employee.
|
|
2317
|
+
* @param {string} employee.department - The employee's department.
|
|
2318
|
+
|
|
2319
|
+
|
|
2320
|
+
*/
|
|
2321
|
+
|
|
2322
|
+
|
|
2323
|
+
s=bw.docString(s)[0];
|
|
2324
|
+
var a = s.split("\n");
|
|
2325
|
+
//console.log(a);
|
|
2326
|
+
var i,r=[bw.docStringParseLine(a[0])];
|
|
2327
|
+
for (i=1;i<a.length;i++) {
|
|
2328
|
+
var l = bw.docStringParseLine(a[i]);
|
|
2329
|
+
if (l["field"]=="") { // nothing parseable...
|
|
2330
|
+
if (r[r.length-1]["field"]=="") {
|
|
2331
|
+
r[r.length-1]["source"] += l["source"];
|
|
2332
|
+
} else
|
|
2333
|
+
r[r.length-1]["description"] += l["source"];
|
|
2334
|
+
} else r.push(l);
|
|
2335
|
+
}
|
|
2336
|
+
return r;
|
|
2337
|
+
};
|
|
2338
|
+
// =============================================================================================
|
|
2339
|
+
bw.isHexStr = function (str, allowChars) {
|
|
2340
|
+
/**
|
|
2341
|
+
isHexStr() returns a number of hex digits found or false if non-hex string.
|
|
2342
|
+
allow is an optional string of characters "-+."etc to permit in the string.
|
|
2343
|
+
the allow characters are not counted in the result
|
|
2344
|
+
|
|
2345
|
+
examples:
|
|
2346
|
+
bw.isHEXStr("123a") ===> 4
|
|
2347
|
+
bw.isHEXStr("12-3a") ===> false
|
|
2348
|
+
bw.isHEXStr("12-3a","-") ===> 4
|
|
2349
|
+
*/
|
|
2350
|
+
if ( _to(str) == "string") {
|
|
2351
|
+
str = str.replace(new RegExp("["+allowChars+"]","g"),"");
|
|
2352
|
+
var isHexReg = new RegExp("^[0-9A-Fa-f]{"+str.length+"}$");
|
|
2353
|
+
return (isHexReg.test(str) == true) ? str.length : false;
|
|
2354
|
+
}
|
|
2355
|
+
return false;
|
|
2356
|
+
};
|
|
2357
|
+
|
|
2358
|
+
// =============================================================================================
|
|
2359
|
+
//bw.__monkey_patch_is_nodejs__ = "ignore"; //used in test suites. use carefully. only acceptable values are true, false, "ignore"
|
|
2360
|
+
bw.__monkey_patch_is_nodejs__ = new (function() { var _t="ignore"; this.set=function(x){_t = _toa(x,"boolean",x,"ignore");}; this.get=function(){return _t;}; return this;});
|
|
2361
|
+
|
|
2362
|
+
bw.isNodeJS = function () {
|
|
2363
|
+
/**
|
|
2364
|
+
bw.isNodeJS() ==> returns true if running in node environment (else browser)
|
|
2365
|
+
*/
|
|
2366
|
+
if (bw.__monkey_patch_is_nodejs__.get() != "ignore")
|
|
2367
|
+
return bw.__monkey_patch_is_nodejs__.get();
|
|
2368
|
+
return (typeof module !== "undefined" && module.exports) !== false; //a hack will fix later
|
|
2369
|
+
};
|
|
2370
|
+
//console.log("=====",bw.isNodeJS())
|
|
2371
|
+
|
|
2372
|
+
// =============================================================================================
|
|
2373
|
+
bw.fixNum = function(num,digits) {
|
|
2374
|
+
/**
|
|
2375
|
+
bw.fixedNum(num,digits)
|
|
2376
|
+
|
|
2377
|
+
Truncate a number at digits number of places.
|
|
2378
|
+
bw.fixNum(1.2345,2) ===> 1.23
|
|
2379
|
+
bw.fixNum(234.32,-2) ===> 200
|
|
2380
|
+
*/
|
|
2381
|
+
num = Number(num);
|
|
2382
|
+
|
|
2383
|
+
if (isNaN(num))
|
|
2384
|
+
return NaN;
|
|
2385
|
+
|
|
2386
|
+
digits = _to(digits) == "number" ? digits : 3;
|
|
2387
|
+
num *= Math.pow(10,digits);
|
|
2388
|
+
|
|
2389
|
+
//num = Math.trunc(num);
|
|
2390
|
+
num = (num > 0) ? Math.floor(num) : Math.ceil(num); // some browsers don't support Math.trunc()
|
|
2391
|
+
|
|
2392
|
+
num /= Math.pow(10,digits);
|
|
2393
|
+
return num;
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
// =============================================================================================
|
|
2397
|
+
bw.multiArray = function (value, dims) {
|
|
2398
|
+
/**
|
|
2399
|
+
bw.multiArray(value, dims)
|
|
2400
|
+
|
|
2401
|
+
return a multidimensional array where all cells are initialized to value.
|
|
2402
|
+
|
|
2403
|
+
bw.multiArray(0,[4,5]) // returns 4x5 array of 0s
|
|
2404
|
+
bw.multiArray("test",[4,5]) // returns 4x5 array of "test"
|
|
2405
|
+
|
|
2406
|
+
this shorthand is available for single dim arrays
|
|
2407
|
+
bw.multiArray(0,5) ===> returns 5x1 array of 0s
|
|
2408
|
+
|
|
2409
|
+
bw.multiArray also accepts functions
|
|
2410
|
+
|
|
2411
|
+
bw.multiArray(bw.random, [3,4]) ===> creates 3x5 array of random #s btw 0..100
|
|
2412
|
+
|
|
2413
|
+
bw.multiArray(function(){return (new Date()).getTime();},[4,6] ) ==> returns values based on the Javascript date
|
|
2414
|
+
*/
|
|
2415
|
+
|
|
2416
|
+
var v = function() { return (_to(value) == "function") ? value(): value;};
|
|
2417
|
+
dims = _to(dims) == "number" ? [dims] : dims;
|
|
2418
|
+
|
|
2419
|
+
var _array = function(a,dim) {
|
|
2420
|
+
if(dim < dims.length) {
|
|
2421
|
+
for(var i=0; i<dims[dim]; i++) {
|
|
2422
|
+
a[i]= (dim== dims.length -1) ? v() : _array([],dim+1);
|
|
2423
|
+
}
|
|
2424
|
+
return a;
|
|
2425
|
+
}
|
|
2426
|
+
};
|
|
2427
|
+
return _array([],0);
|
|
2428
|
+
};
|
|
2429
|
+
|
|
2430
|
+
// =============================================================================================
|
|
2431
|
+
bw.clip = function (data, min, max) {
|
|
2432
|
+
/**
|
|
2433
|
+
@method: bw.clip(data, min, max) clips data in between min and max.
|
|
2434
|
+
|
|
2435
|
+
Examples:
|
|
2436
|
+
bw.clip(5,2,20) ==> 5 // already in range
|
|
2437
|
+
bw.clip(1,2,20) ==> 2 // less than the min value so clips to min value
|
|
2438
|
+
bw.clip([1,4,8,35], 2, 20) ==> [2,4,5,20]
|
|
2439
|
+
*/
|
|
2440
|
+
var l = min < max ? min : max;
|
|
2441
|
+
var h = max > min ? max : min;
|
|
2442
|
+
|
|
2443
|
+
if (_to(data) == "array") {
|
|
2444
|
+
return data.map(function(x){ return (x < l) ? l : ((x > h) ? h : x);});
|
|
2445
|
+
}
|
|
2446
|
+
else
|
|
2447
|
+
return (data < l) ? l : ((data > h) ? h : data);
|
|
2448
|
+
};
|
|
2449
|
+
|
|
2450
|
+
// =============================================================================================
|
|
2451
|
+
bw.mapScale = function (x, in0, in1, out0, out1, options) {
|
|
2452
|
+
/**
|
|
2453
|
+
@method: bw.mapScale()
|
|
2454
|
+
|
|
2455
|
+
Map an input value x in its natural range in0...in1 to the output space out0...out1 with optional clipping
|
|
2456
|
+
expScale allows sigmoidal warping to stretch input values contrained to a small range. (floating point scale factor)
|
|
2457
|
+
x can be either a number or array of numbers.
|
|
2458
|
+
|
|
2459
|
+
if options["clip"] = false, then mapScale will extrapolate outside of out0,out1
|
|
2460
|
+
|
|
2461
|
+
//this is the function that oficially started bitwrench..
|
|
2462
|
+
*/
|
|
2463
|
+
var dopts = {
|
|
2464
|
+
clip : true,
|
|
2465
|
+
expScale : false
|
|
2466
|
+
};
|
|
2467
|
+
|
|
2468
|
+
dopts = optsCopy ( dopts, options);
|
|
2469
|
+
|
|
2470
|
+
if (in0==in1)
|
|
2471
|
+
return x;
|
|
2472
|
+
out0 = _toa(out0, "number", out0, 0);
|
|
2473
|
+
out1 = _toa(out1, "number", out1, 1);
|
|
2474
|
+
|
|
2475
|
+
var ms= function (z) {
|
|
2476
|
+
if (dopts["expScale"]) {
|
|
2477
|
+
var y = ((z-((in1+in0) / 2.0)) / (in1 - in0) ) * dopts["expScale"];
|
|
2478
|
+
z = ((out1-out0)*(1/(1+Math.exp(-y)))) + out0;
|
|
2479
|
+
}
|
|
2480
|
+
else
|
|
2481
|
+
z = (((z-in0)/(in1-in0))*(out1-out0))+out0;
|
|
2482
|
+
|
|
2483
|
+
if (dopts["clip"])
|
|
2484
|
+
z= bw.clip(z,out0,out1);
|
|
2485
|
+
return z;
|
|
2486
|
+
};
|
|
2487
|
+
|
|
2488
|
+
if (_to(x) == "number")
|
|
2489
|
+
return ms(x);
|
|
2490
|
+
return x.map(ms);
|
|
2491
|
+
};
|
|
2492
|
+
|
|
2493
|
+
// =============================================================================================
|
|
2494
|
+
//https://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
|
|
2495
|
+
bw.padNum = function(x, width, options) {
|
|
2496
|
+
/**
|
|
2497
|
+
@description bw.padnum() takes a number and pads left pads (default is '0'). padNum also accepts strings so
|
|
2498
|
+
|
|
2499
|
+
bw.padNum(123,5) ==> " 123"
|
|
2500
|
+
bw.padNum(1234,5) ==> " 1234"
|
|
2501
|
+
bw.padNum("foo",5)==> " foo"
|
|
2502
|
+
@param x {number}
|
|
2503
|
+
@return {string} padded number
|
|
2504
|
+
*/
|
|
2505
|
+
var dopts = {
|
|
2506
|
+
pad : " "
|
|
2507
|
+
};
|
|
2508
|
+
dopts = optsCopy(dopts, options);
|
|
2509
|
+
x = String(x);
|
|
2510
|
+
return (x.length >= width) ? x : new Array(width - x.length+1).join(dopts["pad"]) + x;
|
|
2511
|
+
};
|
|
2512
|
+
// =============================================================================================
|
|
2513
|
+
bw.trim = function (s, dir) {
|
|
2514
|
+
/**
|
|
2515
|
+
@description bw.trim() trims whitespace from string on either left, right, or both. (cross browser works before IE8)
|
|
2516
|
+
@param s {string} : a string to trim white space on
|
|
2517
|
+
@param dir {"left" | "right" | "both" | "none"} : trim white space on left only, right only or both sides, or no trim (default is both)
|
|
2518
|
+
*/
|
|
2519
|
+
var t = bw.choice(
|
|
2520
|
+
dir,
|
|
2521
|
+
{
|
|
2522
|
+
"left" : /^[\s\uFEFF\xA0\n]+/g,
|
|
2523
|
+
"right" : /[\s\uFEFF\xA0\n]+$/g,
|
|
2524
|
+
"none" : /(?!)/ // useful for programmatic scenarios (eg. [....].map ) where not all of the entries should be trimmed.
|
|
2525
|
+
},
|
|
2526
|
+
/^[\s\uFEFF\xA0\n]+|[\s\uFEFF\xA0\n]+$/g
|
|
2527
|
+
);
|
|
2528
|
+
return String(_toa(s,"undefined","",s)).replace(t,"");
|
|
2529
|
+
};
|
|
2530
|
+
|
|
2531
|
+
// =============================================================================================
|
|
2532
|
+
bw.padString = function (s, width, dir, options) {
|
|
2533
|
+
/**
|
|
2534
|
+
@description bw.padString() takes a string and pads it to the specified number of chars either left or right or centered.
|
|
2535
|
+
*/
|
|
2536
|
+
var dopts = {
|
|
2537
|
+
pad : " ",
|
|
2538
|
+
trimDir : "both" // pre-trim the input string: "left", "right", "both", "none"
|
|
2539
|
+
};
|
|
2540
|
+
dopts = optsCopy(dopts, options);
|
|
2541
|
+
|
|
2542
|
+
s = String(s);
|
|
2543
|
+
var x = bw.trim(s,dopts["trimDir"]);
|
|
2544
|
+
var p = (width > x.length ) ? (width - x.length+1) : 0 ; // total padding
|
|
2545
|
+
var q = bw.choice(dir,
|
|
2546
|
+
{
|
|
2547
|
+
"left" : [p,0],
|
|
2548
|
+
"right" : [0,p],
|
|
2549
|
+
"center" : [Math.round(p/2), (p-Math.round(p/2)+1)]
|
|
2550
|
+
},
|
|
2551
|
+
[0,0]
|
|
2552
|
+
);
|
|
2553
|
+
return ((new Array(q[0])).join(dopts["pad"]))+x+(new Array(q[1])).join(dopts["pad"]);
|
|
2554
|
+
};
|
|
2555
|
+
|
|
2556
|
+
// =============================================================================================
|
|
2557
|
+
bw.random = function(rangeBegin, rangeEnd, options) {
|
|
2558
|
+
/**
|
|
2559
|
+
@method: random
|
|
2560
|
+
|
|
2561
|
+
Return a random number between rangeBegin and RangeEnd (inclusive)
|
|
2562
|
+
default is 0,100
|
|
2563
|
+
|
|
2564
|
+
options
|
|
2565
|
+
{
|
|
2566
|
+
setType : "int"
|
|
2567
|
+
dims : false | number | [ , , ] // selector for dimensions
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
options
|
|
2571
|
+
setType:
|
|
2572
|
+
"int" ==> return an integer (default)
|
|
2573
|
+
"float" or "number" ==> return floating point number
|
|
2574
|
+
|
|
2575
|
+
dims
|
|
2576
|
+
false or ommitted ==> return a single number
|
|
2577
|
+
5 ==> return a 5x1 array of random numbers
|
|
2578
|
+
[3,5,2] ==> return a 3x5x2 array of random numbers
|
|
2579
|
+
|
|
2580
|
+
example:
|
|
2581
|
+
bw.random() ==> returns a number btw 0,100
|
|
2582
|
+
bw.random(-4,4,{setType: "float", dims[4,5]}) ==> returns a 3x5 array of floating pt numbers btw -4,4
|
|
2583
|
+
|
|
2584
|
+
see also prandom for psuedorandom numbers
|
|
2585
|
+
|
|
2586
|
+
*/
|
|
2587
|
+
rangeBegin = _to(rangeBegin) == "number" ? rangeBegin : 0;
|
|
2588
|
+
rangeEnd = _to(rangeEnd) == "number" ? rangeEnd : 100;
|
|
2589
|
+
|
|
2590
|
+
var dopts = {
|
|
2591
|
+
setType : "int",
|
|
2592
|
+
dims : false // if dims is array e.g. [3,4,5] returns random elements array
|
|
2593
|
+
};
|
|
2594
|
+
|
|
2595
|
+
dopts = optsCopy(dopts,options);
|
|
2596
|
+
|
|
2597
|
+
var _rnd = function () {
|
|
2598
|
+
var n = 0;
|
|
2599
|
+
|
|
2600
|
+
dopts.setType = ["int","float","number"].indexOf(dopts.setType) == -1 ? "int" : dopts.setType;
|
|
2601
|
+
|
|
2602
|
+
if (rangeEnd < rangeBegin ) {
|
|
2603
|
+
rangeBegin ^= rangeEnd; rangeEnd ^= rangeBegin; rangeBegin ^= rangeEnd;
|
|
2604
|
+
}
|
|
2605
|
+
n = ((Math.random() * (rangeEnd-rangeBegin)) + rangeBegin);
|
|
2606
|
+
|
|
2607
|
+
return (dopts.setType == "int") ? Math.round(n) : n;
|
|
2608
|
+
};
|
|
2609
|
+
|
|
2610
|
+
if ((_to(dopts["dims"]) == "array") || (_to(dopts["dims"])== "number"))
|
|
2611
|
+
return bw.multiArray( _rnd, dopts["dims"]);
|
|
2612
|
+
|
|
2613
|
+
return _rnd();
|
|
2614
|
+
};
|
|
2615
|
+
|
|
2616
|
+
// =============================================================================================
|
|
2617
|
+
bw.prandom = function (rangeBegin,rangeEnd,seed, options) {
|
|
2618
|
+
/**
|
|
2619
|
+
prandom - generate a psuedo random number from internal hash function in a given range
|
|
2620
|
+
*/
|
|
2621
|
+
rangeBegin = _to(rangeBegin) == "number" ? rangeBegin : 0;
|
|
2622
|
+
rangeEnd = _to(rangeEnd) == "number" ? rangeEnd : 100;
|
|
2623
|
+
|
|
2624
|
+
var dopts = {
|
|
2625
|
+
setType : "int",
|
|
2626
|
+
dims : false // if dims is array e.g. [3,4,5] returns random elements array
|
|
2627
|
+
};
|
|
2628
|
+
|
|
2629
|
+
dopts = optsCopy(dopts,options);
|
|
2630
|
+
var _cseed = seed;
|
|
2631
|
+
var _rnd = function () {
|
|
2632
|
+
var n = 0;
|
|
2633
|
+
|
|
2634
|
+
dopts.setType = ["int","float","number"].indexOf(dopts.setType) == -1 ? "int" : dopts.setType;
|
|
2635
|
+
|
|
2636
|
+
if (rangeEnd < rangeBegin ) {
|
|
2637
|
+
rangeBegin ^= rangeEnd; rangeEnd ^= rangeBegin; rangeBegin ^= rangeEnd;
|
|
2638
|
+
}
|
|
2639
|
+
n = (((bw.hashFnv32a("start string",_cseed) & 0xffff)/(65536)) * (rangeEnd-rangeBegin)) + rangeBegin;
|
|
2640
|
+
|
|
2641
|
+
_cseed = (dopts.setType == "int") ? Math.round(n) : n;
|
|
2642
|
+
return (dopts.setType == "int") ? Math.round(n) : n;
|
|
2643
|
+
};
|
|
2644
|
+
|
|
2645
|
+
if ((_to(dopts["dims"]) == "array") || (_to(dopts["dims"])== "number"))
|
|
2646
|
+
return bw.multiArray( _rnd, dopts["dims"]);
|
|
2647
|
+
|
|
2648
|
+
return _rnd();
|
|
2649
|
+
|
|
2650
|
+
};
|
|
2651
|
+
// =============================================================================================
|
|
2652
|
+
|
|
2653
|
+
|
|
2654
|
+
bw.hashFnv32a= function (str, seed, returnHexStr) {
|
|
2655
|
+
/**
|
|
2656
|
+
@method Calculate a 32 bit FNV-1a hash
|
|
2657
|
+
Found here: https://gist.github.com/vaiorabbit/5657561
|
|
2658
|
+
Ref.: http://isthe.com/chongo/tech/comp/fnv/
|
|
2659
|
+
|
|
2660
|
+
@param {string} str the input value
|
|
2661
|
+
|
|
2662
|
+
@param {integer} [seed] optionally pass the hash of the previous chunk
|
|
2663
|
+
|
|
2664
|
+
@param {boolean} [asString=false] set to true to return the hash value as
|
|
2665
|
+
8-digit hex string instead of an integer
|
|
2666
|
+
|
|
2667
|
+
@returns {integer | string}
|
|
2668
|
+
*/
|
|
2669
|
+
/*jshint bitwise:false */
|
|
2670
|
+
var i, l,
|
|
2671
|
+
hval = (typeof seed == "undefined") ? 0x811c9dc5 : seed;
|
|
2672
|
+
|
|
2673
|
+
for (i = 0, l = str.length; i < l; i++) {
|
|
2674
|
+
hval ^= str.charCodeAt(i);
|
|
2675
|
+
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
|
|
2676
|
+
}
|
|
2677
|
+
if( returnHexStr ){
|
|
2678
|
+
// Convert to 8 digit hex string
|
|
2679
|
+
return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
|
|
2680
|
+
}
|
|
2681
|
+
return hval >>> 0;
|
|
2682
|
+
};
|
|
2683
|
+
|
|
2684
|
+
// =============================================================================================
|
|
2685
|
+
bw.CSSMakeTheme = function(color) {
|
|
2686
|
+
/**
|
|
2687
|
+
makeThemeCSS (color)
|
|
2688
|
+
|
|
2689
|
+
makes a CSS theme color palettte based on the supplied color which is exported as a css style
|
|
2690
|
+
|
|
2691
|
+
TODO
|
|
2692
|
+
*/
|
|
2693
|
+
var c = bw.colorRgbToHsl( bw.colorParse(color));
|
|
2694
|
+
|
|
2695
|
+
var p = "bw-theme-";
|
|
2696
|
+
var thm = ["l5","l4","l3","l2","l1","d1","d2","d3","d4","d5"].map(function(x){return p+x;});
|
|
2697
|
+
var im = " !important";
|
|
2698
|
+
thm = thm.map(function(x,i){return [x,[["color", ((i<5)?"#000" : "#fff")+im ],["background-color",c + im] ]]; });
|
|
2699
|
+
|
|
2700
|
+
return thm;
|
|
2701
|
+
|
|
2702
|
+
};
|
|
2703
|
+
// =============================================================================================
|
|
2704
|
+
bw.CSSSimpleStyles = function(appendToHead, options) {
|
|
2705
|
+
/*
|
|
2706
|
+
bw.CSSimpleStyles(appendToHead,options)
|
|
2707
|
+
|
|
2708
|
+
Generate simple styles for bitwrench.
|
|
2709
|
+
write a quick grid style sheet for quick n dirty layout. See docs for examples.
|
|
2710
|
+
|
|
2711
|
+
appendToHead ==> if true, attempts to append to HTML <head> (only writes if not already present)
|
|
2712
|
+
options: {
|
|
2713
|
+
"basics" : "load" // if set to "load will load some global constants for html, body, font-family", set to false to leave these unchanged.
|
|
2714
|
+
"exportCSS": false // if true will wrap the output css in "script" tags.
|
|
2715
|
+
"id" : "bw-default-styles" // id assigned to the script tag, used for preventing multiple loading in a browser page
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
*/
|
|
2719
|
+
|
|
2720
|
+
var dopts = {
|
|
2721
|
+
"globals" : false,
|
|
2722
|
+
"id" : "bw-default-styles",
|
|
2723
|
+
"exportCSS" : false,
|
|
2724
|
+
"colorset" : {"color" : "#000", "background-color" :"#ddd", "active" : "#222"},
|
|
2725
|
+
"pretty" : false, //make easy to read
|
|
2726
|
+
"themes" : // built-in primitive themes
|
|
2727
|
+
[ // must be valid CSS keys / values
|
|
2728
|
+
[".bw-thm-light" , {"color": "#020202 !important;", "background-color": "#e2e2e2 !important;"}],
|
|
2729
|
+
[".bw-thm-dark" , {"color": "#e2e2e2 !important;", "background-color": "#020202 !important;"}],
|
|
2730
|
+
]
|
|
2731
|
+
};
|
|
2732
|
+
|
|
2733
|
+
var s ="\n", i;
|
|
2734
|
+
//var i,j,k,l;
|
|
2735
|
+
var _r = bw.fixNum;
|
|
2736
|
+
var rl = bw.makeCSSRule;
|
|
2737
|
+
dopts = optsCopy(dopts,options);
|
|
2738
|
+
|
|
2739
|
+
var defs = { // defaults
|
|
2740
|
+
defGlobals: {"box-sizing": "border-box"},
|
|
2741
|
+
defContainer: {"height": "100%", "width":"90%", "margin": "0 auto", "padding-left": "2%","padding-right":"2%","left":"0","top":"1%","box-sizing":"border-box"},
|
|
2742
|
+
defFontSerif: {"font-family": "Times New Roman, Times, serif"},
|
|
2743
|
+
defFontSansSerif: {"font-family": "Arial, Helvetica, sans-serif" }
|
|
2744
|
+
};
|
|
2745
|
+
|
|
2746
|
+
if (dopts["globals"] == "load") {
|
|
2747
|
+
s+= rl([["html","body"],defs.defContainer]);
|
|
2748
|
+
s+= rl(["*"+defs.defFontSansSerif]);
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
|
|
2752
|
+
|
|
2753
|
+
var d = [
|
|
2754
|
+
//globals
|
|
2755
|
+
["*", defs.defGlobals],
|
|
2756
|
+
[".bw-def-page-setup" , defs.defContainer],
|
|
2757
|
+
[".bw-font-serif" , defs.defFontSerif],
|
|
2758
|
+
[".bw-font-sans-serif", defs.defFontSansSerif],
|
|
2759
|
+
"\n",
|
|
2760
|
+
//text handling
|
|
2761
|
+
[".bw-left", {"text-align": "left"}],
|
|
2762
|
+
[".bw-right", {"text-align": "right"}],
|
|
2763
|
+
[".bw-center", {"text-align": "center", "margin": "0 auto"}],
|
|
2764
|
+
[".bw-justify", {"text-align": "justify"}],
|
|
2765
|
+
[".bw-code", {"font-family":"monospace", "white-space":"pre-wrap"}],
|
|
2766
|
+
[".bw-pad1", {"padding-left": "1%", "padding-right":"1%"}],
|
|
2767
|
+
"\n",
|
|
2768
|
+
//tables
|
|
2769
|
+
[".bw-table", {"border-collapse":"collapse", "border-spacing": "0", "border":"1px solid #444"}],
|
|
2770
|
+
[".bw-table th", {"background-color": "#bbb", "padding": "4px","border":"1px solid #444"}],
|
|
2771
|
+
[".bw-table td", {"padding": "4px","border":"1px solid #444"}],
|
|
2772
|
+
[".bw-table-stripe tr:nth-child(even)" , {"background-color":"#f0f0f0"}], // striped rows
|
|
2773
|
+
[".bw-table tr td:first-child", {"font-weight": "700"}], // make first col bold
|
|
2774
|
+
[".bw-table-border-round", {"border-radius":"2px"}],
|
|
2775
|
+
[".bw-table-sort-upa::after", {"content": "\"\\2191\""}], // table sort arrow up (when visible arrows chosen)
|
|
2776
|
+
[".bw-table-sort-dna::after", {"content": "\"\\2193\""}], // table sort arrow dn (when visible arrows chosen)
|
|
2777
|
+
[".bw-table-sort-xxa::after", {"content": "\"\\00a0\""}], // table sort space (when visible arrows chosen)
|
|
2778
|
+
"\n",
|
|
2779
|
+
//tabs
|
|
2780
|
+
[".bw-tab-item-list", { "margin": 0, "padding-inline-start":0}],
|
|
2781
|
+
[".bw-tab-item",{"display":"inline", "padding-top":"0.5em", "padding-left":"0.75em", "padding-right":"0.75em", "border-top-right-radius":"7px", "border-top-left-radius": "7px"}],
|
|
2782
|
+
[".bw-tab-active", {/* padding-top:4px; padding-left:6px; padding-right:6px; padding-bottom:0; */ "font-weight":"700"}],
|
|
2783
|
+
[".bw-tab:hover",{"cursor": "pointer", "font-weight": 700/* border: 1px solid #bbb; */}],
|
|
2784
|
+
[".bw-tab-content-list", { margin: 0, "padding-top": "0.0em"}],
|
|
2785
|
+
[".bw-tab-content",{ display:"none", "border-radius":0}],
|
|
2786
|
+
[".bw-tab-content, .bw-tab-active", {"background-color": "#ddd", "padding":"0.5em"}],
|
|
2787
|
+
"\n",
|
|
2788
|
+
//accordian
|
|
2789
|
+
[".bw-accordian-container > div", {"padding": "0.5em"}],
|
|
2790
|
+
"\n",
|
|
2791
|
+
//grid setup
|
|
2792
|
+
[".bw-container",{ margin: "0 auto"}],
|
|
2793
|
+
[".bw-row", { width: "100%", display: "block"}],
|
|
2794
|
+
[".bw-row [class^=\"bw-col\"]", { float: "left"}],
|
|
2795
|
+
[".bw-row::after", { content: "\"\"", display: "table", clear:"both"}],
|
|
2796
|
+
[".bw-box-1", {"padding-top":"10px","padding-bottom": "10px", "border-radius": "8px"}],
|
|
2797
|
+
"\n",
|
|
2798
|
+
[".bw-sign", {"position": "inherit", "display": "table", "height": "100%", "width": "100%"}],
|
|
2799
|
+
[".bw-sign > div", {display:"table-cell", "vertical-align" : "middle"}],
|
|
2800
|
+
[".bw-sign > div > div", {"text-align":"center"}],
|
|
2801
|
+
"\n",
|
|
2802
|
+
//misc element controls
|
|
2803
|
+
[".bw-hide", { display: "none"}],
|
|
2804
|
+
[".bw-show", { display: "block"}]
|
|
2805
|
+
];
|
|
2806
|
+
|
|
2807
|
+
//heading generator
|
|
2808
|
+
[1,2,3,4,5,6].map(function(x){d.push([".bw-h"+x, {"font-size":_r(3.2*Math.pow(.85,x+1))+"rem"}]);});
|
|
2809
|
+
|
|
2810
|
+
d.push("\n");
|
|
2811
|
+
// grid system (generated)
|
|
2812
|
+
for (var k=1; k<=12; k++)
|
|
2813
|
+
d.push([".bw-col-"+k, {width:(_r(k*100/12)+"%")}]);
|
|
2814
|
+
|
|
2815
|
+
d.push("\n");
|
|
2816
|
+
// generate CSS from above rules
|
|
2817
|
+
s+= d.map(function(x){return rl(x,{pretty:dopts.pretty});}).join("\n")+"\n";
|
|
2818
|
+
|
|
2819
|
+
d.push("\n");
|
|
2820
|
+
//primtive in-built color themeing see opts to overide
|
|
2821
|
+
for (i in dopts["colorset"]){
|
|
2822
|
+
s+= ".bw-color-"+i+" {"+i+":" +dopts["colorset"][i]+"}\n";
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
d.push("\n");
|
|
2826
|
+
bw.makeCSS( dopts["themes"]);
|
|
2827
|
+
for (i=0; i< dopts["themes"].length; i++) {
|
|
2828
|
+
s+= rl( dopts["themes"][i]);
|
|
2829
|
+
//s+= bw.makeCSS( dopts["themes"][i])
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
//responsive screen
|
|
2833
|
+
var m = "@media only screen and (min-width: ";
|
|
2834
|
+
s+= m + "540px) {.bw-def-page-setup {width: 96%;}}\n";
|
|
2835
|
+
s+= m + "720px) {.bw-def-page-setup {width: 92%;}}\n";
|
|
2836
|
+
s+= m + "960px) {.bw-def-page-setup {width: 88%;}}\n";
|
|
2837
|
+
s+= m + "1100px){.bw-def-page-setup {width: 86%;}}\n";
|
|
2838
|
+
s+= m + "1600px){.bw-def-page-setup {width: 84%;}}\n";
|
|
2839
|
+
|
|
2840
|
+
|
|
2841
|
+
if (bw.isNodeJS() == false) {
|
|
2842
|
+
var h = bw.DOM("head")[0];
|
|
2843
|
+
var el = document.createElement("style");
|
|
2844
|
+
el.id = dopts["id"];
|
|
2845
|
+
el.textContent = s;
|
|
2846
|
+
|
|
2847
|
+
if (appendToHead && (document.getElementById(dopts["id"]) == null)) // only append once
|
|
2848
|
+
h.appendChild(el);
|
|
2849
|
+
}
|
|
2850
|
+
if (dopts["exportCSS"])
|
|
2851
|
+
s = bw.html(["style",{"id":dopts["id"]},"\n/**\n bitwrench basic css styles\n version: "+bw.version()["version"]+"\n */"+s]);
|
|
2852
|
+
return s;
|
|
2853
|
+
};
|
|
2854
|
+
|
|
2855
|
+
|
|
2856
|
+
|
|
2857
|
+
//================================================================================
|
|
2858
|
+
|
|
2859
|
+
bw.CSSSimpleThemes = function (d,appendToHead) {
|
|
2860
|
+
/**
|
|
2861
|
+
bw.bwSimpleThemes() selects simple (we do mean simple) HTML themes for some basic elements.
|
|
2862
|
+
if d is an number it selects the built-in theme by index (see docs) else if d is a dictionary the elements
|
|
2863
|
+
in d will be converted to a CSS style.
|
|
2864
|
+
|
|
2865
|
+
output is a CSS style. if appendToHead is true or omitted then the theme is appended to the head element.
|
|
2866
|
+
*/
|
|
2867
|
+
var s ="",xs={};
|
|
2868
|
+
/*
|
|
2869
|
+
var thm = {
|
|
2870
|
+
defBkgCol : "#333",
|
|
2871
|
+
defCol : "#ddd",
|
|
2872
|
+
col1 : "#555",
|
|
2873
|
+
col2 : "#f0f0f0",
|
|
2874
|
+
brd : "#ddd"
|
|
2875
|
+
};
|
|
2876
|
+
|
|
2877
|
+
var thmCSS =
|
|
2878
|
+
[
|
|
2879
|
+
["*" , {"background-color": thm.defBkgCol, "color":thm.defCol, "font-family": "sans-serif", "box-sizing":"border-box"}],
|
|
2880
|
+
["body" , {"margin-top":"1%"}],
|
|
2881
|
+
["th" , {"background-color":thm.col1}],
|
|
2882
|
+
["tbody tr:nth-child(even)" , {"background-color": thm.col2}],
|
|
2883
|
+
[["table", "td", "th"] , {"border-collapse":"collapse", "border":"1px solid "+thm.brd}],
|
|
2884
|
+
[["td","th"] , {"padding":"4px"}],
|
|
2885
|
+
[["div","body","button","table","input"] , {"border-radius": "2px"}]
|
|
2886
|
+
// ["div", {"padding-left":"2%", "padding-right":"2%","padding-top":"1%","padding-bottom":"1%"}]
|
|
2887
|
+
];
|
|
2888
|
+
*/
|
|
2889
|
+
var def = [ // default styles
|
|
2890
|
+
{
|
|
2891
|
+
|
|
2892
|
+
css:
|
|
2893
|
+
[
|
|
2894
|
+
["*" , {"background-color": "#333", "color":"#ddd", "font-family": "sans-serif", "box-sizing":"border-box"}],
|
|
2895
|
+
["body" , {"margin-top":"1%"}],
|
|
2896
|
+
["th" , {"background-color":"#555"}],
|
|
2897
|
+
["tbody tr:nth-child(even)" , {"background-color": "#f0f0f0"}],
|
|
2898
|
+
[["table", "td", "th"] , {"border-collapse":"collapse", "border":"1px solid #ddd"}],
|
|
2899
|
+
[["td","th"] , {"padding":"4px"}],
|
|
2900
|
+
[["div","body","button","table","input"] , {"border-radius": "2px"}]
|
|
2901
|
+
// ["div", {"padding-left":"2%", "padding-right":"2%","padding-top":"1%","padding-bottom":"1%"}]
|
|
2902
|
+
],
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
css:
|
|
2906
|
+
[
|
|
2907
|
+
["*" , {"background-color": "#f8f8f8", "color":"#111", "font-family": "sans-serif", "box-sizing":"border-box"}],
|
|
2908
|
+
["body" , {"margin-top":"1%"}],
|
|
2909
|
+
["th" , {"background-color":"#ddd"}],
|
|
2910
|
+
["tbody tr:nth-child(even)" , {"background-color":"#ddd"}],
|
|
2911
|
+
[["table", "td", "th"] , {"border-collapse":"collapse", "border":"1px solid #111"}],
|
|
2912
|
+
[["td","th"] , {"padding":"4px"}],
|
|
2913
|
+
[["div","body","button","table","input"] , {"border-radius": "2px"}]
|
|
2914
|
+
// ["div", {"padding-left":"2%", "padding-right":"2%","padding-top":"1%","padding-bottom":"1%"}]
|
|
2915
|
+
],
|
|
2916
|
+
}
|
|
2917
|
+
];
|
|
2918
|
+
xs = bw.choice(_to(d),{
|
|
2919
|
+
"object" : d,
|
|
2920
|
+
// "string" : function(){}
|
|
2921
|
+
"number" : ((d>=0) && (d<def.length))?def[d].css:def[0].css
|
|
2922
|
+
},def[0].css);
|
|
2923
|
+
|
|
2924
|
+
s= xs.map(function(y){return bw.makeCSSRule(y,{pretty:false});}).join("\n");
|
|
2925
|
+
if (appendToHead != false) {
|
|
2926
|
+
//var hs = document.getElementById("bw-simple-theme-styles");
|
|
2927
|
+
var hs = bw.DOM("bw-simple-theme-styles");
|
|
2928
|
+
if (hs.length == 0) {// first time
|
|
2929
|
+
//var h = document.getElementsByTagName("head")[0];
|
|
2930
|
+
var h = bw.DOM("head")[0];
|
|
2931
|
+
var el = document.createElement("style");
|
|
2932
|
+
el.id = "bw-simple-theme-styles";
|
|
2933
|
+
el.textContent = s; //note IE8 requires .text=
|
|
2934
|
+
h.appendChild(el);
|
|
2935
|
+
}
|
|
2936
|
+
else { // replace it
|
|
2937
|
+
hs.textContent = s; //note IE8 requires .text=
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
return s;
|
|
2943
|
+
};
|
|
2944
|
+
|
|
2945
|
+
// =============================================================================================
|
|
2946
|
+
bw.selectTabContent = function (item, target) {
|
|
2947
|
+
/**
|
|
2948
|
+
This function is used inside a tab block to show the appropriate content. Note that this is
|
|
2949
|
+
designed to work even if code is emitted as document.getElementById("myTabs").innerHTML = <<generated code..>>
|
|
2950
|
+
or statically written by the programmer.
|
|
2951
|
+
|
|
2952
|
+
note that DOM IDs are not required as selectTabContent() uses DOM path relative logic internally
|
|
2953
|
+
|
|
2954
|
+
<div class="bw-tab-container"> <!-- bw-tab-container -- bw-tab-items (array of items), bw-tab-content (array of content to show) -->
|
|
2955
|
+
<ul class="bw-tab-item-list"> <!-- container for the tabs -->
|
|
2956
|
+
<li class="bw-tab userTab bw-tab-active" onclick="bw.selectTabContent(this)" >Tab 1</li>
|
|
2957
|
+
<li class="bw-tab userTab " onclick="bw.selectTabContent(this)" >Tab 2</li>
|
|
2958
|
+
<li class="bw-tab userTab " onclick="bw.selectTabContent(this)" >Tab 3</li>
|
|
2959
|
+
<li class="bw-tab userTab " onclick="bw.selectTabContent(this)" >Tab 4</li>
|
|
2960
|
+
</ul>
|
|
2961
|
+
<div class="bw-tab-content-list"> <!-- container for the tab content -->
|
|
2962
|
+
<div class="bw-tab-content bw-show" >coontent area 1 </div> <!-- bw-show picks which tab to make active at first -->
|
|
2963
|
+
<div class="bw-tab-content" >content area 2</div>
|
|
2964
|
+
<div class="bw-tab-content" >content area 3</div>
|
|
2965
|
+
<div class="bw-tab-content" >content area 4</div>
|
|
2966
|
+
</div> <!-- end of tab content sect -->
|
|
2967
|
+
</div>
|
|
2968
|
+
*/
|
|
2969
|
+
//if (_to(item)=="string")
|
|
2970
|
+
// item = document.getElementById(item);
|
|
2971
|
+
item = bw.DOM(item)[0];
|
|
2972
|
+
|
|
2973
|
+
if (_to(item).substr(0,4) != "html")
|
|
2974
|
+
return false; //unable to set tab content
|
|
2975
|
+
document.gx=item;
|
|
2976
|
+
var i,j,index=0;
|
|
2977
|
+
var cols = item.parentNode.getElementsByTagName("li");
|
|
2978
|
+
//update which tab selected
|
|
2979
|
+
for (i=0; i< cols.length; i++) {
|
|
2980
|
+
if (cols[i] == item) { // selected tab logic
|
|
2981
|
+
index = i;
|
|
2982
|
+
cols[i].className = bw.classStrAddDel(cols[i].className,"bw-tab-active");
|
|
2983
|
+
}
|
|
2984
|
+
else { // unselected tab logic
|
|
2985
|
+
cols[i].className = bw.classStrAddDel(cols[i].className,"","bw-tab-active");
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
//console.log(item);
|
|
2989
|
+
var tcols=[];// = item.parentNode.parentNode.getElementsByClassName("bw-tab-content-list")[0].getElementsByClassName("bw-tab-content");
|
|
2990
|
+
|
|
2991
|
+
|
|
2992
|
+
for (i=0; i<item.parentNode.parentNode.children.length; i++) {
|
|
2993
|
+
if (item.parentNode.parentNode.children[i].className.trim().split(/\s+/).indexOf("bw-tab-content-list")>=0) {
|
|
2994
|
+
//were in the right child...
|
|
2995
|
+
for (j=0; j<item.parentNode.parentNode.children[i].children.length;j++) {
|
|
2996
|
+
if (item.parentNode.parentNode.children[i].children[j].className.trim().split(/\s+/).indexOf("bw-tab-content") >=0)
|
|
2997
|
+
tcols.push(item.parentNode.parentNode.children[i].children[j]);
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
if (tcols.length <= 0)
|
|
3004
|
+
return false;
|
|
3005
|
+
|
|
3006
|
+
target = (_to(target) == "undefined") ? tcols[index] : target; //we will infer it by the tab index
|
|
3007
|
+
target = (_to(target) == "string" ) ? bw.DOM(target)[0] : target; // we hav an ID so we'll use that
|
|
3008
|
+
|
|
3009
|
+
for (i=0; i < tcols.length; i++) {
|
|
3010
|
+
if (tcols[i] == target)
|
|
3011
|
+
tcols[i].className = bw.classStrAddDel(tcols[i].className,"bw-show");
|
|
3012
|
+
//bw.DOMClass(tcols[i],"bw-show","bw-show"); //tcols[i].style.display = "block";
|
|
3013
|
+
else
|
|
3014
|
+
tcols[i].className = bw.classStrAddDel(tcols[i].className,"","bw-show");
|
|
3015
|
+
//bw.DOMClass(tcols[i],"bw-show","");//tcols[i].style.display = "none";
|
|
3016
|
+
|
|
3017
|
+
}
|
|
3018
|
+
return true;
|
|
3019
|
+
};
|
|
3020
|
+
|
|
3021
|
+
// =============================================================================================
|
|
3022
|
+
|
|
3023
|
+
bw.DOMClass = function(el, key, replace) {
|
|
3024
|
+
/**
|
|
3025
|
+
bw.DOMClass(el,value)
|
|
3026
|
+
|
|
3027
|
+
returns whether a specific DOM element class name (key) is set on atleast one the supplied element(s).
|
|
3028
|
+
|
|
3029
|
+
If replace is supplied then the class name (key) is replaced or added if it doesn't exist.
|
|
3030
|
+
note that if key is not found but a replace is supplied the return-value is still false as the supplied key was not found
|
|
3031
|
+
|
|
3032
|
+
el must be valid element or CSS selector
|
|
3033
|
+
|
|
3034
|
+
markElement is used by bw UI toggles
|
|
3035
|
+
*/
|
|
3036
|
+
var r = false, elems, x,j;
|
|
3037
|
+
elems = bw.DOM(el);
|
|
3038
|
+
if (elems.length <=0 )
|
|
3039
|
+
return r;
|
|
3040
|
+
|
|
3041
|
+
for (j=0; j< elems.length; j++) {
|
|
3042
|
+
x = elems[j];
|
|
3043
|
+
try {
|
|
3044
|
+
|
|
3045
|
+
var c = x.className.split(/[ ]+/);
|
|
3046
|
+
var i = c.indexOf(key);
|
|
3047
|
+
|
|
3048
|
+
if (i >= 0) // found key
|
|
3049
|
+
r = true;
|
|
3050
|
+
|
|
3051
|
+
|
|
3052
|
+
if ((_to(replace) == "string") && (c.indexOf(replace)== -1)){
|
|
3053
|
+
if (i == -1) //key not found
|
|
3054
|
+
c.push(replace);
|
|
3055
|
+
else {
|
|
3056
|
+
if (replace.length > 0)
|
|
3057
|
+
c[i]=replace;
|
|
3058
|
+
else
|
|
3059
|
+
c.splice(i,1);
|
|
3060
|
+
}
|
|
3061
|
+
x.className = c.join(" ").trim();
|
|
3062
|
+
r = true;
|
|
3063
|
+
// element.className = element.className.replace(/\bmystyle\b/g, "");
|
|
3064
|
+
}
|
|
3065
|
+
/*
|
|
3066
|
+
var c = x.className;
|
|
3067
|
+
x.className = bw.classStrAddDel(c,key,replace);
|
|
3068
|
+
r=true;
|
|
3069
|
+
*/
|
|
3070
|
+
}
|
|
3071
|
+
catch(e) { bw.log(e);}
|
|
3072
|
+
}
|
|
3073
|
+
return r;
|
|
3074
|
+
};
|
|
3075
|
+
|
|
3076
|
+
// =============================================================================================
|
|
3077
|
+
bw.DOMClassToggle = function(el,className) {
|
|
3078
|
+
/**
|
|
3079
|
+
bw.DOMClassToggle(el,classname)
|
|
3080
|
+
for each element specified in el (eg "#id", ".myClass", "h2", <DOM OBJECT>) toggle className.
|
|
3081
|
+
|
|
3082
|
+
If className is present on the object then it is removed. if it is not present it is added.
|
|
3083
|
+
|
|
3084
|
+
|
|
3085
|
+
classNames with spaces or tabs are not valid and result in undefined behavior.
|
|
3086
|
+
|
|
3087
|
+
returns last element current toggle state.
|
|
3088
|
+
*/
|
|
3089
|
+
var x,i,elems = bw.DOM(el), r=false;
|
|
3090
|
+
for (i=0; i< elems.length; i++) {
|
|
3091
|
+
x=elems[i];
|
|
3092
|
+
try {
|
|
3093
|
+
r = bw.DOMClass(x,className);
|
|
3094
|
+
if (r)
|
|
3095
|
+
bw.DOMClass(x,className,"");
|
|
3096
|
+
else
|
|
3097
|
+
bw.DOMClass(x,className,className);
|
|
3098
|
+
|
|
3099
|
+
} catch(e) { bw.log(e); }
|
|
3100
|
+
}
|
|
3101
|
+
return !r;
|
|
3102
|
+
};
|
|
3103
|
+
// =============================================================================================
|
|
3104
|
+
bw.version = function() {
|
|
3105
|
+
/**
|
|
3106
|
+
@method version() - bitwrench runtime version & license info.
|
|
3107
|
+
|
|
3108
|
+
*/
|
|
3109
|
+
var v = {
|
|
3110
|
+
"version" : "1.2.14",
|
|
3111
|
+
"about" : "bitwrench is a simple library of miscellaneous Javascript helper functions for common web design tasks.",
|
|
3112
|
+
"copy" : "(c) M A Chatterjee deftio (at) deftio (dot) com",
|
|
3113
|
+
"url" : "http://github.com/deftio/bitwrench",
|
|
3114
|
+
"license" : "BSD-2-Clause"
|
|
3115
|
+
};
|
|
3116
|
+
return v;
|
|
3117
|
+
};
|
|
3118
|
+
|
|
3119
|
+
// ==============================================================================================
|
|
3120
|
+
/**
|
|
3121
|
+
command line handling
|
|
3122
|
+
|
|
3123
|
+
this can be done via URL e.g. myPage.com?bw-load-styles=true
|
|
3124
|
+
|
|
3125
|
+
or via script tag
|
|
3126
|
+
<script type="text/javascript" src="./path/to/bitwrenchjs" bwargs="bw-load-styles=true"></script>
|
|
3127
|
+
|
|
3128
|
+
*/
|
|
3129
|
+
bw.bwargs = {enableUJURLArgs : "true"}; // the arguments are exported so one can see them as a simple dict
|
|
3130
|
+
|
|
3131
|
+
var parseArgs = function(s) {
|
|
3132
|
+
var args = {};
|
|
3133
|
+
if ((typeof s == "string") && (s!= "")) {
|
|
3134
|
+
s=s.split(";");
|
|
3135
|
+
var j;
|
|
3136
|
+
for (j in s) {
|
|
3137
|
+
var k = s[j].split(":");
|
|
3138
|
+
args[k[0]]=k[1];
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
return args;
|
|
3142
|
+
};
|
|
3143
|
+
|
|
3144
|
+
var getArgs = function () {
|
|
3145
|
+
if(bw.isNodeJS()==false) { // in browser
|
|
3146
|
+
//var els = document.getElementsByTagName("script"); // array of script elements
|
|
3147
|
+
var els = bw.DOM("script");
|
|
3148
|
+
var i,a,b;
|
|
3149
|
+
for (i in els) {
|
|
3150
|
+
try {
|
|
3151
|
+
// bw.log(_args[i]);
|
|
3152
|
+
var el = els[i]; //
|
|
3153
|
+
if (el.hasOwnProperty("src") != false)
|
|
3154
|
+
break;
|
|
3155
|
+
|
|
3156
|
+
var s = String(el.getAttribute("src"));
|
|
3157
|
+
var f = "bitwrench.js";
|
|
3158
|
+
|
|
3159
|
+
if (s.toLocaleLowerCase().substring(s.length-f.length,s.length) == f.toLocaleLowerCase()) {
|
|
3160
|
+
s = _to(s) == "string" ? el.getAttribute("bwargs") : [""];
|
|
3161
|
+
s = _to(s) == "string" ? el.getAttribute("data-bwargs") : s; //the html4/5 standard way
|
|
3162
|
+
a = parseArgs(s);
|
|
3163
|
+
for (b in a)
|
|
3164
|
+
bw.bwargs[b]=a[b];
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
} catch (e) {
|
|
3168
|
+
//bw.log(String(["err 1418",i,e]));
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
//pick up from URL
|
|
3173
|
+
if(bw.bwargs["enableUJURLArgs"] == "true") { //note string literal "true"
|
|
3174
|
+
//note in the script tag you can disable ?bwload=foo:bar; params with this
|
|
3175
|
+
//<script type="text/javascript" src="./path/to/bitwrench.js" bwargs="enableURLConfig:false"></script>
|
|
3176
|
+
a = parseArgs(bw.getURLParam("bwargs",""));
|
|
3177
|
+
for (b in a)
|
|
3178
|
+
bw.bwargs[b]=a[b];
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
};
|
|
3182
|
+
|
|
3183
|
+
|
|
3184
|
+
// ==============================================================================================
|
|
3185
|
+
// ==============================================================================================
|
|
3186
|
+
// ==============================================================================================
|
|
3187
|
+
//internally used function declarations:
|
|
3188
|
+
|
|
3189
|
+
|
|
3190
|
+
getArgs();
|
|
3191
|
+
|
|
3192
|
+
// do command line stuff
|
|
3193
|
+
var loadStyles = bw.bwargs["bw-load-styles"]!="false";
|
|
3194
|
+
var loadStyleBasics = bw.typeAssign(bw.bwargs["bw-load-style-basics"],"string", bw.bwargs["bw-load-style-basics"], "load");
|
|
3195
|
+
bw.CSSSimpleStyles(loadStyles,{"globals":loadStyleBasics}); // append to head the bitwrench css styles by default
|
|
3196
|
+
|
|
3197
|
+
bw.funcRegister(bw.log,"bw_log"); // this is globally registered for debugging purposes, it will never get called though unless programmer does this explicitly.
|
|
3198
|
+
|
|
3199
|
+
return bw;
|
|
3200
|
+
|
|
3201
|
+
})();
|
|
3202
|
+
export default bw;
|