bitwrench 1.2.16 → 2.0.7

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.
Files changed (130) hide show
  1. package/README.md +160 -158
  2. package/bin/bitwrench.js +3 -0
  3. package/dist/bitwrench-code-edit.cjs.js +639 -0
  4. package/dist/bitwrench-code-edit.es5.js +875 -0
  5. package/dist/bitwrench-code-edit.es5.min.js +15 -0
  6. package/dist/bitwrench-code-edit.esm.js +628 -0
  7. package/dist/bitwrench-code-edit.esm.min.js +15 -0
  8. package/dist/bitwrench-code-edit.umd.js +645 -0
  9. package/dist/bitwrench-code-edit.umd.min.js +15 -0
  10. package/dist/bitwrench.cjs.js +6983 -0
  11. package/dist/bitwrench.cjs.min.js +62 -0
  12. package/dist/bitwrench.css +5100 -0
  13. package/dist/bitwrench.es5.js +8446 -0
  14. package/dist/bitwrench.es5.min.js +31 -0
  15. package/dist/bitwrench.esm.js +6981 -0
  16. package/dist/bitwrench.esm.min.js +62 -0
  17. package/dist/bitwrench.umd.js +6989 -0
  18. package/dist/bitwrench.umd.min.js +62 -0
  19. package/dist/builds.json +127 -0
  20. package/dist/sri.json +18 -0
  21. package/package.json +86 -24
  22. package/readme.html +288 -0
  23. package/src/bitwrench-code-edit.js +627 -0
  24. package/src/bitwrench-color-utils.js +311 -0
  25. package/src/bitwrench-component-base.js +736 -0
  26. package/src/bitwrench-components-inline.js +374 -0
  27. package/src/bitwrench-components-v2.js +1879 -0
  28. package/src/bitwrench-components.js +610 -0
  29. package/src/bitwrench-styles.js +3240 -0
  30. package/src/bitwrench.js +3367 -0
  31. package/src/cli/convert.js +205 -0
  32. package/src/cli/index.js +122 -0
  33. package/src/cli/inject.js +55 -0
  34. package/src/cli/layout-default.js +142 -0
  35. package/src/generate-css.js +381 -0
  36. package/src/vendor/quikdown.js +654 -0
  37. package/src/version.js +16 -0
  38. package/.eslintrc.json +0 -27
  39. package/.github/workflows/codeql-analysis.yml +0 -72
  40. package/.travis.yml +0 -34
  41. package/bitwrench.css +0 -92
  42. package/bitwrench.js +0 -3348
  43. package/bitwrench.js_sri.txt +0 -1
  44. package/bitwrench.min.js +0 -1
  45. package/bitwrench.min.js_sri.txt +0 -1
  46. package/bitwrench_ESM.js +0 -3207
  47. package/bitwrench_ESM.js_sri.txt +0 -1
  48. package/bitwrench_ESM.min.js +0 -1
  49. package/bitwrench_ESM.min.js_sri.txt +0 -1
  50. package/dev/bitwrench-todo.md +0 -215
  51. package/dev/css-arrows.md +0 -23
  52. package/dev/docStringDev.js +0 -124
  53. package/dev/docStringParseDev.js +0 -171
  54. package/dev/example11-load-mjs-page.html +0 -17
  55. package/dev/figures.html +0 -37
  56. package/dev/html_gen.js +0 -349
  57. package/dev/htmld.md +0 -250
  58. package/dev/htmldev.html +0 -45
  59. package/dev/index-old.html +0 -87
  60. package/dev/misc-notes.md +0 -21
  61. package/dev/norm.css +0 -30
  62. package/dev/notes.md +0 -2
  63. package/dev/pageData.mjs +0 -69
  64. package/dev/sizes.html +0 -49
  65. package/dev/universal-js-module.js +0 -37
  66. package/examples/example1.html +0 -78
  67. package/examples/example10.html +0 -84
  68. package/examples/example11.html +0 -17
  69. package/examples/example12.html +0 -18
  70. package/examples/example2.html +0 -44
  71. package/examples/example3.html +0 -50
  72. package/examples/example4.html +0 -22
  73. package/examples/example5.html +0 -82
  74. package/examples/example6.html +0 -128
  75. package/examples/example7.html +0 -91
  76. package/examples/example8.html +0 -27
  77. package/examples/example9.html +0 -102
  78. package/examples/examplePageData12.mjs +0 -73
  79. package/examples/pageData.mjs +0 -69
  80. package/examples/pico.min.css +0 -5
  81. package/icon/bitwrench-dark-tall.png +0 -0
  82. package/icon/bitwrench-dark.png +0 -0
  83. package/icon/bitwrench-icon-lt-grey.png +0 -0
  84. package/icon/bitwrench-icon.vsd +0 -0
  85. package/icon/bitwrench-logo-dark.png +0 -0
  86. package/icon/bitwrench-logo-full.png +0 -0
  87. package/icon/bitwrench-logo-green.png +0 -0
  88. package/icon/bitwrench-logo-grey.png +0 -0
  89. package/icon/bitwrench-logo-white.png +0 -0
  90. package/icon/bitwrench-logos-colors.png +0 -0
  91. package/icon/bitwrench-thick-logo.png +0 -0
  92. package/icon/bitwrench-thick-teal/android-chrome-192x192.png +0 -0
  93. package/icon/bitwrench-thick-teal/android-chrome-512x512.png +0 -0
  94. package/icon/bitwrench-thick-teal/apple-touch-icon.png +0 -0
  95. package/icon/bitwrench-thick-teal/browserconfig.xml +0 -9
  96. package/icon/bitwrench-thick-teal/favicon-16x16.png +0 -0
  97. package/icon/bitwrench-thick-teal/favicon-32x32.png +0 -0
  98. package/icon/bitwrench-thick-teal/favicon.ico +0 -0
  99. package/icon/bitwrench-thick-teal/mstile-144x144.png +0 -0
  100. package/icon/bitwrench-thick-teal/mstile-150x150.png +0 -0
  101. package/icon/bitwrench-thick-teal/mstile-310x150.png +0 -0
  102. package/icon/bitwrench-thick-teal/mstile-310x310.png +0 -0
  103. package/icon/bitwrench-thick-teal/mstile-70x70.png +0 -0
  104. package/icon/bitwrench-thick-teal/site.webmanifest +0 -19
  105. package/icon/bitwrench-thick-teal.ico +0 -0
  106. package/icon/bitwrench-thick-teal.svg +0 -44
  107. package/icon/bitwrench-thick-teal.zip +0 -0
  108. package/icon/favicon-test.html +0 -20
  109. package/icon/logos-test.PNG +0 -0
  110. package/images/bitwrench-512x512.png +0 -0
  111. package/images/bitwrench-logo-med.png +0 -0
  112. package/images/bitwrench-thick-logo.png +0 -0
  113. package/images/bitwrench-thick-logo.svg +0 -64
  114. package/images/bitwrench-thick-teal.ico +0 -0
  115. package/images/favicon.ico +0 -0
  116. package/index.html +0 -282
  117. package/instr_tmp/bitwrench.js +0 -1350
  118. package/karma.conf.js +0 -140
  119. package/makefile +0 -21
  120. package/quick-docs.html +0 -206
  121. package/test/bitwrench_test.js +0 -1255
  122. package/test/karma-test.js +0 -1081
  123. package/tools/bw_deprecatedNames.js +0 -19
  124. package/tools/bwconsole.js +0 -20
  125. package/tools/createSimpleHTMLPage.js +0 -41
  126. package/tools/emitreadme.sh +0 -4
  127. package/tools/export-bw-default-css.js +0 -41
  128. package/tools/umd2ModuleHack.js +0 -32
  129. package/tools/update-bw-package.js +0 -36
  130. package/tools/updatereadme.js +0 -34
