3 <bindings id="openils_bindings"
4 xmlns="http://www.mozilla.org/xbl"
5 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
6 xmlns:xbl="http://www.mozilla.org/xbl">
13 <xul:button label="Help" accesskey="H" xbl:inherits="label,accesskey" />
21 // This widget will try to load help content from various static and dynamic URL's, stopping at the
22 // first one that it finds. Given an example location.href of '/xul/server/patron/display.xul' and
23 // a @src of 'foo.html', it will check these URL's in this order:
26 // /xul/server/patron/display.xul.custom_help.html
27 // /xul/server/patron/display.xul.help.html
28 // /xul/server/patron/custom_help.html
29 // /xul/server/patron/help.html
30 // /xul/server/custom_help.html
31 // /xul/server/help.html
32 // /xul/custom_help.html
37 // If @pathname is specified, it will override the value from location.pathname
38 // If @hostname is specified, it will override the value from location.hostname
39 // If @port is specified, it will override the value from location.port
40 // If @protocol is specified, it will override the value from location.protocol
42 this.addEventListener( 'command', function(ev) { this._open_window(); }, false);
44 alert('Error constructing help widget in bindings.xml: ' + E);
50 <method name="_open_window">
56 var protocol = obj.getAttribute('protocol') || location.protocol;
57 var hostname = obj.getAttribute('hostname') || location.hostname;
58 var port = obj.getAttribute('port') || location.port;
59 var pathname = obj.getAttribute('pathname') || location.pathname;
61 function test_url(loc) {
62 if (loc == '/' || !loc) { return false; }
63 var x = new XMLHttpRequest();
64 var url = protocol + '//' + hostname + ( port ? ':' + port : '' ) + loc;
65 dump('help widget: testing <'+url+'>\n');
66 x.open("HEAD", url, false);
68 return x.status == 200;
71 function open_url(loc) {
72 var url = protocol + '//' + hostname + ( port ? ':' + port : '' ) + loc +'?id='+obj.id;
73 dump('help widget: opening <'+url+'>\n');
74 window.open(url, obj.getAttribute('label'), 'resizable,scrollbars');
77 if (test_url('/'+this.getAttribute('src'))) {
78 open_url('/'+this.getAttribute('src'));
80 if (test_url(pathname + '.custom_help.html')) {
81 open_url(pathname + '.custom_help.html');
83 if (test_url(pathname + '.help.html')) {
84 open_url(pathname + '.help.html');
86 var pathparts = pathname.split('/');
87 var base_url; var url; var test_result;
88 for (var i = pathparts.length - 2; i>0 && !test_result; i--) {
89 base_url = ''; url = '';
90 for ( j = 1; j <= i; j++ ) { base_url += '/' + pathparts[j]; };
91 url = base_url + '/custom_help.html';
92 test_result = test_url(url);
94 url = base_url + '/help.html';
95 test_result = test_url(url);
101 if (test_url("/custom_help.html")) {
102 open_url("/custom_help.html");
104 if (test_url("/help.html")) {
105 open_url("/help.html");
108 alert('No Help Found');
116 alert('Error opening window in help widget in bindings.xml: ' + E);
127 <binding id="messagecatalog">
140 this._load_sprintf();
142 this._load_from_src();
144 alert('Error constructing messagecatalog in bindings.xml: ' + E);
150 <property name="src">
154 return this.getAttribute('src');
156 alert('Error getting @src in messagecatalog in bindings.xml: ' + E);
164 this.setAttribute('src',val);
165 this.load_from_src();
168 alert('Error setting @src in messagecatalog in bindings.xml: ' + E);
175 <method name="_load_from_src">
179 var x = new XMLHttpRequest();
180 x.open("GET",this.src,false);
182 if ((x.status == 0 || x.status == 200) && x.responseText) {
183 var props = this._props2object(x.responseText);
184 for (var i in props) {
185 this._props[i] = props[i];
188 var msg = "messageCatalog: No text from " + this.src;
189 alert(msg); throw(msg);
192 try { // fail silently if no custom properties file exists
193 var custom_src = String(this.src).replace(/\.properties$/,'_custom.properties');
194 var x2 = new XMLHttpRequest();
195 x2.open("GET",custom_src,false);
197 if ((x2.status == 0 || x2.status == 200) && x2.responseText) {
198 var props = this._props2object(x2.responseText);
199 for (var i in props) {
200 this._props[i] = props[i];
206 alert('Error loading properties in messagecatalog in bindings.xml: ' + E);
213 <method name="_props2object">
214 <parameter name="str"/>
218 var lines = str.split("\n");
221 var in_comment = false;
223 for (var l in lines) {
226 // handle multi-line comments
227 if (line.indexOf('/*') >= 0) in_comment = true;
229 if (in_comment && line.indexOf('*/') > 0) {
230 var comment_start = line.indexOf('/*');
231 var comment_end = line.indexOf('*/');
232 line = line.substring(0, comment_start) + line.substring(0, comment_end + 2);
234 } else if (in_comment) continue;
236 // get rid of entire-line comments
237 if (line.indexOf('#') == 0) {
242 // handle end-of-line comments
243 var end_comment = line.indexOf('//');
244 if (end_comment >= 0) line = line.substring(0, end_comment);
246 // and line concatenation
247 if (line.charAt(line.length - 1) == '\\') {
248 line = line.substring(0,line.length - 1);
252 var eq_pos = line.indexOf('=');
253 if (eq_pos < 0) continue;
255 var k = line.substring(0,eq_pos);
256 k = k.replace(/\s+/g,"");
258 var v = line.substring(eq_pos + 1);
263 if (v.indexOf( "{" + current_m + "}" ) >= 0 ) {
264 var mes_bund = new RegExp( "\\\{" + current_m + "\\\}", 'g' );
265 var sprintf_format = "%" + (current_m + 1) + "$s";
267 v = v.replace( mes_bund, sprintf_format );
274 } while ( cont == true );
282 alert('Error in props2object in messagecatalog in bindings.xml: ' + E);
289 <method name="testString">
290 <parameter name="key"/>
294 var str = this._props[key];
295 return (typeof str != 'undefined');
304 <method name="getString">
305 <parameter name="key"/>
309 var str = this._props[key];
310 if (typeof str == 'undefined') throw(str);
313 alert("*** Failed to get string " + key + " in bundle: " + this.src + "\n" + e);
320 <method name="getFormattedString">
321 <parameter name="key"/>
322 <parameter name="params"/>
326 var str = this._props[key];
327 if (typeof str == 'undefined') throw(str);
328 var these = [ str ].concat(params);
329 var v = this.sprintf.apply(this,these);
331 // replace unicode escapes
334 /\\u([0-9a-f]{4})/gi,
335 function (r,s) { return String.fromCharCode(s); }
341 alert("*** Failed to get string " + key + " in bundle: " + this.src + "\n");
348 <method name="_load_sprintf">
352 this.sprintf = function() {
353 // FIXME - is the following license GPL-compatible? seems equivalent to the public domain
355 * JavaScript printf/sprintf functions.
357 * This code is unrestricted: you are free to use it however you like.
359 * The functions should work as expected, performing left or right alignment,
360 * truncating strings, outputting numbers with a required precision etc.
362 * For complex cases, these functions follow the Perl implementations of
363 * (s)printf, allowing arguments to be passed out-of-order, and to set the
364 * precision or length of the output based on arguments instead of fixed
367 * See http://perldoc.perl.org/functions/sprintf.html for more information.
370 * - zero and space-padding
371 * - right and left-alignment,
372 * - base X prefix (binary, octal and hex)
373 * - positive number prefix
375 * - precision / truncation / maximum width
376 * - out of order arguments
378 * Not implemented (yet):
380 * - size (bytes, words, long-words etc.)
382 * Will not implement:
383 * - %n or %p (no pass-by-reference in JavaScript)
385 * @version 2007.04.27
389 function pad(str, len, chr, leftJustify) {
390 var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
391 return leftJustify ? str + padding : padding + str;
395 function justify(value, prefix, leftJustify, minWidth, zeroPad) {
396 var diff = minWidth - value.length;
398 if (leftJustify || !zeroPad) {
399 value = pad(value, minWidth, ' ', leftJustify);
401 value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
407 function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
408 // Note: casts negative numbers to positive ones
409 var number = value >>> 0;
410 prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
411 value = prefix + pad(number.toString(base), precision || 0, '0', false);
412 return justify(value, prefix, leftJustify, minWidth, zeroPad);
415 function formatString(value, leftJustify, minWidth, precision, zeroPad) {
416 if (precision != null) {
417 value = value.slice(0, precision);
419 return justify(value, '', leftJustify, minWidth, zeroPad);
422 var a = arguments, i = 0, format = a[i++];
423 return format.replace(/%%|%(\d+\$)?([-+#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEGS])/g, function(substring, valueIndex, flags, minWidth, _, precision, type) {
424 if (substring == '%%') return '%';
427 var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false;
428 for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
429 case ' ': positivePrefix = ' '; break;
430 case '+': positivePrefix = '+'; break;
431 case '-': leftJustify = true; break;
432 case '0': zeroPad = true; break;
433 case '#': prefixBaseX = true; break;
436 // parameters may be null, undefined, empty-string or real valued
437 // we want to ignore null, undefined and empty-string values
441 } else if (minWidth == '*') {
443 } else if (minWidth.charAt(0) == '*') {
444 minWidth = +a[minWidth.slice(1, -1)];
446 minWidth = +minWidth;
449 // Note: undocumented perl feature:
451 minWidth = -minWidth;
455 if (!isFinite(minWidth)) {
456 throw new Error('sprintf: (minimum-)width must be finite');
460 precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
461 } else if (precision == '*') {
463 } else if (precision.charAt(0) == '*') {
464 precision = +a[precision.slice(1, -1)];
466 precision = +precision;
469 // grab value using valueIndex if required?
470 var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
474 case 's': return formatString(String(value), leftJustify, minWidth, precision, zeroPad);
475 case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
476 case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
477 case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
478 case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
479 case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
480 case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
483 var number = parseInt(+value);
484 var prefix = number < 0 ? '-' : positivePrefix;
485 value = prefix + pad(String(Math.abs(number)), precision, '0', false);
486 return justify(value, prefix, leftJustify, minWidth, zeroPad);
496 var prefix = number < 0 ? '-' : positivePrefix;
497 var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
498 var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
499 value = prefix + Math.abs(number)[method](precision);
500 return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
502 default: return substring;
507 alert("*** Failed to load sprintf library: " + e + "\n");
516 <binding id="caption" extends="chrome://global/content/bindings/general.xml#basetext">
518 <stylesheet src="chrome://global/skin/groupbox.css"/>
525 var n = document.getAnonymousNodes(parentNode.parentNode)[1];
528 this.setAttribute('src','chrome://open_ils_staff_client/skin/media/images/opentriangle.gif');
531 this.setAttribute('src','chrome://open_ils_staff_client/skin/media/images/triangle.gif');
535 <xul:image class="caption-icon" xbl:inherits="src=image"/>
536 <xul:label class="caption-text" flex="1" xbl:inherits="default,value=label,crop,accesskey" />
543 var n = document.getAnonymousNodes(this)[0];
544 n.setAttribute('src','chrome://open_ils_staff_client/skin/media/images/opentriangle.gif');