// this file provides shared javascript functions for all interface functions // return a new json data store // url - url to fetch data from // fields - array of field names to use from url response function newStore(url, fields) { return new Ext.data.JsonStore({ url: url, root: 'results', fields: fields }); } // generate a extjs grid. a checkbox column, a double click handler and 'Add' and 'Delete' buttons are added automatically. // store - data store to use for grid content // columns - array of column definitions to use // dblclickUrl - url to hit when a row is double clicked. the id of the record (not column) is appended to the url // addUrl - url to hit when the Add button is clicked. // deleteUrl - url to hit when the Delete button is clicked. this url will be hit once for each row with the id of the selected // record appended to it. // includeFields - additional fields from the row to include with both, dblclickUrl and deleteUrl // deleteMsg - optionally, a different message to display on delete function newGrid(store, columns, dblclickUrl, addUrl, deleteUrl, includeFields, deleteMsg, title) { if ('undefined' != typeof deleteUrl) { var sm = new Ext.grid.CheckboxSelectionModel(); columns = [sm].concat(columns); } var cm = new Ext.grid.ColumnModel(columns); cm.defaultSortable = true; cm.defaultWidth = 140; var tbAdd = { text: 'Add', handler: function() { window.location = addUrl; } }; var tbDelete = { text: 'Delete', handler: function() { var msg = 'Are you sure you want to delete the selected item(s)?'; if ('undefined' != typeof deleteMsg) { msg = deleteMsg; } Ext.MessageBox.confirm('Confirm', msg, function(btn, text) { if ('yes' == btn) { sm.getSelections().each(function(rec) { // always add id to url var url = deleteUrl + '&id=' + rec.data.id; // add any optional fields if ('undefined' != typeof includeFields) { includeFields.each(function(field, i) { if ('undefined' != typeof rec.data[field]) { url += '&' + field + '=' + rec.data.type; } }); } new Ajax.Request(url, { method: 'get', onSuccess: function(rs) { store.remove(rec); } }); }); } }); } }; var tb = []; if ('undefined' != typeof deleteUrl) { tb.push(tbDelete); } if ('undefined' != typeof addUrl) { tb.push(tbAdd); } var grid = new Ext.grid.GridPanel({ store: store, cm: cm, sm: sm, width: 800, height: 500, stripRows: true, trackMouseOver: true, title: title, //style: 'padding-bottom: 10px', // XXX this adds useless padding if contained in a frame tbar: (0 < tb.size()) ? tb : undefined }); if ('undefined' != typeof dblclickUrl) { grid.on('rowdblclick', function(grid, i, e) { var data = store.getAt(i).data; // always add id to url var url = dblclickUrl + '&id=' + data.id; // add any optional fields if ('undefined' != typeof includeFields) { includeFields.each(function(field, i) { if ('undefined' != typeof data[field]) { url += '&' + field + '=' + data.type; } }); } window.location = url; }); } return grid; } function newGridSimple(store, columns, title, dblclickUrl, style) { var cm = new Ext.grid.ColumnModel(columns); cm.defaultSortable = true; cm.defaultWidth = 140; var grid = new Ext.grid.GridPanel({ store: store, cm: cm, width: 800, height: 500, stripRows: true, trackMouseOver: true, title: title, style: style }); if ('undefined' != typeof dblclickUrl) { grid.on('rowdblclick', function(grid, i, e) { var data = store.getAt(i).data; var url = dblclickUrl + '&id=' + data.id; if ('undefined' != typeof includeFields) { includeFields.each(function(field, i) { if ('undefined' != typeof data[field]) { url += '&' + field + '=' + data.type; } }); } window.location = url; }); } return grid; } // generates a extjs form. // title - title of the form // targetUrl - target url to hit when clicking Save button // completedUrl - url to hit after targetUrl returned success = true function newForm(title, targetUrl, completedUrl, buttonName) { Ext.form.Field.prototype.msgTarget = 'under'; var form = new Ext.FormPanel({ url: targetUrl, method: 'get', frame: true, title: title, bodyStyle: 'padding: 5px 5px 0', width: 600, defaultType: 'textfield', buttons: [{ text: ('undefined' == typeof buttonName) ? 'Save' : buttonName, handler: function() { if (form.form.isValid()) { form.form.submit({ waitMsg: 'Saving...', failure: function(form, rs) { Ext.MessageBox.alert('Errors', rs.result.message); }, success: function(form, rs) { var url = completedUrl; Object.keys(rs.result).each(function(key, i) { url += '&' + key + '=' + rs.result[key]; }); window.location = url; } }); } else { Ext.MessageBox.alert('Errors', 'Please correct the errors noted.'); } } } ] }); return form; } // generates a sub menu containing links. // links - hash containing objects mapping a name to a url. Eg $H({'foo': 'bar'}) generates a link 'foo' pointing to 'bar'. // ($H() is prototype's version of a 'hash') function subMenu(links, font_size) { if ('undefined' != typeof font_size) { var submenu = new Element('div', {style: 'float: left; width: 550px; font-size: ' + font_size + ';'}); } else { var submenu = new Element('div', {style: 'float: left; width: 550px;'}); } links.each(function(data, index) { var linkClass = 'navbutton'; if (1 == links.size()) { // if only one link, use rounded edges on both sides linkClass += ' button4'; } else if (0 == index) { // if first link, use rounded left edge linkClass += ' button1'; } else if (links.size() - 1 == index) { // if last link, use rounded right edge linkClass += ' button3'; } else { // if middle, use straight edges if (12 > data[0].length) { linkClass += ' button2'; } else { linkClass += ' button2b'; } } if (window.location.search == data[1]) { linkClass += ' navactive'; } var link = new Element('a', { 'class': linkClass, href: data[1] }).update(data[0]); submenu.appendChild(link); }, links); $('submenu').appendChild(submenu); } // generates provisioning submenu. add additional entries as required function subMenuProvisioning() { subMenu($H({ 'Customers': '?a=provisioning&b=show_customers' })); } // generates monitor submenu. function subMenuMonitoring() { subMenu($H({ 'Access Points': '?a=monitoring&b=show_access_points', 'Customers': '?a=monitoring&b=show_customers', 'Network Map': '?a=monitoring&b=show_network_map', 'Network Overview': '?a=monitoring&b=show_network_overview', 'Towers': '?a=monitoring&b=show_towers' }), '10px'); } // adds additional verification types to extjs on load Ext.onReady(function() { Ext.apply(Ext.form.VTypes, { // creates 'coordinaet' vtype for use by form elements coordinate: function(value) { return /^([\d.-]+,[\d.-]+)$/.test(value); }, // message to display if coordinate() returns false coordinateText: 'Coordinates must be in XX,YY format.', coordinateMask: /[\d,.-]/, ip_address: function(value) { return /^([\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3})$/.test(value); }, ip_addressText: 'IP address must be in XXX.XXX.XXX.XXX format.', ip_addressMask: /[\d.]/, hostmask: function(value) { return /^([\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\/[\d]{1,2})$/.test(value); }, hostmaskText: 'Hostmask must be in XXX.XXX.XXX.XXX/YY format.', hostmaskMask: /[\d.//]/, account_number: function(value) { return /^([\d]{4}-[\d]{7})$/.test(value); }, account_numberText: 'Account number must be in XXXX-XXXXXXX format.', account_numberMask: /[\d-]/, zip: function(value) { return /^([\d]{5})$/.test(value); }, zipText: 'Zip code must be 5 digits.', zipMask: /[\d]/, phone_number: function(value) { return /^(\d{3}[-]?){1,2}(\d{4})$/.test(value); }, phone_numberText: 'Phone number must be in XXX-XXX-XXXX format.', phone_numberMask: /[\d-]/, mac_address: function(value) { return /^([0-9A-Fa-f]{2}:?){5}[0-9A-Fa-f]{2}$/i.test(value); }, mac_addressText: 'Please enter a valid MAC address', mac_addressMask: /[0-9A-Fa-f:]/, username: function(value) { return /^([0-9A-Z_-]+)$/i.test(value); }, usernameText: 'Username must only contain alphanumerics, - and _', usernameMask: /[0-9A-Z_-]/i }); }); function show(item) { item.enable().show(); if ('function' != typeof item.getEl() && item.getEl().up('.x-form-item')) { item.getEl().up('.x-form-item').setDisplayed(true); } if ('object' == typeof item.items) { item.items.each(function(component, i) { component.enable(); }); } } function hide(item) { item.disable().hide(); if ('undefined' != typeof item.getEl() && item.getEl().up('.x-form-item')) { item.getEl().up('.x-form-item').setDisplayed(false); } if ('object' == typeof item.items) { item.items.each(function(component, i) { component.disable(); }); } } function hideAll(items) { items.each(function(item, i) { hide(item); }); } function showAll(items) { items.each(function(item, i) { show(item); }); } // customer renderer for status column function statusRenderer(value) { if (0 == value) { return 'Offline'; } else { return 'Online'; } } function addGroupButton(grid, returnUrl) { grid.getTopToolbar().push({ xtype: 'tbseparator' }); grid.getTopToolbar().push({ text: 'Manage groups', handler: function() { var url = '?a=provisioning&b=update_group_devices&returnurl=' + escape(returnUrl) + '&devices='; var go = false; grid.getSelectionModel().getSelections().each(function(rec) { url += rec.data['table'] + "|" + rec.data['id'] + ','; go = true; }); if (go) { window.location = url.truncate(url.length - 1, ''); } } }); } function onLoad(stores, callback) { stores.each(function(store) { store.loaded = false; store.on('load', function() { this.loaded = true; var done = true; stores.each(function(store) { if (!store.loaded) { done = false; } }); if (done) { callback(); } }); }); }