package/bitwrench_ESM.js DELETED
@@ -1,3207 +0,0 @@
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 = {"<":"&lt;", ">":"&gt;", "&":"&amp;", "\"":"&quot;", "'":"&#039;","#":"&#035;","\\\\":"","\n":"<br>","\t":"&nbsp;&nbsp;&nbsp;&nbsp;"};
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); //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(), null means no outer tag will be emitted.
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
- if (n.node.t != "")
1356
- h.push("<",n.node.t, _atr(n.node)); // add tag
1357
-
1358
-
1359
- var tagClose = dopts.tagClose != "inherit" ? dopts.tagClose : n.node.o.tagClose;
1360
- switch(tagClose) {
1361
- /*eslint no-fallthrough: [0, { "commentPattern": "break[\\s\\w]*omitted" }]*/
1362
- case "auto":
1363
- if (n.isVoidTag){
1364
- // <tag a{} /> # content is not rendered for void tags # ["<",n.t , a{}, "/>" ]
1365
- h.push( "/>");
1366
- break;
1367
- }
1368
-
1369
- case "all":
1370
- default:
1371
- //<tag a{}> .... </tag> # h=["<",n.t , a{}, crend() , "</", n.t, ">"]
1372
- if (n.node.t != "")
1373
- h.push(">");
1374
- var i,x;
1375
- if (bw.typeOf (n.node.c) != "array") {
1376
- state.levelCnt++;
1377
- state.levelMax = state.levelCnt > state.levelMax ? state.levelCnt : state.levelMax;
1378
- x = bw.htmlEmit(n.node.c,dopts,state);
1379
- state.levelCnt--;
1380
- h.push(x.html);
1381
- }
1382
- else {
1383
- for (i in n.node.c) {
1384
- state.levelCnt++;
1385
- state.levelMax = state.levelCnt > state.levelMax ? state.levelCnt : state.levelMax;
1386
- x= bw.htmlEmit(n.node.c[i],dopts,state);
1387
- state.levelCnt--;
1388
- h.push(x.html);
1389
- }
1390
- }
1391
- if ( tagClose != "none" ) {
1392
- if (n.node.t != "")
1393
- h.push( "</",n.node.t,">");
1394
- }
1395
- }
1396
-
1397
- }
1398
- var html = h.join("");
1399
- return {html: html, state: state};
1400
- };
1401
- // ===================================================================================
1402
- bw.html = function(data,options) {
1403
- return bw.htmlEmit(data,options).html;
1404
- };
1405
- // ===================================================================================
1406
- bw.htmla = function (listData,options) {
1407
- /**
1408
- bw.htmla(listData,options)
1409
-
1410
- listData is a single dim array of bw.html() compatible cnostructs
1411
-
1412
- */
1413
- if (_to(listData) != "array")
1414
- return bw.html(listData,options);
1415
-
1416
- return listData.map( function(x) {return bw.html(x,options);}).join("");
1417
- };
1418
-
1419
- // ===================================================================================
1420
- bw.htmlList = function (listData, listType, atr, atri) {
1421
- /**
1422
- bw.makeHTMLList (listData, listType, attribute{}, attribute_for_each_items {})
1423
-
1424
- listType = "ul" | "ol"
1425
- listData = [ item1, item2, item3, .. ]
1426
-
1427
- */
1428
- if (_to(listData) != "array")
1429
- return "";
1430
-
1431
- if (listData.length < 1)
1432
- return "";
1433
-
1434
- atr = _toa(atr,"object",atr,{});
1435
- atri = _toa(atr,"object",atr,{});
1436
-
1437
- var lc = listData.map(function(x){return bw.html(["li",atri,x]);});
1438
- listType = ["ul","ol"].indexOf(listType)== -1 ? "ol" : listType;
1439
- return bw.html ({t:listType,a:atr,c:lc});
1440
- };
1441
-
1442
- // ===================================================================================
1443
- bw.openFullScreen = function () {
1444
- /**
1445
- bw.openFullScreen() attempt to open the document full screen (usefull for signs, banners)
1446
- */
1447
- var elem = document.documentElement;
1448
- if (elem.requestFullscreen) {
1449
- elem.requestFullscreen();
1450
- } else if (elem.mozRequestFullScreen) { /* Firefox */
1451
- elem.mozRequestFullScreen();
1452
- } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
1453
- elem.webkitRequestFullscreen();
1454
- } else if (elem.msRequestFullscreen) { /* IE/Edge */
1455
- elem.msRequestFullscreen();
1456
- }
1457
- };
1458
-
1459
- // ===================================================================================
1460
- bw.classStrAddDel = function (classData,classesToAdd,classesToDel) {
1461
- /**
1462
- classStrAddDel (classData, classesToAdd, classesToDel)
1463
- for CSS classes
1464
-
1465
- takes a valid classData string e.g. "myclass1 myclass2" etc
1466
-
1467
- and adds/del classes from classesToAdd string if they are not already present in classData
1468
-
1469
- classStrAddDel("class1 class2", "class3") ==> "class1 class2 class3"
1470
- classStrAddDel("class1 class2", "class3 class4") ==> "class1 class2 class3 class4"
1471
- classStrAddDel("class1 class2", "class2 class3") ==> "class1 class2 class3" // doesn't add class2 again
1472
-
1473
- classStrAddDel("class1 class2", "class 2 class3",class1) ==> "class2 class3" // doesn't add class2 again. removes class1
1474
- classStrAddDel("class1 class2", "",class1) ==> "class2" // removes class1
1475
-
1476
- classData, classesToAdd, classesToDel may be strings (space delimited) or arrays of strings (["c1", "c2"], ["c3", "c4"], ["c1"])
1477
- */
1478
-
1479
- var tnorm = function(x){x=bw.toa(x,"undefined",[],x); return (bw.to(x)=="array")? x : x.toString().trim().split(/\s+/ig);};
1480
- var c = tnorm(classData);
1481
- var ca = tnorm(classesToAdd);
1482
- var cd = tnorm(classesToDel);
1483
- return bw.arrayBNotInA(cd,c.concat(ca)).join(" ").trim().replace(/\s+/ig," ");
1484
-
1485
- };
1486
- // ===================================================================================
1487
- bw.classStrToggle = function (classData, classesToToggle) {
1488
- /**
1489
- classStrToggle (classData, classesToToggle)
1490
-
1491
- toggles classes listed in classesToToggle
1492
-
1493
- takes a valid classData string e.g. "myclass1 myclass2" etc
1494
- */
1495
- var tnorma = function(x){x=bw.toa(x,"undefined",[],x); return (bw.to(x)=="array")? x : x.toString().trim().split(/\s+/ig);};
1496
- var c = tnorma(classData);
1497
- var t = tnorma(classesToToggle);
1498
- return bw.classStrAddDel(classData,bw.arrayBNotInA(c,t),bw.arrayBinA(c,t));
1499
- };
1500
-
1501
- // ===================================================================================
1502
- bw.htmlTabs = function(tabData, opts) {
1503
- /**
1504
- bw.makeHTMLTabs(tabData, atr)
1505
- tabData = [[tab1Title,tab1-content], [tab2Title,tab2-content], [tab3Title,tab3-content]]
1506
- */
1507
- if (_to(tabData) != "array")
1508
- return "";
1509
- if (tabData.length < 1)
1510
- return "";
1511
-
1512
- var dopts = {
1513
- atr : {"class":""}, //container {}
1514
- tab_atr : {"class":""}, //attributs for each tab container
1515
- tabc_atr: {"class":""}, //attributes for each tab-content area container
1516
- indent : "", //indent string for pretty printing
1517
- pretty : false
1518
- };
1519
- dopts = optsCopy(dopts,opts);
1520
-
1521
- var ti = tabData.map(function(x){return ["li",{"class":"bw-tab-item", "onclick":"bw.selectTabContent(this)"},x[0]];});
1522
- var tc = tabData.map(function(x){return ["div",{"class":"bw-tab-content"},x[1]];});
1523
-
1524
- ti[0][1]["class"] = bw.classStrAddDel(ti[0][1]["class"], "bw-tab-active");
1525
- tc[0][1]["class"] = bw.classStrAddDel(tc[0][1]["class"], "bw-show");
1526
-
1527
- dopts["atr" ]["class"] = bw.classStrAddDel (dopts["atr" ]["class"],"bw-tab-container");
1528
- dopts["tab_atr" ]["class"] = bw.classStrAddDel (dopts["tab_atr" ]["class"],"bw-tab-item-list");
1529
- dopts["tabc_atr"]["class"] = bw.classStrAddDel (dopts["tabc_atr"]["class"],"bw-tab-content-list");
1530
-
1531
- return bw.html({t:"div",a: dopts["atr"],c:[["ul",dopts["tab_atr"],ti],["div",dopts["tabc_atr"],tc]]});
1532
- };
1533
-
1534
-
1535
- // ===================================================================================
1536
-
1537
- bw.htmlTable = function(data,opts) {
1538
- /**
1539
- bw.makeHTMLTableStr (data, options)
1540
-
1541
- Creates an HTML table element (as a string) from raw array data.
1542
-
1543
- var table1 =
1544
- [["this", "that", "the", "other"],[,6,4,0,4],[3,5,1,4],[1,2,4,5],["2u30","23",function(){return 834},23]];
1545
- document.getElementById("myTableDiv") = bw.makeHTMLTableStr(table1); // displays simple table.
1546
-
1547
- var options = {
1548
- useFirstRowAsHeaders:false, // first row is data
1549
- caption:"Important Table" // caption
1550
- sortable: false | true | function // make table sortable (false is default, if true uses bw built-in sort, else supply function)
1551
- }
1552
- document.getElementById("myTableDiv") = bw.htmlTable(table1, options);
1553
-
1554
- Options:
1555
- useFirstRowAsHeaders : true; //
1556
- */
1557
- if ((_to(data) != "array") || (data.length < 1))
1558
- return "";
1559
-
1560
- //default options
1561
- var dopts = {
1562
- useFirstRowAsHeaders : true,
1563
- useDefaultStyle : true, // for
1564
- atr : {}, // attributes for table object can use function() for dynamic
1565
- thead_atr : {}, // attributes for table head section
1566
- th_atr : {}, // attributes for header cells,
1567
- tbody_atr : {}, // atttributs for table body section
1568
- tr_atr : {}, // attributes for rows
1569
- td_atr : {}, // attributes for cells
1570
- caption : "", // optional table caption (can be HTML, or function, bw.buildHTMLObjString compatible data)
1571
- sortable : false// make table sortable. if true, uses default sort, otherwise pass function to sort table. f(a,b,optionalColumnNumber)
1572
- };
1573
-
1574
- var i=0,head="",body="",r,_hs=bw.html;
1575
- dopts = optsCopy(dopts,opts);
1576
-
1577
- if (dopts.useDefaultStyle) {
1578
- // dopts.atr["class"] = "bw-table-stripe bw-table-col0-bold bw-table-compact bw-table-border-round bw-table-head bw-table-cellpad";
1579
- dopts.atr["class"] = "bw-table bw-table-stripe";
1580
- }
1581
- if (dopts.sortable == true) {
1582
- dopts.th_atr["onclick"] = "bw.sortTableDispatch(this)";
1583
- if ("class" in dopts.th_atr)
1584
- dopts.th_atr["class"] += dopts.th_atr["class"].split(/[ ]+/).indexOf("bw-table-sort-xxa") <0 ? " bw-table-sort-xxa" : "";
1585
- else
1586
- dopts.th_atr["class"] = "bw-table-sort-xxa";
1587
- }
1588
- else {
1589
- if (_to(dopts.sortable) == "function") {
1590
- var sfid = bw.funcRegister(dopts.sortable);
1591
- dopts.th_atr["onclick"] = bw.funcGetDispatchStr(sfid,"this");
1592
- }
1593
- }
1594
-
1595
- if (dopts["useFirstRowAsHeaders"]) {
1596
- head=data[0].map(function(x){return _hs({t:"th",a:dopts.th_atr,c:x});}).join("");
1597
- head= _hs({t:"tr",a:dopts.tr_atr,c:head});
1598
- i=1;
1599
- }
1600
- else
1601
- i=0;
1602
- head = bw.html({t:"thead",a:dopts.thead_atr,c:head});
1603
-
1604
- for (; i<data.length; i++) {
1605
- r = data[i].map(function(x){return _hs({t:"td",a:dopts.td_atr,c:x});}).join("");
1606
- body+= _hs({t:"tr",a:dopts.tr_atr,c:r});
1607
- }
1608
- body = bw.html({t:"tbody",a:dopts.tbody_atr,c:body});
1609
- //console.log(head,'\n',body);
1610
- dopts.caption = dopts.caption == "" ? "" : _hs({t:"caption",a:{},c:dopts.caption});
1611
- return _hs({t:"table",a:dopts.atr,c:[dopts.caption,head,body]});
1612
- };
1613
-
1614
-
1615
- bw.htmlAccordian = function (data, opts) {
1616
- /**
1617
- htmlAccordian
1618
-
1619
- [[data-title, data-to-show, {show: true|false}], // show determines whether the default content is visible
1620
- [...]]
1621
-
1622
- data-title and data-to-show can be strings or any valid bw.html() constructs
1623
- */
1624
- var s = "";
1625
- if (_to(data) !== "array")
1626
- return s;
1627
-
1628
- var dopts = {
1629
- "atr" : { "class": "bw-accordian-container"}, // div for overall accordian
1630
- "atr_h" : { "onclick":"bw.DOMClassToggle(this.nextSibling,'bw-hide')", "class" : "bw-thm-light"}, // div wrapping each header
1631
- "atr_c" : { "class":"bw-hide"} // div wrapping each content
1632
- };
1633
- dopts = optsCopy(dopts,opts);
1634
- dopts.atr_h["onclick"]="bw.DOMClassToggle(this.nextSibling,'bw-hide')";
1635
- //dopts.atr_h["class"]="bw-thm-light";
1636
- //console.log(dopts);
1637
- s = data.map(function(x){
1638
- var a=dopts["atr_c"],show;
1639
- show = ( (x.length > 2) && (x[2].show==true));
1640
-
1641
-
1642
- if (a["class"]) {
1643
- a["class"] = show ? bw.classStrAddDel(a["class"],"","bw-hide"):bw.classStrAddDel(a["class"],"bw-hide") ;
1644
- }
1645
- else a["class"]=show ? "":"bw-hide";
1646
-
1647
- return bw.html({t:"div",a:dopts["atr_h"],c:[x[0]]})+bw.html({t:"div",a:a,c:[x[1]]} );
1648
- }).join("");
1649
- s = bw.html({t:"div",a:dopts["atr"],c:[s]});
1650
- return s;
1651
- };
1652
- // ===================================================================================
1653
- bw.htmlSign = function (content, opts) {
1654
- /**
1655
- htmlSign("my content",options)
1656
- create a centered banner / billboard
1657
- */
1658
- var dopts = {
1659
- atr : {style:{"font-weight":"700", "font-size":"7em"}},
1660
- escContent : false,
1661
- };
1662
-
1663
- dopts = optsCopy(dopts,opts);
1664
- content = dopts.escContent!=false ? bw.htmlSafeStr(content) : content;
1665
- var c = {a:{class:"bw-sign" },c:[{c:{a:dopts.atr, c:[content]}}]};
1666
- //{a:{class:"bw-jumbo"},c:[{c:{ c:"foo"}}]}
1667
- return bw.html(c);
1668
- };
1669
- // ===================================================================================
1670
- bw.getFile = function (fname,callback_fn, options) {
1671
- /**
1672
- bw.getFile(filename,callback)
1673
- Attempt to load a file.
1674
- Works both client side and i nodejs.
1675
- */
1676
- var dops = {
1677
- parser : "raw" // valid types are "raw", "JSON", future "CSV", "TSV" or parserFunction
1678
- };
1679
-
1680
- dops = optsCopy(dops,options);
1681
-
1682
- if (_to(fname) != "string") {
1683
- return "invalid filename";
1684
- }
1685
-
1686
- var prs = (dops["parser"]=="JSON") ? JSON.parse : function(s){return s;};
1687
-
1688
-
1689
- if (bw.isNodeJS() ==true) {
1690
- var fs = require("fs");
1691
- fs.readFile(fname, "utf8", function (err, data) { if (err) throw err; callback_fn(prs(data)); });
1692
- }
1693
- else // running in a browser
1694
- {
1695
- var x = new XMLHttpRequest();
1696
- x.overrideMimeType("application/json");
1697
- x.open("GET", fname, true);
1698
- x.onreadystatechange =
1699
- function () {if (x.readyState == 4 && x.status == "200") {callback_fn(prs(x.responseText));}};
1700
- x.send(null);
1701
- }
1702
- return "BW_OK";
1703
- };
1704
-
1705
- bw.getJSONFile = function (fname,callback_fn) { return bw.getFile(fname,callback_fn,{"parser":"JSON"});};
1706
-
1707
- bw.copyToClipboard = function(data) {
1708
- /**
1709
- bw.copyToClipboard
1710
- simple copy content to clipboard. (browser only)
1711
- */
1712
-
1713
- /*
1714
- var temp = document.createElement("input");
1715
- var b = document.getElementsByTagName("body")[0];
1716
- b.appendChild(temp);
1717
-
1718
- temp.innerText = data;
1719
- temp.select();
1720
- document.execCommand("copy");
1721
- temp.remove();
1722
-
1723
-
1724
-
1725
- var temp = document.createElement("input");
1726
- document.getElementsByTagName("body")[0].append(temp);
1727
- temp.innerHTML = data;
1728
- //temp.val(data).select();
1729
-
1730
- //var temp = document.createElement("input");
1731
- //var b = document.getElementsByTagName("body")[0];
1732
- //b.appendChild(temp);
1733
- //temp.innerText = data;
1734
- temp.select();
1735
- document.execCommand("copy");
1736
- temp.remove();
1737
- */
1738
- if (bw.isNodeJS())
1739
- return;
1740
- var listener = function (e) {
1741
- e.clipboardData.setData("text/html", data);
1742
- e.clipboardData.setData("text/plain", data);
1743
- e.preventDefault();
1744
- };
1745
- document.addEventListener("copy", listener);
1746
- document.execCommand("copy");
1747
- document.removeEventListener("copy", listener);
1748
- };
1749
-
1750
- // ===================================================================================
1751
- bw.saveClientFile = function(fname,data) {
1752
- /**
1753
- bw.saveClientFile(fname,data) saves data the program the client environtmnet
1754
- fname is filename to save as
1755
- data is data to save.
1756
-
1757
- works both in node and browser.
1758
- */
1759
- if (bw.isNodeJS()) {
1760
- var fs = require("fs");
1761
- fs.writeFile(fname, data, function (err) {
1762
- if (err) return bw.log(err);
1763
- bw.log("error saving ",fname,data);
1764
- });
1765
- }
1766
- else { // we're in a browser
1767
-
1768
- var saveData = (function () {
1769
- var a = document.createElement("a");
1770
- document.body.appendChild(a);
1771
- a.style = "display: none";
1772
- return function (data, fname) {
1773
- var json = JSON.stringify(data),
1774
- blob = new Blob([json], {type: "octet/stream"}),
1775
- url = window.URL.createObjectURL(blob);
1776
- a.href = url;
1777
- a.download = fname;
1778
- a.click();
1779
- window.URL.revokeObjectURL(url);
1780
- };
1781
- }());
1782
- saveData(data,fname);
1783
- }
1784
- };
1785
-
1786
- // ===================================================================================
1787
- //Timers ... clear / read / fixed number of events
1788
-
1789
- // ===================================================================================
1790
- // crude performance measurements
1791
- var gBWTime = (new Date()).getTime(); //global closure for time. 'cause we always want a gbw gbw time :)
1792
-
1793
- // ===================================================================================
1794
- bw.clearTimer = function (message) {
1795
- /**
1796
- bw.clearTimer("message")
1797
- When bitwrench loads its starts a timer which can be checked at any time as a ref running (see bw.readTimer()).
1798
- bw.clearTimer() clears the timer with optional message.
1799
- */
1800
- gBWTime = (new Date()).getTime();
1801
- if (_to(message) != "undefined")
1802
- bw.logd(String(message));
1803
- return gBWTime;
1804
- };
1805
-
1806
- // ===================================================================================
1807
- bw.readTimer = function (message) {
1808
- /**
1809
- bw.readTimer("message")
1810
- When bitwrench loads its starts a page timer which can be checked for how long the page as been running.
1811
- */
1812
- var ct = (new Date()).getTime();
1813
- if (_to(message) != "undefined")
1814
- bw.logd(String(message));
1815
- return ct-gBWTime;
1816
- };
1817
- bw.clearTimer(); //when bw is loaded, we start the timer.
1818
-
1819
- // ===================================================================================
1820
- bw.setIntervalX = function (callback, delay, number_of_repetitions) {
1821
- /**
1822
- bw.setIntervalX(callbackFn, delayBtwCalls, repetitions)
1823
- set a javascript timer to only run a max of N repetions.
1824
-
1825
- Example:
1826
- bw.setIntervalX(function(x){console.log(x)},100,5)
1827
- this will the function 5 times 100ms apart
1828
- */
1829
- var x = 0;
1830
- var intervalID = setInterval(function () {
1831
- callback(x);
1832
-
1833
- if (++x >= number_of_repetitions) {
1834
- clearInterval(intervalID);
1835
- }
1836
- }, delay);
1837
- };
1838
-
1839
- // ===================================================================================
1840
- bw.repeatUntil = function (testFn, successFn, failFn, delay, maxReps, lastFn) {
1841
- /**
1842
- bw.repeatUntil()
1843
- repeatUntil runs the supplied testFn every delay milliseconds up until a maxReps number of times.
1844
- if the test function returns true it runs the successFn and stops the iterations.
1845
- then the lastFn is called with the params (true, number_of_attempts).
1846
- lastFn is optional.
1847
-
1848
- for each time the testFn is called and fails, the failFn() is called.
1849
-
1850
- After the last rep has been completed the lastFn is called with (with the last testFn result and
1851
- with the current iteration).
1852
-
1853
-
1854
- lastFn is optional.
1855
- failFn is optional
1856
-
1857
- Example:
1858
- bw.repeatUntil( myLibsAndDataAreLoaded_fn, renderMyChart, null, 250, 10, null); // attempts to wait until mylib is loaded 10 times before giving up
1859
-
1860
- */
1861
- var _count = 0;
1862
- if (typeof testFn != "function")
1863
- return "err";
1864
- if (typeof delay != "number")
1865
- delay = 250; // 250ms
1866
- if (typeof maxReps != "number")
1867
- maxReps = 1; // run 1 time.
1868
-
1869
- var _testFn = testFn;
1870
- var _successFn = (typeof successFn == "function") ? successFn : function () {};
1871
- var _failFn = (typeof failFn == "function") ? failFn : function () {};
1872
- var _lastFn = (typeof lastFn == "function") ? lastFn : function () {};
1873
-
1874
- var _f = function () {
1875
- var success = _testFn();
1876
- if (true == success) {
1877
- _successFn();
1878
- _lastFn(true, _count);
1879
- }
1880
- else {
1881
- _failFn();
1882
-
1883
- if (_count >= maxReps) {
1884
- _lastFn(success, _count);
1885
- }
1886
- else {
1887
- _count++;
1888
- window.setTimeout(_f, delay);
1889
- }
1890
- }
1891
- };
1892
- _f();
1893
- };
1894
- // =============================================================================================
1895
- /*
1896
- bw.htmlDataToImg = function(data, opts) {
1897
- /**
1898
- htmlDataToImg(data, opts) // takes a 2D array of numbers and render as an image
1899
- 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.
1900
-
1901
- OR
1902
-
1903
- function can be a string as long as it returns a valud HTML color prefixed with "#"
1904
-
1905
- e.g.
1906
- "#123"
1907
- "#112233"
1908
-
1909
- e.g.:
1910
- function (return 23)
1911
- function(x,y) { return x+y;}
1912
-
1913
- * /
1914
- var dopts = {
1915
- outputType : "canvas" , // "table" | "divs" | "svg"
1916
- colorMode : "auto", // use greyscale map
1917
- colorStretch: 1.0
1918
- }
1919
-
1920
- dopts = optsCopy(dopts,opts);
1921
- // if (_to(dopts["colorMapFn"]) != "function")
1922
- // dopts["colorMapFn"] = function(x){var c= mapScale(x,0,255,0,255,true).}
1923
-
1924
-
1925
-
1926
- }
1927
- */
1928
- // =============================================================================================
1929
- bw.naturalCompare = function (as, bs){
1930
- /**
1931
- bw.naturalCompare(a,b) {
1932
- bw.naturalCompare() is a function which can be passed to an array sort to provide natural sorting of mixed array elements.
1933
-
1934
- [3,4,2,1,"10","111","foo","bar","01","this123","This123", "848"].sort()
1935
- vs
1936
- [3,4,2,1,"10","111","foo","bar","01","this123","This123", "848"].sort(bw.naturalCompare)
1937
-
1938
- it is the default sort for bw.sortHTMLTable()
1939
-
1940
- */
1941
- //https://www.webdeveloper.com/forum/d/254726-sorting-alphanumeric-array (taken from here) see also
1942
- //using .localCompare() in newer versions of JS
1943
-
1944
- var a, b, a1, b1, i= 0, L, rx= /(\d+)|(\D+)/g, rd= /\d/;
1945
- if(isFinite(as) && isFinite(bs)) return Math.sign(as - bs);
1946
- a= String(as).toLocaleLowerCase();
1947
- b= String(bs).toLocaleLowerCase();
1948
- if(a=== b) return (as > bs) ? 1 : 0;
1949
- if(!(rd.test(a) && rd.test(b))) return a> b? 1:-1;
1950
- a= a.match(rx);
1951
- b= b.match(rx);
1952
- L= a.length> b.length? b.length:a.length;
1953
- while(i<L){
1954
- a1= a[i];
1955
- b1= b[i++];
1956
- if(a1!== b1){
1957
- if(isFinite(a1) && isFinite(b1)){
1958
- if(a1.charAt(0)=== "0") a1= "." + a1;
1959
- if(b1.charAt(0)=== "0") b1= "." + b1;
1960
- return a1 - b1;
1961
- }
1962
- else return a1> b1? 1:-1;
1963
- }
1964
- }
1965
- return Math.sign(a.length - b.length);
1966
- };
1967
-
1968
- // =============================================================================================
1969
- bw.sortHTMLTable = function (table, col, dir, sortFunction) {
1970
- /**
1971
- bw.sortHTMLTable(table, column, optionalSortFunction).
1972
-
1973
- sort any HTML table active in the DOM
1974
- table must be a valid DOM table element or CSS selector (first element is used)
1975
-
1976
- default uses string compare. but can pass in a function
1977
- sortFunc(a,b,col) // a and b are the cells to compare, col is optional info on what column this is
1978
- */
1979
-
1980
- var rows, switching, i, x, y, shouldSwitch;
1981
- var sortF = _to(sortFunction) == "function" ? sortFunction : bw.naturalCompare;
1982
-
1983
- table = bw.DOM(table)[0];
1984
-
1985
- dir = (dir==true) || (dir=="up") ? true : false;
1986
-
1987
- switching = true;
1988
- col = _to(col) == "number" ? col : 0; //default sort on left most column
1989
-
1990
- //Make a loop that will continue until no switching has been done
1991
- while (switching) {
1992
- //start by saying: no switching is done:
1993
- switching = 0;
1994
- rows = table.getElementsByTagName("TR");
1995
- /*Loop through all table rows (except the first, which contains table headers):*/
1996
- for (i = 1; i < (rows.length - 1); i++) {
1997
- //start by saying there should be no switching:
1998
- shouldSwitch = 0;
1999
- /*Get the two elements you want to compare,
2000
- one from current row and one from the next:*/
2001
- x = rows[i].getElementsByTagName("TD")[col].innerHTML;
2002
- y = rows[i + 1].getElementsByTagName("TD")[col].innerHTML;
2003
-
2004
- //check if the two rows should switch place:
2005
-
2006
- shouldSwitch = (dir) ? sortF(x,y,col) > 0 : sortF(x,y,col) < 0;
2007
- if (shouldSwitch)
2008
- break;
2009
- }
2010
-
2011
- if (shouldSwitch) {
2012
- //If a switch has been marked, make the switch and mark that a switch has been done:
2013
- rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
2014
- switching = true;
2015
- }
2016
- }
2017
- };
2018
-
2019
- // =============================================================================================
2020
- bw.sortTableDispatch = function (item,fn) {
2021
- /**
2022
- bw.sortTableDispatch(el) is used to bind sorting functions to tables generated by bw.htmlTable(....)
2023
- item must be a valid DOM element or id.
2024
- */
2025
- var i;
2026
-
2027
- item = bw.DOM(item)[0];
2028
-
2029
- if (_to(item).substr(0,4) != "html")
2030
- return false; //something not right about this table element
2031
-
2032
- var index=0,dir;
2033
- var cols = item.parentElement.getElementsByTagName("th");
2034
- //update which tab selected
2035
- for (i=0; i< cols.length; i++) {
2036
- if (cols[i] == item) { // selected tab logic
2037
- index = i;
2038
- dir = bw.DOMClass(cols[i],"bw-table-sort-upa") ; // ifthe current col is already up..
2039
- if (dir) {
2040
- bw.DOMClass(cols[i],"bw-table-sort-upa", "bw-table-sort-dna" );
2041
- }
2042
- else { //dna or xxa
2043
- if (bw.DOMClass(cols[i],"bw-table-sort-dna")) {
2044
- bw.DOMClass(cols[i],"bw-table-sort-dna", "bw-table-sort-upa" );
2045
- } else
2046
- bw.DOMClass(cols[i],"bw-table-sort-xxa", "bw-table-sort-upa" );
2047
- }
2048
- }
2049
- else{ // its not the selected column so we clear the up / down arrow
2050
- bw.DOMClass(cols[i],"bw-table-sort-upa","");
2051
- bw.DOMClass(cols[i],"bw-table-sort-dna","");
2052
- bw.DOMClass(cols[i],"bw-table-sort-xxa","bw-table-sort-xxa");
2053
- }
2054
-
2055
- }
2056
- bw.sortHTMLTable(item.parentElement.parentElement.parentElement,index,dir,fn);
2057
- };
2058
- // ===================================================================================
2059
- /**
2060
- bw.function dispatch for DOM elements..
2061
-
2062
- 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().
2063
- */
2064
- var _fnRegistry = {};
2065
- var _fnIDCounter = 0;
2066
-
2067
-
2068
- bw.funcRegister = function (fn, forceName) {
2069
- /**
2070
- bw.funcRegister()
2071
- register a function to be called by iD.
2072
- fn is any function or can be anonymous function.
2073
- (optional) forceName forces the returned ID used to be forceName. forceName must be only alpha and numeric chars.
2074
- forceName is useful when declaring static HTML content and one wants to use the bwFunctionDispatch but before bitwrench has been loaded or run.
2075
-
2076
- In this case in the static code call like this:
2077
-
2078
- <div class="..." onclick="bw.funcGetById('myFnName')(this)"> regular html content goes here </div>
2079
- <script ..>
2080
- function superDuperFunctionCode (a) { .... code for my function ... };
2081
- bw.funcRegister(superDuperFunctionCode,"myFnName"); //now when the element is clicked on superDuperFunctionCode() will be called.
2082
- </script>
2083
-
2084
- */
2085
- var fnID = "class_bwfn_" + _fnIDCounter;
2086
- _fnIDCounter++;
2087
- fnID = _to(forceName) == "string" ? forceName : fnID;
2088
- fnID.trim();
2089
- _fnRegistry[fnID] = fn;
2090
- return fnID;
2091
- };
2092
-
2093
- bw.funcUnregister = function (fnID) {
2094
- /**
2095
- bw.funcUnregister(fnID)
2096
- remove a function from the bitwrench dispatch registry
2097
- */
2098
- if (fnID in _fnRegistry)
2099
- delete _fnRegistry[fnID];
2100
- };
2101
-
2102
- bw.funcGetById = function(fnID,errFn) {
2103
- /**
2104
- bw.funcGetById(fnId, errFn)
2105
- allows a function to be exectued by its bw function ID.
2106
- bw.funcGetById(myId)(... args ...)
2107
-
2108
- errFn is optional function to call if fnID is not found.
2109
-
2110
- example:
2111
- var myFunc = bw.getFuncById("myFuncID"); // function must already be registered.
2112
- */
2113
- fnID = String(fnID);
2114
- if (fnID in _fnRegistry)
2115
- return _fnRegistry[fnID];
2116
- else {
2117
- var _id = fnID;
2118
- return (_to(errFn) == "function") ? errFn : function(){bw.log(_id,"bw.funcGetById(): unregistered fn error");} ;
2119
- }
2120
- };
2121
-
2122
- bw.funcGetDispatchStr = function (fnID, argstring) {
2123
- /**
2124
- bw.funcGetDispatchStr(fnID, argString)
2125
- create a string suitble for use in DOM element dispatch. note argstring is a literal so variables must be reduce to their values.
2126
- see bw.funcRegister() for getting valid IDs for user supplied functions.
2127
-
2128
- example: bw.funcGetDispatchStr("myFuncID","param1,param2")
2129
- */
2130
-
2131
- switch (_to(argstring)) {
2132
- case "string" :
2133
- case "number" :
2134
- argstring = String(argstring);
2135
- break;
2136
- case "array" :
2137
- argstring = argstring.join(",");
2138
- break;
2139
- case "function":
2140
- argstring = argstring();
2141
- break;
2142
- default:
2143
- argstring = "";
2144
- }
2145
-
2146
- return "bw.funcGetById('"+fnID+"')("+argstring+")";
2147
- };
2148
-
2149
- bw.funcGetRegistry = function() {
2150
- return _fnRegistry;
2151
- };
2152
-
2153
- // =============================================================================================
2154
- bw.loremIpsum = function (numChars, startSpot, startWithCapitalLetter) {
2155
- /**
2156
- bw.loremIpsum(numChars, startSpot)
2157
-
2158
- generate a simple string of Lorem Ipsum text (sample typographer's text) of numChars in length.
2159
-
2160
- if startSpot is supplied, it starts the string at the supplied index e.g. bw.loremIpsum(200, 50)
2161
- will supply 200 chars of loremIpsum starting at index 50 of the Lorem Ipsum sample text.
2162
-
2163
- 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.
2164
- default is true;
2165
-
2166
- Default is a paragraph of lorem ipsum (446 chars)
2167
- */
2168
-
2169
- startSpot = _to(startSpot) != "number" ? 0 : Math.round(startSpot);
2170
-
2171
- 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. ";
2172
- startSpot = startSpot % l.length;
2173
- l= l.substring(startSpot, l.length) + l.substring(0,startSpot);
2174
-
2175
- if (_to(numChars) != "number")
2176
- numChars = l.length;
2177
-
2178
- var i=numChars, s="";
2179
-
2180
-
2181
- while (i>0) {
2182
- s+= (i < l.length) ? l.substring(0,i) : l;
2183
- i-= l.length;
2184
- }
2185
- if (s[s.length-1] == " ")
2186
- s= s.substring(0,s.length-1) + "."; // always end on non-whitespace. "." was chosen arbitrarily.
2187
- if (startWithCapitalLetter != false) {
2188
- var c = s[0].toUpperCase();
2189
- c = c.match(/[A-Z]/) ? c:"M";
2190
- s = c+s.substring(1,s.length);
2191
- }
2192
-
2193
- return s;
2194
-
2195
- };
2196
-
2197
- bw.docString = function (s, options) {
2198
- /**
2199
- bw.docString(functionAsString, options)
2200
- returns array of valid docStrings embedded in a string
2201
-
2202
- @param docType{string} : "jsdoc" | "python" | "custom" (python means triplequote (") 3 times), "custom" means supply delims
2203
- @param options {delims:[string,string]} : start, stop delimiters (only used if docType set to "custom")
2204
-
2205
- @return array{strings} : array of captured params
2206
-
2207
- */
2208
-
2209
- var dopts = {
2210
- docType : "jsdoc", // "js doc", "python", "other" (jsdoc is default)
2211
- delims : ["/**","*/"],
2212
- parseJSDocParams : false,
2213
- dropLeadin : false // removes lead-in whitespace or floating single * on each line e.g. " * @mycomment" ==> "@mycomment"
2214
- };
2215
- dopts = optsCopy(dopts,options);
2216
-
2217
- var _es = function (str) {return str.replace(/(?=[\\^$*+?.()|{}[\]])/g, "\\");}; // do escape of regex chars
2218
-
2219
- dopts["delims"] = bw.choice(dopts["docType"],{
2220
- "jsdoc" : ["/**","*/"],
2221
- "python": ["\"\"\"","\"\"\""], // old regex: /["]{3}([\s\S]*?)["]{3}/ig
2222
- "jspy" : ["/**\"\"\"","\"\"\"*/"] // js && python
2223
- },dopts["delims"]);
2224
-
2225
- var c = (_to(s)=="function") ? s.toString() : String(s);
2226
- var r = [];
2227
-
2228
- try {
2229
- var re = (new RegExp( _es(dopts["delims"][0])+ "\\s*\\n*([^\\*]|(\\*(?!\\/)))*" +_es(dopts["delims"][1]),"ig")); // "([\\s\\S]*?)"
2230
- r = c.match(re);
2231
- }
2232
- catch (e) {bw.log(String(e));}
2233
-
2234
- if (_to(r)=="array") {
2235
- 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
2236
- 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
2237
- }
2238
- else
2239
- r=[];
2240
-
2241
- return r;
2242
- };
2243
- // =============================================================================================
2244
- bw.docStringParseLine = function(s) {
2245
- /**
2246
- Parse a single line of a jsdoc string.
2247
- @param {string} s - line of docstring to parse
2248
- @return - dict of line contents {source: s, field:string, name:string, description: string, types: type1,type2 }
2249
- if not a valid doc string line then returns source string only
2250
- */
2251
- var r={"source":s, "field" : "", "types":"", "name" :"", "description" : ""};
2252
- var a = s.replace(/^\s*(\/\*\*?)?|(\*\/)?\s*$/ig,""); // remove the comment markers if still there "/** my comment */"" ==> "my comment"
2253
- a = a.replace(/^\s*\**\s*/,""); // remove any cruft at beginning of line " * @myParam {}....." ==> "@myParam {}....."
2254
- if (a.charAt(0) == "@") { // if we have hit a @fieldname parameter we start parsing.
2255
- // ([str, regex, fieldStr, result{}]) ==> ([str, regex, fieldStr, result{}]) ::> ([str,result{},fieldStr,regex])
2256
- /*
2257
- var _tok = function(x){
2258
- var m = x[0].match(x[1]);
2259
- if (m != null) {x[4][3]=m[1];}
2260
- x[0] = x[0].replace(x[1],"");
2261
- return x;
2262
- }
2263
- */
2264
- //r = [[e,f],[e,f],[e,f],[e,f]].reduce(,_tok);
2265
-
2266
- var e,x;
2267
- var t = bw.trim;
2268
- e =/^@([A-Za-z0-9_<>[\]]*)/i;
2269
- x = a.match(e);
2270
- if (x != null) {r["field"] = t(x[1]);} else return r; // didn't match... opt out here
2271
- a = a.replace(e,"");
2272
-
2273
- e = /^\s*\{([A-Za-z0-9_|\s,.\-+!@#$%^&*()=[\]]*)\}/i;
2274
- x = a.match(e);
2275
- if (x != null) {r["types"]=t(x[1]);} // types is optional..
2276
- a = a.replace(e,"");
2277
-
2278
- e = /^\s*([\S]*)/i;
2279
- x = a.match(e);
2280
- if (x != null) {r["description"]=t(x[1]);} //
2281
- a = a.replace(e,"");
2282
-
2283
- e = /^\s*([\S]*)/i;
2284
- x = a.match(e);
2285
- if (x != null) {r["name"]=t(x[1]);} //
2286
- a = a.replace(e,"");
2287
-
2288
- // descrpition ==> name: "" description : "description"
2289
- // description we we ==> name: "" description : "description we we "
2290
- // name - description we we ==> name: "name" descrpition : "description we we"
2291
- // - description we we ==> name: "" description : "description we we"
2292
- if (r["name"].match(/^\s*-+\s*/) != null) {
2293
- r["name"] = r["description"];
2294
- r["description"] = t(a);
2295
- } else {
2296
- r["description"] = r["description"]+" "+r["name"]+" "+t(a);
2297
- r["name"] ="";
2298
- }
2299
- }
2300
- return r;
2301
- };
2302
- // =============================================================================================
2303
- bw.docStringParse = function(s) {
2304
- /**
2305
- @method bw.parseJsDocString() parse and extract info from a jsdoc style comment. expects there to be only a single docString comment
2306
- @description docStringParse parses a jsdoc string
2307
- and returns the paramters as an array which can be formatted for display or interrogtion.
2308
- @param{string} - a valid js docstring
2309
-
2310
- @returns An array of triplets [@param, {types}, comment info]
2311
- */
2312
-
2313
- /*
2314
- implementation notes:
2315
- the parser splits the candidate doc string in to lines.
2316
-
2317
- Examples:
2318
-
2319
- * Assign the project to an employee.
2320
- * @param {Object} - The employee who is responsible for the project. ==> ["@param","object","", "The employee who is resposnsible for the project"]
2321
- * @param {string} employee.name - The name of the employee.
2322
- * @param {string} employee.department - The employee's department.
2323
-
2324
-
2325
- */
2326
-
2327
-
2328
- s=bw.docString(s)[0];
2329
- var a = s.split("\n");
2330
- //console.log(a);
2331
- var i,r=[bw.docStringParseLine(a[0])];
2332
- for (i=1;i<a.length;i++) {
2333
- var l = bw.docStringParseLine(a[i]);
2334
- if (l["field"]=="") { // nothing parseable...
2335
- if (r[r.length-1]["field"]=="") {
2336
- r[r.length-1]["source"] += l["source"];
2337
- } else
2338
- r[r.length-1]["description"] += l["source"];
2339
- } else r.push(l);
2340
- }
2341
- return r;
2342
- };
2343
- // =============================================================================================
2344
- bw.isHexStr = function (str, allowChars) {
2345
- /**
2346
- isHexStr() returns a number of hex digits found or false if non-hex string.
2347
- allow is an optional string of characters "-+."etc to permit in the string.
2348
- the allow characters are not counted in the result
2349
-
2350
- examples:
2351
- bw.isHEXStr("123a") ===> 4
2352
- bw.isHEXStr("12-3a") ===> false
2353
- bw.isHEXStr("12-3a","-") ===> 4
2354
- */
2355
- if ( _to(str) == "string") {
2356
- str = str.replace(new RegExp("["+allowChars+"]","g"),"");
2357
- var isHexReg = new RegExp("^[0-9A-Fa-f]{"+str.length+"}$");
2358
- return (isHexReg.test(str) == true) ? str.length : false;
2359
- }
2360
- return false;
2361
- };
2362
-
2363
- // =============================================================================================
2364
- //bw.__monkey_patch_is_nodejs__ = "ignore"; //used in test suites. use carefully. only acceptable values are true, false, "ignore"
2365
- 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;});
2366
-
2367
- bw.isNodeJS = function () {
2368
- /**
2369
- bw.isNodeJS() ==> returns true if running in node environment (else browser)
2370
- */
2371
- if (bw.__monkey_patch_is_nodejs__.get() != "ignore")
2372
- return bw.__monkey_patch_is_nodejs__.get();
2373
- return (typeof module !== "undefined" && module.exports) !== false; //a hack will fix later
2374
- };
2375
- //console.log("=====",bw.isNodeJS())
2376
-
2377
- // =============================================================================================
2378
- bw.fixNum = function(num,digits) {
2379
- /**
2380
- bw.fixedNum(num,digits)
2381
-
2382
- Truncate a number at digits number of places.
2383
- bw.fixNum(1.2345,2) ===> 1.23
2384
- bw.fixNum(234.32,-2) ===> 200
2385
- */
2386
- num = Number(num);
2387
-
2388
- if (isNaN(num))
2389
- return NaN;
2390
-
2391
- digits = _to(digits) == "number" ? digits : 3;
2392
- num *= Math.pow(10,digits);
2393
-
2394
- //num = Math.trunc(num);
2395
- num = (num > 0) ? Math.floor(num) : Math.ceil(num); // some browsers don't support Math.trunc()
2396
-
2397
- num /= Math.pow(10,digits);
2398
- return num;
2399
- };
2400
-
2401
- // =============================================================================================
2402
- bw.multiArray = function (value, dims) {
2403
- /**
2404
- bw.multiArray(value, dims)
2405
-
2406
- return a multidimensional array where all cells are initialized to value.
2407
-
2408
- bw.multiArray(0,[4,5]) // returns 4x5 array of 0s
2409
- bw.multiArray("test",[4,5]) // returns 4x5 array of "test"
2410
-
2411
- this shorthand is available for single dim arrays
2412
- bw.multiArray(0,5) ===> returns 5x1 array of 0s
2413
-
2414
- bw.multiArray also accepts functions
2415
-
2416
- bw.multiArray(bw.random, [3,4]) ===> creates 3x5 array of random #s btw 0..100
2417
-
2418
- bw.multiArray(function(){return (new Date()).getTime();},[4,6] ) ==> returns values based on the Javascript date
2419
- */
2420
-
2421
- var v = function() { return (_to(value) == "function") ? value(): value;};
2422
- dims = _to(dims) == "number" ? [dims] : dims;
2423
-
2424
- var _array = function(a,dim) {
2425
- if(dim < dims.length) {
2426
- for(var i=0; i<dims[dim]; i++) {
2427
- a[i]= (dim== dims.length -1) ? v() : _array([],dim+1);
2428
- }
2429
- return a;
2430
- }
2431
- };
2432
- return _array([],0);
2433
- };
2434
-
2435
- // =============================================================================================
2436
- bw.clip = function (data, min, max) {
2437
- /**
2438
- @method: bw.clip(data, min, max) clips data in between min and max.
2439
-
2440
- Examples:
2441
- bw.clip(5,2,20) ==> 5 // already in range
2442
- bw.clip(1,2,20) ==> 2 // less than the min value so clips to min value
2443
- bw.clip([1,4,8,35], 2, 20) ==> [2,4,5,20]
2444
- */
2445
- var l = min < max ? min : max;
2446
- var h = max > min ? max : min;
2447
-
2448
- if (_to(data) == "array") {
2449
- return data.map(function(x){ return (x < l) ? l : ((x > h) ? h : x);});
2450
- }
2451
- else
2452
- return (data < l) ? l : ((data > h) ? h : data);
2453
- };
2454
-
2455
- // =============================================================================================
2456
- bw.mapScale = function (x, in0, in1, out0, out1, options) {
2457
- /**
2458
- @method: bw.mapScale()
2459
-
2460
- Map an input value x in its natural range in0...in1 to the output space out0...out1 with optional clipping
2461
- expScale allows sigmoidal warping to stretch input values contrained to a small range. (floating point scale factor)
2462
- x can be either a number or array of numbers.
2463
-
2464
- if options["clip"] = false, then mapScale will extrapolate outside of out0,out1
2465
-
2466
- //this is the function that oficially started bitwrench..
2467
- */
2468
- var dopts = {
2469
- clip : true,
2470
- expScale : false
2471
- };
2472
-
2473
- dopts = optsCopy ( dopts, options);
2474
-
2475
- if (in0==in1)
2476
- return x;
2477
- out0 = _toa(out0, "number", out0, 0);
2478
- out1 = _toa(out1, "number", out1, 1);
2479
-
2480
- var ms= function (z) {
2481
- if (dopts["expScale"]) {
2482
- var y = ((z-((in1+in0) / 2.0)) / (in1 - in0) ) * dopts["expScale"];
2483
- z = ((out1-out0)*(1/(1+Math.exp(-y)))) + out0;
2484
- }
2485
- else
2486
- z = (((z-in0)/(in1-in0))*(out1-out0))+out0;
2487
-
2488
- if (dopts["clip"])
2489
- z= bw.clip(z,out0,out1);
2490
- return z;
2491
- };
2492
-
2493
- if (_to(x) == "number")
2494
- return ms(x);
2495
- return x.map(ms);
2496
- };
2497
-
2498
- // =============================================================================================
2499
- //https://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
2500
- bw.padNum = function(x, width, options) {
2501
- /**
2502
- @description bw.padnum() takes a number and pads left pads (default is '0'). padNum also accepts strings so
2503
-
2504
- bw.padNum(123,5) ==> " 123"
2505
- bw.padNum(1234,5) ==> " 1234"
2506
- bw.padNum("foo",5)==> " foo"
2507
- @param x {number}
2508
- @return {string} padded number
2509
- */
2510
- var dopts = {
2511
- pad : " "
2512
- };
2513
- dopts = optsCopy(dopts, options);
2514
- x = String(x);
2515
- return (x.length >= width) ? x : new Array(width - x.length+1).join(dopts["pad"]) + x;
2516
- };
2517
- // =============================================================================================
2518
- bw.trim = function (s, dir) {
2519
- /**
2520
- @description bw.trim() trims whitespace from string on either left, right, or both. (cross browser works before IE8)
2521
- @param s {string} : a string to trim white space on
2522
- @param dir {"left" | "right" | "both" | "none"} : trim white space on left only, right only or both sides, or no trim (default is both)
2523
- */
2524
- var t = bw.choice(
2525
- dir,
2526
- {
2527
- "left" : /^[\s\uFEFF\xA0\n]+/g,
2528
- "right" : /[\s\uFEFF\xA0\n]+$/g,
2529
- "none" : /(?!)/ // useful for programmatic scenarios (eg. [....].map ) where not all of the entries should be trimmed.
2530
- },
2531
- /^[\s\uFEFF\xA0\n]+|[\s\uFEFF\xA0\n]+$/g
2532
- );
2533
- return String(_toa(s,"undefined","",s)).replace(t,"");
2534
- };
2535
-
2536
- // =============================================================================================
2537
- bw.padString = function (s, width, dir, options) {
2538
- /**
2539
- @description bw.padString() takes a string and pads it to the specified number of chars either left or right or centered.
2540
- */
2541
- var dopts = {
2542
- pad : " ",
2543
- trimDir : "both" // pre-trim the input string: "left", "right", "both", "none"
2544
- };
2545
- dopts = optsCopy(dopts, options);
2546
-
2547
- s = String(s);
2548
- var x = bw.trim(s,dopts["trimDir"]);
2549
- var p = (width > x.length ) ? (width - x.length+1) : 0 ; // total padding
2550
- var q = bw.choice(dir,
2551
- {
2552
- "left" : [p,0],
2553
- "right" : [0,p],
2554
- "center" : [Math.round(p/2), (p-Math.round(p/2)+1)]
2555
- },
2556
- [0,0]
2557
- );
2558
- return ((new Array(q[0])).join(dopts["pad"]))+x+(new Array(q[1])).join(dopts["pad"]);
2559
- };
2560
-
2561
- // =============================================================================================
2562
- bw.random = function(rangeBegin, rangeEnd, options) {
2563
- /**
2564
- @method: random
2565
-
2566
- Return a random number between rangeBegin and RangeEnd (inclusive)
2567
- default is 0,100
2568
-
2569
- options
2570
- {
2571
- setType : "int"
2572
- dims : false | number | [ , , ] // selector for dimensions
2573
- }
2574
-
2575
- options
2576
- setType:
2577
- "int" ==> return an integer (default)
2578
- "float" or "number" ==> return floating point number
2579
-
2580
- dims
2581
- false or ommitted ==> return a single number
2582
- 5 ==> return a 5x1 array of random numbers
2583
- [3,5,2] ==> return a 3x5x2 array of random numbers
2584
-
2585
- example:
2586
- bw.random() ==> returns a number btw 0,100
2587
- bw.random(-4,4,{setType: "float", dims[4,5]}) ==> returns a 3x5 array of floating pt numbers btw -4,4
2588
-
2589
- see also prandom for psuedorandom numbers
2590
-
2591
- */
2592
- rangeBegin = _to(rangeBegin) == "number" ? rangeBegin : 0;
2593
- rangeEnd = _to(rangeEnd) == "number" ? rangeEnd : 100;
2594
-
2595
- var dopts = {
2596
- setType : "int",
2597
- dims : false // if dims is array e.g. [3,4,5] returns random elements array
2598
- };
2599
-
2600
- dopts = optsCopy(dopts,options);
2601
-
2602
- var _rnd = function () {
2603
- var n = 0;
2604
-
2605
- dopts.setType = ["int","float","number"].indexOf(dopts.setType) == -1 ? "int" : dopts.setType;
2606
-
2607
- if (rangeEnd < rangeBegin ) {
2608
- rangeBegin ^= rangeEnd; rangeEnd ^= rangeBegin; rangeBegin ^= rangeEnd;
2609
- }
2610
- n = ((Math.random() * (rangeEnd-rangeBegin)) + rangeBegin);
2611
-
2612
- return (dopts.setType == "int") ? Math.round(n) : n;
2613
- };
2614
-
2615
- if ((_to(dopts["dims"]) == "array") || (_to(dopts["dims"])== "number"))
2616
- return bw.multiArray( _rnd, dopts["dims"]);
2617
-
2618
- return _rnd();
2619
- };
2620
-
2621
- // =============================================================================================
2622
- bw.prandom = function (rangeBegin,rangeEnd,seed, options) {
2623
- /**
2624
- prandom - generate a psuedo random number from internal hash function in a given range
2625
- */
2626
- rangeBegin = _to(rangeBegin) == "number" ? rangeBegin : 0;
2627
- rangeEnd = _to(rangeEnd) == "number" ? rangeEnd : 100;
2628
-
2629
- var dopts = {
2630
- setType : "int",
2631
- dims : false // if dims is array e.g. [3,4,5] returns random elements array
2632
- };
2633
-
2634
- dopts = optsCopy(dopts,options);
2635
- var _cseed = seed;
2636
- var _rnd = function () {
2637
- var n = 0;
2638
-
2639
- dopts.setType = ["int","float","number"].indexOf(dopts.setType) == -1 ? "int" : dopts.setType;
2640
-
2641
- if (rangeEnd < rangeBegin ) {
2642
- rangeBegin ^= rangeEnd; rangeEnd ^= rangeBegin; rangeBegin ^= rangeEnd;
2643
- }
2644
- n = (((bw.hashFnv32a("start string",_cseed) & 0xffff)/(65536)) * (rangeEnd-rangeBegin)) + rangeBegin;
2645
-
2646
- _cseed = (dopts.setType == "int") ? Math.round(n) : n;
2647
- return (dopts.setType == "int") ? Math.round(n) : n;
2648
- };
2649
-
2650
- if ((_to(dopts["dims"]) == "array") || (_to(dopts["dims"])== "number"))
2651
- return bw.multiArray( _rnd, dopts["dims"]);
2652
-
2653
- return _rnd();
2654
-
2655
- };
2656
- // =============================================================================================
2657
-
2658
-
2659
- bw.hashFnv32a= function (str, seed, returnHexStr) {
2660
- /**
2661
- @method Calculate a 32 bit FNV-1a hash
2662
- Found here: https://gist.github.com/vaiorabbit/5657561
2663
- Ref.: http://isthe.com/chongo/tech/comp/fnv/
2664
-
2665
- @param {string} str the input value
2666
-
2667
- @param {integer} [seed] optionally pass the hash of the previous chunk
2668
-
2669
- @param {boolean} [asString=false] set to true to return the hash value as
2670
- 8-digit hex string instead of an integer
2671
-
2672
- @returns {integer | string}
2673
- */
2674
- /*jshint bitwise:false */
2675
- var i, l,
2676
- hval = (typeof seed == "undefined") ? 0x811c9dc5 : seed;
2677
-
2678
- for (i = 0, l = str.length; i < l; i++) {
2679
- hval ^= str.charCodeAt(i);
2680
- hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
2681
- }
2682
- if( returnHexStr ){
2683
- // Convert to 8 digit hex string
2684
- return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
2685
- }
2686
- return hval >>> 0;
2687
- };
2688
-
2689
- // =============================================================================================
2690
- bw.CSSMakeTheme = function(color) {
2691
- /**
2692
- makeThemeCSS (color)
2693
-
2694
- makes a CSS theme color palettte based on the supplied color which is exported as a css style
2695
-
2696
- TODO
2697
- */
2698
- var c = bw.colorRgbToHsl( bw.colorParse(color));
2699
-
2700
- var p = "bw-theme-";
2701
- var thm = ["l5","l4","l3","l2","l1","d1","d2","d3","d4","d5"].map(function(x){return p+x;});
2702
- var im = " !important";
2703
- thm = thm.map(function(x,i){return [x,[["color", ((i<5)?"#000" : "#fff")+im ],["background-color",c + im] ]]; });
2704
-
2705
- return thm;
2706
-
2707
- };
2708
- // =============================================================================================
2709
- bw.CSSSimpleStyles = function(appendToHead, options) {
2710
- /*
2711
- bw.CSSimpleStyles(appendToHead,options)
2712
-
2713
- Generate simple styles for bitwrench.
2714
- write a quick grid style sheet for quick n dirty layout. See docs for examples.
2715
-
2716
- appendToHead ==> if true, attempts to append to HTML <head> (only writes if not already present)
2717
- options: {
2718
- "basics" : "load" // if set to "load will load some global constants for html, body, font-family", set to false to leave these unchanged.
2719
- "exportCSS": false // if true will wrap the output css in "script" tags.
2720
- "id" : "bw-default-styles" // id assigned to the script tag, used for preventing multiple loading in a browser page
2721
- }
2722
-
2723
- */
2724
-
2725
- var dopts = {
2726
- "globals" : false,
2727
- "id" : "bw-default-styles",
2728
- "exportCSS" : false,
2729
- "colorset" : {"color" : "#000", "background-color" :"#ddd", "active" : "#222"},
2730
- "pretty" : false, //make easy to read
2731
- "themes" : // built-in primitive themes
2732
- [ // must be valid CSS keys / values
2733
- [".bw-thm-light" , {"color": "#020202 !important;", "background-color": "#e2e2e2 !important;"}],
2734
- [".bw-thm-dark" , {"color": "#e2e2e2 !important;", "background-color": "#020202 !important;"}],
2735
- ]
2736
- };
2737
-
2738
- var s ="\n", i;
2739
- //var i,j,k,l;
2740
- var _r = bw.fixNum;
2741
- var rl = bw.makeCSSRule;
2742
- dopts = optsCopy(dopts,options);
2743
-
2744
- var defs = { // defaults
2745
- defGlobals: {"box-sizing": "border-box"},
2746
- defContainer: {"height": "100%", "width":"90%", "margin": "0 auto", "padding-left": "2%","padding-right":"2%","left":"0","top":"1%","box-sizing":"border-box"},
2747
- defFontSerif: {"font-family": "Times New Roman, Times, serif"},
2748
- defFontSansSerif: {"font-family": "Arial, Helvetica, sans-serif" }
2749
- };
2750
-
2751
- if (dopts["globals"] == "load") {
2752
- s+= rl([["html","body"],defs.defContainer]);
2753
- s+= rl(["*"+defs.defFontSansSerif]);
2754
- }
2755
-
2756
-
2757
-
2758
- var d = [
2759
- //globals
2760
- ["*", defs.defGlobals],
2761
- [".bw-def-page-setup" , defs.defContainer],
2762
- [".bw-font-serif" , defs.defFontSerif],
2763
- [".bw-font-sans-serif", defs.defFontSansSerif],
2764
- "\n",
2765
- //text handling
2766
- [".bw-left", {"text-align": "left"}],
2767
- [".bw-right", {"text-align": "right"}],
2768
- [".bw-center", {"text-align": "center", "margin": "0 auto"}],
2769
- [".bw-justify", {"text-align": "justify"}],
2770
- [".bw-code", {"font-family":"monospace", "white-space":"pre-wrap"}],
2771
- [".bw-pad1", {"padding-left": "1%", "padding-right":"1%"}],
2772
- "\n",
2773
- //tables
2774
- [".bw-table", {"border-collapse":"collapse", "border-spacing": "0", "border":"1px solid #444"}],
2775
- [".bw-table th", {"background-color": "#bbb", "padding": "4px","border":"1px solid #444"}],
2776
- [".bw-table td", {"padding": "4px","border":"1px solid #444"}],
2777
- [".bw-table-stripe tr:nth-child(even)" , {"background-color":"#f0f0f0"}], // striped rows
2778
- [".bw-table tr td:first-child", {"font-weight": "700"}], // make first col bold
2779
- [".bw-table-border-round", {"border-radius":"2px"}],
2780
- [".bw-table-sort-upa::after", {"content": "\"\\2191\""}], // table sort arrow up (when visible arrows chosen)
2781
- [".bw-table-sort-dna::after", {"content": "\"\\2193\""}], // table sort arrow dn (when visible arrows chosen)
2782
- [".bw-table-sort-xxa::after", {"content": "\"\\00a0\""}], // table sort space (when visible arrows chosen)
2783
- "\n",
2784
- //tabs
2785
- [".bw-tab-item-list", { "margin": 0, "padding-inline-start":0}],
2786
- [".bw-tab-item",{"display":"inline", "padding-top":"0.75em", "padding-left":"0.75em", "padding-right":"0.75em" , "border-top-right-radius":"7px", "border-top-left-radius": "7px"}],
2787
- [".bw-tab-active", {/* padding-top:4px; padding-left:6px; padding-right:6px; padding-bottom:0; */ "font-weight":"700"}],
2788
- [".bw-tab:hover",{"cursor": "pointer", "font-weight": 700/* border: 1px solid #bbb; */}],
2789
- [".bw-tab-content-list", { margin: 0, "padding-top": "0.0em"}],
2790
- [".bw-tab-content",{ display:"none", "border-radius":0}],
2791
- [".bw-tab-content, .bw-tab-active", {"background-color": "#ddd", "padding":"0.5em"}],
2792
- "\n",
2793
- //accordian
2794
- [".bw-accordian-container > div", {"padding": "0.5em"}],
2795
- "\n",
2796
- //grid setup
2797
- [".bw-container",{ margin: "0 auto"}],
2798
- [".bw-row", { width: "100%", display: "block"}],
2799
- [".bw-row [class^=\"bw-col\"]", { float: "left"}],
2800
- [".bw-row::after", { content: "\"\"", display: "table", clear:"both"}],
2801
- [".bw-box-1", {"padding-top":"10px","padding-bottom": "10px", "border-radius": "8px"}],
2802
- "\n",
2803
- [".bw-sign", {"position": "inherit", "display": "table", "height": "100%", "width": "100%"}],
2804
- [".bw-sign > div", {display:"table-cell", "vertical-align" : "middle"}],
2805
- [".bw-sign > div > div", {"text-align":"center"}],
2806
- "\n",
2807
- //misc element controls
2808
- [".bw-hide", { display: "none"}],
2809
- [".bw-show", { display: "block"}]
2810
- ];
2811
-
2812
- //heading generator
2813
- [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"}]);});
2814
-
2815
- d.push("\n");
2816
- // grid system (generated)
2817
- for (var k=1; k<=12; k++)
2818
- d.push([".bw-col-"+k, {width:(_r(k*100/12)+"%")}]);
2819
-
2820
- d.push("\n");
2821
- // generate CSS from above rules
2822
- s+= d.map(function(x){return rl(x,{pretty:dopts.pretty});}).join("\n")+"\n";
2823
-
2824
- d.push("\n");
2825
- //primtive in-built color themeing see opts to overide
2826
- for (i in dopts["colorset"]){
2827
- s+= ".bw-color-"+i+" {"+i+":" +dopts["colorset"][i]+"}\n";
2828
- }
2829
-
2830
- d.push("\n");
2831
- bw.makeCSS( dopts["themes"]);
2832
- for (i=0; i< dopts["themes"].length; i++) {
2833
- s+= rl( dopts["themes"][i]);
2834
- //s+= bw.makeCSS( dopts["themes"][i])
2835
- }
2836
-
2837
- //responsive screen
2838
- var m = "@media only screen and (min-width: ";
2839
- s+= m + "540px) {.bw-def-page-setup {width: 96%;}}\n";
2840
- s+= m + "720px) {.bw-def-page-setup {width: 92%;}}\n";
2841
- s+= m + "960px) {.bw-def-page-setup {width: 88%;}}\n";
2842
- s+= m + "1100px){.bw-def-page-setup {width: 86%;}}\n";
2843
- s+= m + "1600px){.bw-def-page-setup {width: 84%;}}\n";
2844
-
2845
-
2846
- if (bw.isNodeJS() == false) {
2847
- var h = bw.DOM("head")[0];
2848
- var el = document.createElement("style");
2849
- el.id = dopts["id"];
2850
- el.textContent = s;
2851
-
2852
- if (appendToHead && (document.getElementById(dopts["id"]) == null)) // only append once
2853
- h.appendChild(el);
2854
- }
2855
- if (dopts["exportCSS"])
2856
- s = bw.html(["style",{"id":dopts["id"]},"\n/**\n bitwrench basic css styles\n version: "+bw.version()["version"]+"\n */"+s]);
2857
- return s;
2858
- };
2859
-
2860
-
2861
-
2862
- //================================================================================
2863
-
2864
- bw.CSSSimpleThemes = function (d,appendToHead) {
2865
- /**
2866
- bw.bwSimpleThemes() selects simple (we do mean simple) HTML themes for some basic elements.
2867
- if d is an number it selects the built-in theme by index (see docs) else if d is a dictionary the elements
2868
- in d will be converted to a CSS style.
2869
-
2870
- output is a CSS style. if appendToHead is true or omitted then the theme is appended to the head element.
2871
- */
2872
- var s ="",xs={};
2873
- /*
2874
- var thm = {
2875
- defBkgCol : "#333",
2876
- defCol : "#ddd",
2877
- col1 : "#555",
2878
- col2 : "#f0f0f0",
2879
- brd : "#ddd"
2880
- };
2881
-
2882
- var thmCSS =
2883
- [
2884
- ["*" , {"background-color": thm.defBkgCol, "color":thm.defCol, "font-family": "sans-serif", "box-sizing":"border-box"}],
2885
- ["body" , {"margin-top":"1%"}],
2886
- ["th" , {"background-color":thm.col1}],
2887
- ["tbody tr:nth-child(even)" , {"background-color": thm.col2}],
2888
- [["table", "td", "th"] , {"border-collapse":"collapse", "border":"1px solid "+thm.brd}],
2889
- [["td","th"] , {"padding":"4px"}],
2890
- [["div","body","button","table","input"] , {"border-radius": "2px"}]
2891
- // ["div", {"padding-left":"2%", "padding-right":"2%","padding-top":"1%","padding-bottom":"1%"}]
2892
- ];
2893
- */
2894
- var def = [ // default styles
2895
- {
2896
-
2897
- css:
2898
- [
2899
- ["*" , {"background-color": "#333", "color":"#ddd", "font-family": "sans-serif", "box-sizing":"border-box"}],
2900
- ["body" , {"margin-top":"1%"}],
2901
- ["th" , {"background-color":"#555"}],
2902
- ["tbody tr:nth-child(even)" , {"background-color": "#f0f0f0"}],
2903
- [["table", "td", "th"] , {"border-collapse":"collapse", "border":"1px solid #ddd"}],
2904
- [["td","th"] , {"padding":"4px"}],
2905
- [["div","body","button","table","input"] , {"border-radius": "2px"}]
2906
- // ["div", {"padding-left":"2%", "padding-right":"2%","padding-top":"1%","padding-bottom":"1%"}]
2907
- ],
2908
- },
2909
- {
2910
- css:
2911
- [
2912
- ["*" , {"background-color": "#f8f8f8", "color":"#111", "font-family": "sans-serif", "box-sizing":"border-box"}],
2913
- ["body" , {"margin-top":"1%"}],
2914
- ["th" , {"background-color":"#ddd"}],
2915
- ["tbody tr:nth-child(even)" , {"background-color":"#ddd"}],
2916
- [["table", "td", "th"] , {"border-collapse":"collapse", "border":"1px solid #111"}],
2917
- [["td","th"] , {"padding":"4px"}],
2918
- [["div","body","button","table","input"] , {"border-radius": "2px"}]
2919
- // ["div", {"padding-left":"2%", "padding-right":"2%","padding-top":"1%","padding-bottom":"1%"}]
2920
- ],
2921
- }
2922
- ];
2923
- xs = bw.choice(_to(d),{
2924
- "object" : d,
2925
- // "string" : function(){}
2926
- "number" : ((d>=0) && (d<def.length))?def[d].css:def[0].css
2927
- },def[0].css);
2928
-
2929
- s= xs.map(function(y){return bw.makeCSSRule(y,{pretty:false});}).join("\n");
2930
- if (appendToHead != false) {
2931
- //var hs = document.getElementById("bw-simple-theme-styles");
2932
- var hs = bw.DOM("bw-simple-theme-styles");
2933
- if (hs.length == 0) {// first time
2934
- //var h = document.getElementsByTagName("head")[0];
2935
- var h = bw.DOM("head")[0];
2936
- var el = document.createElement("style");
2937
- el.id = "bw-simple-theme-styles";
2938
- el.textContent = s; //note IE8 requires .text=
2939
- h.appendChild(el);
2940
- }
2941
- else { // replace it
2942
- hs.textContent = s; //note IE8 requires .text=
2943
- }
2944
-
2945
- }
2946
-
2947
- return s;
2948
- };
2949
-
2950
- // =============================================================================================
2951
- bw.selectTabContent = function (item, target) {
2952
- /**
2953
- This function is used inside a tab block to show the appropriate content. Note that this is
2954
- designed to work even if code is emitted as document.getElementById("myTabs").innerHTML = <<generated code..>>
2955
- or statically written by the programmer.
2956
-
2957
- note that DOM IDs are not required as selectTabContent() uses DOM path relative logic internally
2958
-
2959
- <div class="bw-tab-container"> <!-- bw-tab-container -- bw-tab-items (array of items), bw-tab-content (array of content to show) -->
2960
- <ul class="bw-tab-item-list"> <!-- container for the tabs -->
2961
- <li class="bw-tab userTab bw-tab-active" onclick="bw.selectTabContent(this)" >Tab 1</li>
2962
- <li class="bw-tab userTab " onclick="bw.selectTabContent(this)" >Tab 2</li>
2963
- <li class="bw-tab userTab " onclick="bw.selectTabContent(this)" >Tab 3</li>
2964
- <li class="bw-tab userTab " onclick="bw.selectTabContent(this)" >Tab 4</li>
2965
- </ul>
2966
- <div class="bw-tab-content-list"> <!-- container for the tab content -->
2967
- <div class="bw-tab-content bw-show" >coontent area 1 </div> <!-- bw-show picks which tab to make active at first -->
2968
- <div class="bw-tab-content" >content area 2</div>
2969
- <div class="bw-tab-content" >content area 3</div>
2970
- <div class="bw-tab-content" >content area 4</div>
2971
- </div> <!-- end of tab content sect -->
2972
- </div>
2973
- */
2974
- //if (_to(item)=="string")
2975
- // item = document.getElementById(item);
2976
- item = bw.DOM(item)[0];
2977
-
2978
- if (_to(item).substr(0,4) != "html")
2979
- return false; //unable to set tab content
2980
- document.gx=item;
2981
- var i,j,index=0;
2982
- var cols = item.parentNode.getElementsByTagName("li");
2983
- //update which tab selected
2984
- for (i=0; i< cols.length; i++) {
2985
- if (cols[i] == item) { // selected tab logic
2986
- index = i;
2987
- cols[i].className = bw.classStrAddDel(cols[i].className,"bw-tab-active");
2988
- }
2989
- else { // unselected tab logic
2990
- cols[i].className = bw.classStrAddDel(cols[i].className,"","bw-tab-active");
2991
- }
2992
- }
2993
- //console.log(item);
2994
- var tcols=[];// = item.parentNode.parentNode.getElementsByClassName("bw-tab-content-list")[0].getElementsByClassName("bw-tab-content");
2995
-
2996
-
2997
- for (i=0; i<item.parentNode.parentNode.children.length; i++) {
2998
- if (item.parentNode.parentNode.children[i].className.trim().split(/\s+/).indexOf("bw-tab-content-list")>=0) {
2999
- //were in the right child...
3000
- for (j=0; j<item.parentNode.parentNode.children[i].children.length;j++) {
3001
- if (item.parentNode.parentNode.children[i].children[j].className.trim().split(/\s+/).indexOf("bw-tab-content") >=0)
3002
- tcols.push(item.parentNode.parentNode.children[i].children[j]);
3003
- }
3004
- }
3005
-
3006
- }
3007
-
3008
- if (tcols.length <= 0)
3009
- return false;
3010
-
3011
- target = (_to(target) == "undefined") ? tcols[index] : target; //we will infer it by the tab index
3012
- target = (_to(target) == "string" ) ? bw.DOM(target)[0] : target; // we hav an ID so we'll use that
3013
-
3014
- for (i=0; i < tcols.length; i++) {
3015
- if (tcols[i] == target)
3016
- tcols[i].className = bw.classStrAddDel(tcols[i].className,"bw-show");
3017
- //bw.DOMClass(tcols[i],"bw-show","bw-show"); //tcols[i].style.display = "block";
3018
- else
3019
- tcols[i].className = bw.classStrAddDel(tcols[i].className,"","bw-show");
3020
- //bw.DOMClass(tcols[i],"bw-show","");//tcols[i].style.display = "none";
3021
-
3022
- }
3023
- return true;
3024
- };
3025
-
3026
- // =============================================================================================
3027
-
3028
- bw.DOMClass = function(el, key, replace) {
3029
- /**
3030
- bw.DOMClass(el,value)
3031
-
3032
- returns whether a specific DOM element class name (key) is set on atleast one the supplied element(s).
3033
-
3034
- If replace is supplied then the class name (key) is replaced or added if it doesn't exist.
3035
- 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
3036
-
3037
- el must be valid element or CSS selector
3038
-
3039
- markElement is used by bw UI toggles
3040
- */
3041
- var r = false, elems, x,j;
3042
- elems = bw.DOM(el);
3043
- if (elems.length <=0 )
3044
- return r;
3045
-
3046
- for (j=0; j< elems.length; j++) {
3047
- x = elems[j];
3048
- try {
3049
-
3050
- var c = x.className.split(/[ ]+/);
3051
- var i = c.indexOf(key);
3052
-
3053
- if (i >= 0) // found key
3054
- r = true;
3055
-
3056
-
3057
- if ((_to(replace) == "string") && (c.indexOf(replace)== -1)){
3058
- if (i == -1) //key not found
3059
- c.push(replace);
3060
- else {
3061
- if (replace.length > 0)
3062
- c[i]=replace;
3063
- else
3064
- c.splice(i,1);
3065
- }
3066
- x.className = c.join(" ").trim();
3067
- r = true;
3068
- // element.className = element.className.replace(/\bmystyle\b/g, "");
3069
- }
3070
- /*
3071
- var c = x.className;
3072
- x.className = bw.classStrAddDel(c,key,replace);
3073
- r=true;
3074
- */
3075
- }
3076
- catch(e) { bw.log(e);}
3077
- }
3078
- return r;
3079
- };
3080
-
3081
- // =============================================================================================
3082
- bw.DOMClassToggle = function(el,className) {
3083
- /**
3084
- bw.DOMClassToggle(el,classname)
3085
- for each element specified in el (eg "#id", ".myClass", "h2", <DOM OBJECT>) toggle className.
3086
-
3087
- If className is present on the object then it is removed. if it is not present it is added.
3088
-
3089
-
3090
- classNames with spaces or tabs are not valid and result in undefined behavior.
3091
-
3092
- returns last element current toggle state.
3093
- */
3094
- var x,i,elems = bw.DOM(el), r=false;
3095
- for (i=0; i< elems.length; i++) {
3096
- x=elems[i];
3097
- try {
3098
- r = bw.DOMClass(x,className);
3099
- if (r)
3100
- bw.DOMClass(x,className,"");
3101
- else
3102
- bw.DOMClass(x,className,className);
3103
-
3104
- } catch(e) { bw.log(e); }
3105
- }
3106
- return !r;
3107
- };
3108
- // =============================================================================================
3109
- bw.version = function() {
3110
- /**
3111
- @method version() - bitwrench runtime version & license info.
3112
-
3113
- */
3114
- var v = {
3115
- "version" : "1.2.16",
3116
- "about" : "bitwrench is a simple library of miscellaneous Javascript helper functions for common web design tasks.",
3117
- "copy" : "(c) M A Chatterjee deftio (at) deftio (dot) com",
3118
- "url" : "http://github.com/deftio/bitwrench",
3119
- "license" : "BSD-2-Clause"
3120
- };
3121
- return v;
3122
- };
3123
-
3124
- // ==============================================================================================
3125
- /**
3126
- command line handling
3127
-
3128
- this can be done via URL e.g. myPage.com?bw-load-styles=true
3129
-
3130
- or via script tag
3131
- <script type="text/javascript" src="./path/to/bitwrenchjs" bwargs="bw-load-styles=true"></script>
3132
-
3133
- */
3134
- bw.bwargs = {enableUJURLArgs : "true"}; // the arguments are exported so one can see them as a simple dict
3135
-
3136
- var parseArgs = function(s) {
3137
- var args = {};
3138
- if ((typeof s == "string") && (s!= "")) {
3139
- s=s.split(";");
3140
- var j;
3141
- for (j in s) {
3142
- var k = s[j].split(":");
3143
- args[k[0]]=k[1];
3144
- }
3145
- }
3146
- return args;
3147
- };
3148
-
3149
- var getArgs = function () {
3150
- if(bw.isNodeJS()==false) { // in browser
3151
- //var els = document.getElementsByTagName("script"); // array of script elements
3152
- var els = bw.DOM("script");
3153
- var i,a,b;
3154
- for (i in els) {
3155
- try {
3156
- // bw.log(_args[i]);
3157
- var el = els[i]; //
3158
- if (el.hasOwnProperty("src") != false)
3159
- break;
3160
-
3161
- var s = String(el.getAttribute("src"));
3162
- var f = "bitwrench.js";
3163
-
3164
- if (s.toLocaleLowerCase().substring(s.length-f.length,s.length) == f.toLocaleLowerCase()) {
3165
- s = _to(s) == "string" ? el.getAttribute("bwargs") : [""];
3166
- s = _to(s) == "string" ? el.getAttribute("data-bwargs") : s; //the html4/5 standard way
3167
- a = parseArgs(s);
3168
- for (b in a)
3169
- bw.bwargs[b]=a[b];
3170
- }
3171
-
3172
- } catch (e) {
3173
- //bw.log(String(["err 1418",i,e]));
3174
- }
3175
- }
3176
-
3177
- //pick up from URL
3178
- if(bw.bwargs["enableUJURLArgs"] == "true") { //note string literal "true"
3179
- //note in the script tag you can disable ?bwload=foo:bar; params with this
3180
- //<script type="text/javascript" src="./path/to/bitwrench.js" bwargs="enableURLConfig:false"></script>
3181
- a = parseArgs(bw.getURLParam("bwargs",""));
3182
- for (b in a)
3183
- bw.bwargs[b]=a[b];
3184
- }
3185
- }
3186
- };
3187
-
3188
-
3189
- // ==============================================================================================
3190
- // ==============================================================================================
3191
- // ==============================================================================================
3192
- //internally used function declarations:
3193
-
3194
-
3195
- getArgs();
3196
-
3197
- // do command line stuff
3198
- var loadStyles = bw.bwargs["bw-load-styles"]!="false";
3199
- var loadStyleBasics = bw.typeAssign(bw.bwargs["bw-load-style-basics"],"string", bw.bwargs["bw-load-style-basics"], "load");
3200
- bw.CSSSimpleStyles(loadStyles,{"globals":loadStyleBasics}); // append to head the bitwrench css styles by default
3201
-
3202
- bw.funcRegister(bw.log,"bw_log"); // this is globally registered for debugging purposes, it will never get called though unless programmer does this explicitly.
3203
-
3204
- return bw;
3205
-
3206
- })();
3207
- export default bw;