/* Author: Josh Lamb * File: app.js * Description: General functions and classes for use with the application. Use anything you find useful */ var app = {}; app.ie_ver = function() { // Returns the version of Internet Explorer or a -1 // (indicating the use of another browser). var rv = -1; // Return value assumes failure. if(navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if(re.exec(ua) != null) rv = parseFloat( RegExp.$1 ); } return rv; } app.msg = function(html, title, callback) { // Manages display of the message box and then calls the callback when the dialog is closed var default_callback = function() {}; var ttl = title ? title : 'Message'; var fn = (typeof(callback) == 'function') ? callback : default_callback; var d = $('#msg_dialog'); d.dialog('option', 'title', ttl) .dialog('option', 'buttons', {Okay: function() { $(this).dialog('close'); fn(); }}) .find('span').html(html).end() .dialog('open'); }, app.confirm = function(html, title, callback_ok, callback_cancel) { // Manages display of the message box and then calls the callback when the dialog is closed var default_callback = function() {}; var ttl = title ? title : 'Please Confirm'; var ok = (typeof(callback_ok) == 'function') ? callback_ok : default_callback; var cancel = (typeof(callback_cancel) == 'function') ? callback_cancel : default_callback; var d = $('#confirm_dialog'); d.dialog('option', 'title', ttl) .dialog('option', 'buttons', { Cancel: function() { $(this).dialog('close'); cancel(); }, Okay: function() { $(this).dialog('close'); ok(); } }) .find('span').html(html).end() .dialog('open'); }; app.error_remove = function($input) { $input.removeClass('error'); $input.parents('.innerformrow').find('.inputmessage').fadeOut('slow'); }; app.closeme = function() { // Shortcut for making a close function for dialogs this.dialog('close'); } app.navmenu = function(o) { /** * @prop menus : array * @desc An array of objects listing elements for each menu/submenu pair, whether they are expanded and the times at which * they should be collapsed */ this.menus = {}; /** * @prop timeout_id : integer * @desc The id of the timeout process that checks to see if a submenu should be hidden. Do not toggle this unless * you want to affect the handling of menu behavior. */ this.timeout_id = false; /** * @prop delayTime : integer * @desc Number of milliseconds between when the mouse leaves a menu and when that menu should disappear. This number is * actually a minimum: The time between when the menu was first left and when the menu should disappear is checked * at a frequency determined using the refreshInterval; only after the minimum duration has passed will the menu * disappear. */ this.delayTime = o.delayTime ? o.delayTime : 500; /** * @prop refreshInterval : integer * @desc Number of milliseconds between checks on all submenus to see which ones are visible and which ones should be * hidden. */ this.refreshInterval = o.refreshInterval ? o.refreshInterval : 250; /** * @prop menu_id : string * @desc ID of the unordered list (ul element) to be used as the menu header */ this.menu_id = o.menu_id; /** * @constr dropdown_menu * @desc Creates a dropdown_menu object, assigning the appropriate behavior functionality to * menus and submenus identified in the id_list variable. The constructor is loaded into * the sgx.actions.DocLoad action list when the object is created, allowing for it to be * executed only when the document is loaded. * * @param id_list : array * @desc An array of the following form: * @ex [ * {'menuid': , 'submenuid': }, * etc... * ]; */ this.construct = function() { var menu_id = this.menu_id; var m = this.menus = {}; var i = 0; var t = this; $('#' + menu_id + ' > li').each(function() { var hdr = $(this); var i = hdr.get(0).id; var j = '\\+' + i; var submenu = $('#' + j); m[i] = { 'header': hdr, 'sub': submenu.length ? submenu : false, 'hide_tm': 0, 'collapsed': true, 'hover': false }; // Assign position and behavior to submenus if they are present if(submenu.length) { submenu.css('left', hdr.offset().left); submenu.mouseover(function() {hdr.trigger('mouseover')}); submenu.mouseout(function() {hdr.trigger('mouseout')}); } // Assign behavior to headers hdr.mouseover(function() { t.enter(i) }); hdr.mouseout(function() { t.exit(i) }); }); } /** * @func enter * @desc Behavior for when the mouse enters a menu heading or its corresponding submenu. * The hover state for the current menu is set to true, and then the refresh function * is called. * @param el : object * @desc The element that the mouse entered. This element has an sgx_menu_id property that * is assigned by the constructor, which relates it to the menus property. */ this.enter = function(menu_id) { // Store the time the hover began and the hover state true, then refresh the render this.menus[menu_id].hover = true; this.menus[menu_id].hide_tm = 0; this.refresh(); }; /** * @func exit * @desc Behavior for when the mouse leaves a menu heading or its corresponding submenu. * The hover state for the current menu is set to false, and the time at which the * menu should be hidden is calculated. Finally the refresh function is called. * @param el : object * @desc The element that the mouse left. This element has an sgx_menu_id property that * is assigned by the constructor, which relates it to the menus property. */ this.exit = function(menu_id) { this.menus[menu_id].hover = false; this.menus[menu_id].hide_tm = sgx.time.getTime(false) + this.delayTime; this.refresh(); }; /** * @func show * @desc Displays a submenu defined by the supplied menu_ar variable * @param menu_ar : object * @desc A member of the menus property */ this.show = function(menu_ar) { if (menu_ar['sub'] !== false) { menu_ar.hover = true; menu_ar['sub'].css('display', 'block'); } }; /** * @func hide * @desc Hides a submenu defined by the supplied menu_ar variable * @param menu_ar : object * @desc A member of the menus property */ this.hide = function(menu_ar) { if (menu_ar['sub'] !== false) { menu_ar['sub'].css('display', 'none'); } } /** * @func refresh * @desc This function begins running at an defined by the refreshInterval property after the mouse * has exited a menu header or submenu body. It begins by clearing any previously queued refresh() * call in case it was initiated by the exit() function (rather than by itself using a call to * setTimeout()). * * It then searches if any objects are currently being hovered over. If a new menu is being * hovered over, it shows this submenu and immediately hides all other menus. Otherwise, * it checks to see the time between when the last menu was left and when it should be hidden. * The time it should be hidden at is calculated based on the delayTime property (x milleseconds * after the menu is exited). * * If the time when it should be hidden is reached, the hide() function is called for the menu * to be hidden. Otherwise, another refresh() call is scheduled using the refreshInterval property * as the delay. */ this.refresh = function() { var i; var tm = sgx.time.getTime(false); var diff_tm; var cur; var left_hides = 0; var len = this.menus.length; var found_hover = false; // Clear previous timeouts if (this.timeout_id !== false) { clearTimeout(this.timeout_id); } // Find if any objects are currently being hovered over for (i in this.menus) { cur = this.menus[i]; if (cur.hover) { this.show(cur); found_hover = true; } } for (i in this.menus) { cur = this.menus[i]; if ((cur.submenu !== false) && (!cur.hover)) { diff_tm = cur.hide_tm - tm; if ((diff_tm <= 0) && (found_hover === false)) { this.hide(cur); } else if(found_hover === true) { this.hide(cur); } else { left_hides++; } } } if (left_hides) { var t = this; this.timeout_id = setTimeout(function() {t.refresh()}, this.refreshInterval); } else { this.timeout_id = false; } }; this.construct(); }; app.quickstart = function(o) { this.ids = { 'f': o.footer_id, 'm': o.msg_id, 'b': o.button_id, 'i': o.image_id }; this.state = -1; this.init = function() { var t = this; var x = $('#' + this.ids.b); x.mouseover(function() {t.hover()}); x.mouseout(function() {t.no_hover()}); x.click(function() {t.click()}); }; this.hover = function() { var img = $('#' + this.ids.i); $('#' + this.ids.b).addClass('hover'); if (this.state == -1) { img.src = '../app/images/arrow/qsl1.png'; } else { img.src = '../app/images/arrow/qsr1.png'; } } this.no_hover = function() { var img = $('#' + this.ids.i); $('#' + this.ids.b).removeClass('hover'); if (this.state == -1) { img.src = '../app/images/arrow/qsl0.png'; } else { img.src = '../app/images/arrow/qsr0.png'; } } this.click = function() { var el = $('#' + this.ids.f); this.state = -this.state; if (this.state == -1) { el.removeClass('bord-t'); el.removeClass('show-footer'); el.addClass('hide-footer'); } else { el.addClass('bord-t'); el.removeClass('hide-footer'); el.addClass('show-footer'); } this.hover(); } this.init(); } // Storage locations for data and url information if needed app.data = {}; app.url = {}; app.get_table_strings = function() { var v; var k; var r = { errors_db_records: 'cmp_table_db_error', no_records: 'cmp_table_no_records', first: 'cmp_table_first', previous: 'cmp_table_previous', last: 'cmp_table_last', next: 'cmp_table_next', records: 'cmp_table_records' }; if(typeof(app.data.lang) == 'object') { var l = app.data.lang; for(var k in r) { v = r[k]; if(typeof(l[v]) == 'string') { r[k] = l[v]; } } } return r; }; app.post = function(o) { if((typeof(o.url) == 'undefined') && (typeof(app.url.ajax) != 'undefined')) { // Default value for o.url is the value of app.url.ajax, so long as it is defined o.url = app.url.ajax; } $.ajax({ url: o.url, type: 'POST', data: o.data, dataType: 'json', error: o.error, success: o.success }); }; app.loading = {}; app.loading.dlg = ''; app.loading.active = false; app.loading.timeout = 0; app.loading.animate = function() { var $x = $('#' + app.loading.dlg).find('span'); if($x.text().length > 2) { $x.text(''); } else { $x.text($x.text() + '.'); } app.loading.timeout = setTimeout(app.loading.animate, 250); }; app.loading.toggle = function() { if(app.loading.dlg == '') { app.loading.dlg = sgx.generate_id(); $('body').append('
Please Wait
'); $('#' + app.loading.dlg).dialog({ width: 200, height: 120, modal: true, draggable: false, resizable: false, autoOpen: false, beforeClose: function() { if(app.loading.active) { return false; } } }); } if(app.loading.active) { clearTimeout(app.loading.timeout); app.loading.active = false; $('#' + app.loading.dlg).dialog('close'); } else { app.loading.active = true; $('#' + app.loading.dlg).dialog('open'); app.loading.animate(); } }; (function(app) { var re = { not_string: /[^s]/, not_bool: /[^t]/, not_type: /[^T]/, not_primitive: /[^v]/, number: /[diefg]/, numeric_arg: /[bcdiefguxX]/, json: /[j]/, not_json: /[^j]/, text: /^[^\x25]+/, modulo: /^\x25{2}/, placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/, key: /^([a-z_][a-z_\d]*)/i, key_access: /^\.([a-z_][a-z_\d]*)/i, index_access: /^\[(\d+)\]/, sign: /^[\+\-]/ } function sprintf(key) { // `arguments` is not an array, but should be fine for this call return sprintf_format(sprintf_parse(key), arguments) } function vsprintf(fmt, argv) { return sprintf.apply(null, [fmt].concat(argv || [])) } function sprintf_format(parse_tree, argv) { var cursor = 1, tree_length = parse_tree.length, arg, output = '', i, k, ph, pad, pad_character, pad_length, is_positive, sign for (i = 0; i < tree_length; i++) { if (typeof parse_tree[i] === 'string') { output += parse_tree[i] } else if (typeof parse_tree[i] === 'object') { ph = parse_tree[i] // convenience purposes only if (ph.keys) { // keyword argument arg = argv[cursor] for (k = 0; k < ph.keys.length; k++) { if (arg == undefined) { throw new Error(sprintf('[sprintf] Cannot access property "%s" of undefined value "%s"', ph.keys[k], ph.keys[k-1])) } arg = arg[ph.keys[k]] } } else if (ph.param_no) { // positional argument (explicit) arg = argv[ph.param_no] } else { // positional argument (implicit) arg = argv[cursor++] } if (re.not_type.test(ph.type) && re.not_primitive.test(ph.type) && arg instanceof Function) { arg = arg() } if (re.numeric_arg.test(ph.type) && (typeof arg !== 'number' && isNaN(arg))) { throw new TypeError(sprintf('[sprintf] expecting number but found %T', arg)) } if (re.number.test(ph.type)) { is_positive = arg >= 0 } switch (ph.type) { case 'b': arg = parseInt(arg, 10).toString(2) break case 'c': arg = String.fromCharCode(parseInt(arg, 10)) break case 'd': case 'i': arg = parseInt(arg, 10) break case 'j': arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0) break case 'e': arg = ph.precision ? parseFloat(arg).toExponential(ph.precision) : parseFloat(arg).toExponential() break case 'f': arg = ph.precision ? parseFloat(arg).toFixed(ph.precision) : parseFloat(arg) break case 'g': arg = ph.precision ? String(Number(arg.toPrecision(ph.precision))) : parseFloat(arg) break case 'o': arg = (parseInt(arg, 10) >>> 0).toString(8) break case 's': arg = String(arg) arg = (ph.precision ? arg.substring(0, ph.precision) : arg) break case 't': arg = String(!!arg) arg = (ph.precision ? arg.substring(0, ph.precision) : arg) break case 'T': arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase() arg = (ph.precision ? arg.substring(0, ph.precision) : arg) break case 'u': arg = parseInt(arg, 10) >>> 0 break case 'v': arg = arg.valueOf() arg = (ph.precision ? arg.substring(0, ph.precision) : arg) break case 'x': arg = (parseInt(arg, 10) >>> 0).toString(16) break case 'X': arg = (parseInt(arg, 10) >>> 0).toString(16).toUpperCase() break } if (re.json.test(ph.type)) { output += arg } else { if (re.number.test(ph.type) && (!is_positive || ph.sign)) { sign = is_positive ? '+' : '-' arg = arg.toString().replace(re.sign, '') } else { sign = '' } pad_character = ph.pad_char ? ph.pad_char === '0' ? '0' : ph.pad_char.charAt(1) : ' ' pad_length = ph.width - (sign + arg).length pad = ph.width ? (pad_length > 0 ? pad_character.repeat(pad_length) : '') : '' output += ph.align ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg) } } } return output } var sprintf_cache = Object.create(null) function sprintf_parse(fmt) { if (sprintf_cache[fmt]) { return sprintf_cache[fmt] } var _fmt = fmt, match, parse_tree = [], arg_names = 0 while (_fmt) { if ((match = re.text.exec(_fmt)) !== null) { parse_tree.push(match[0]) } else if ((match = re.modulo.exec(_fmt)) !== null) { parse_tree.push('%') } else if ((match = re.placeholder.exec(_fmt)) !== null) { if (match[2]) { arg_names |= 1 var field_list = [], replacement_field = match[2], field_match = [] if ((field_match = re.key.exec(replacement_field)) !== null) { field_list.push(field_match[1]) while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { if ((field_match = re.key_access.exec(replacement_field)) !== null) { field_list.push(field_match[1]) } else if ((field_match = re.index_access.exec(replacement_field)) !== null) { field_list.push(field_match[1]) } else { throw new SyntaxError('[sprintf] failed to parse named argument key') } } } else { throw new SyntaxError('[sprintf] failed to parse named argument key') } match[2] = field_list } else { arg_names |= 2 } if (arg_names === 3) { throw new Error('[sprintf] mixing positional and named placeholders is not (yet) supported') } parse_tree.push( { placeholder: match[0], param_no: match[1], keys: match[2], sign: match[3], pad_char: match[4], align: match[5], width: match[6], precision: match[7], type: match[8] } ) } else { throw new SyntaxError('[sprintf] unexpected placeholder') } _fmt = _fmt.substring(match[0].length) } return sprintf_cache[fmt] = parse_tree } app.sprintf = sprintf; app.vsprintf = vsprintf; })(app); app.translate = function(str, key, substitutions, desc, quiet) { key = typeof(key) == 'undefined' ? null : key; substitutions = typeof(substitutions) == 'undefined' ? null : substitutions; desc = typeof(desc) == 'undefined' ? null : desc; quiet = typeof(quiet) == 'undefined' ? true : quiet; var ajax_enabled = typeof(window.ajax_translations_enabled) == 'undefined' ? true : window.ajax_translations_enabled; var translations = typeof(window.translations) == 'undefined' ? null : window.translations; var term = str; var define_translation = function(str, key, desc) { $.ajax({ url: 'index.php?page=core.define_translation', type: 'post', data: { 'str': str, 'key': key, 'desc': desc } }); }; // If translations not provided, bypass the rest of the translate workflow if(translations !== null) { if(typeof(translations[key]) == 'undefined') { // No def/key - emit the default term and make an ajax call if(ajax_enabled) { define_translation(str, key, desc); } } else { // Key found, output the term term = translations[key].term; if(translations[key].orig != str && ajax_enabled) { // Def change? ajax call and output string define_translation(str, key, desc); } } } if(substitutions !== null) { term = app.vsprintf(term, substitutions); } if(!quiet) { document.write(term); } return term; }; app.ptranslate = function(str, key, substitutions, desc) { key = typeof(key) == 'undefined' ? null : key; substitutions = typeof(substitutions) == 'undefined' ? null : substitutions; desc = typeof(desc) == 'undefined' ? null : desc; var quiet = false; return app.translate(str, key, substitutions, desc, quiet); }; var __ = app.translate; var _p = app.ptranslate; var roster = app;