From 4b2eae58b19c407b548a8503b8ff51ad6364a3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez?= Date: Wed, 20 Nov 2013 11:09:30 +0000 Subject: [PATCH] Added gui basic elements refactoring of existint javascript translations of buttons moved to better place added css class that will made button text dissapear for smaller displays --- server/src/uds/REST/methods/providers.py | 10 +- server/src/uds/REST/mixins.py | 35 ++ server/src/uds/static/adm/css/uds-admin.css | 9 +- .../js/{spreadsheet.js => api-spreadsheet.js} | 0 .../static/adm/js/{tools.js => api-tools.js} | 0 server/src/uds/static/adm/js/gui-elements.js | 10 +- server/src/uds/static/adm/js/gui.js | 160 ++++--- server/src/uds/static/adm/js/strftime.js | 392 ------------------ server/src/uds/templates/uds/admin/index.html | 14 +- 9 files changed, 171 insertions(+), 459 deletions(-) rename server/src/uds/static/adm/js/{spreadsheet.js => api-spreadsheet.js} (100%) rename server/src/uds/static/adm/js/{tools.js => api-tools.js} (100%) delete mode 100644 server/src/uds/static/adm/js/strftime.js diff --git a/server/src/uds/REST/methods/providers.py b/server/src/uds/REST/methods/providers.py index ad0585312..58b02b9b2 100644 --- a/server/src/uds/REST/methods/providers.py +++ b/server/src/uds/REST/methods/providers.py @@ -36,7 +36,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _ from uds.models import Provider from uds.core import services -from uds.REST import Handler, HandlerError +from uds.REST import Handler, NotFound from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin import logging @@ -62,12 +62,18 @@ class Types(ModelTypeHandlerMixin, Handler): def enum_types(self): return services.factory().providers().values() + def getGui(self, type_): + try: + return services.factory().lookup(type_).guiDescription() + except: + raise NotFound('type not found') + class TableInfo(ModelTableHandlerMixin, Handler): path = 'providers' title = _('Current service providers') fields = [ { 'name': {'title': _('Name'), 'type': 'iconType' } }, { 'comments': {'title': _('Comments')}}, - { 'services_count': {'title': _('Services'), 'type': 'numeric', 'width': '5em'}} + { 'services_count': {'title': _('Services'), 'type': 'numeric', 'width': '5em'}}, ] diff --git a/server/src/uds/REST/mixins.py b/server/src/uds/REST/mixins.py index 43b98f268..581a63c86 100644 --- a/server/src/uds/REST/mixins.py +++ b/server/src/uds/REST/mixins.py @@ -145,6 +145,41 @@ class ModelTypeHandlerMixin(object): if self._args[1] == 'gui': gui = self.getGui(self._args[0]) + # Add name default description, at top of form + gui.insert(0, { + 'name': 'name', + 'value':'', + 'gui': { + 'required':True, + 'defvalue':'', + 'value':'', + 'label': _('Name'), + 'length': 128, + 'multiline': 0, + 'tooltip': _('Name of this element'), + 'rdonly': False, + 'type': 'text', + 'order': 1 + } + }) + # And comments + gui.insert(1, { + 'name': 'comments', + 'value':'', + 'gui': { + 'required':True, + 'defvalue':'', + 'value':'', + 'label': _('Comments'), + 'length': 256, + 'multiline': 0, + 'tooltip': _('Comments for this element'), + 'rdonly': False, + 'type': 'text', + 'order': 1 + } + }) + logger.debug("GUI: {0}".format(gui)) return gui diff --git a/server/src/uds/static/adm/css/uds-admin.css b/server/src/uds/static/adm/css/uds-admin.css index 83232c2a9..cb127137e 100644 --- a/server/src/uds/static/adm/css/uds-admin.css +++ b/server/src/uds/static/adm/css/uds-admin.css @@ -92,7 +92,9 @@ table.tablesorter thead tr th:hover { margin-bottom:12px; } - +.label-tbl-button { + display: none; +} /* Edit Below to Customize Widths > 768px */ @media (min-width:768px) { @@ -166,4 +168,9 @@ table.tablesorter thead tr th:hover { white-space: normal; } + .label-tbl-button { + display: inline-block; + } +} + } diff --git a/server/src/uds/static/adm/js/spreadsheet.js b/server/src/uds/static/adm/js/api-spreadsheet.js similarity index 100% rename from server/src/uds/static/adm/js/spreadsheet.js rename to server/src/uds/static/adm/js/api-spreadsheet.js diff --git a/server/src/uds/static/adm/js/tools.js b/server/src/uds/static/adm/js/api-tools.js similarity index 100% rename from server/src/uds/static/adm/js/tools.js rename to server/src/uds/static/adm/js/api-tools.js diff --git a/server/src/uds/static/adm/js/gui-elements.js b/server/src/uds/static/adm/js/gui-elements.js index afd12f2d7..e85bf34b4 100644 --- a/server/src/uds/static/adm/js/gui-elements.js +++ b/server/src/uds/static/adm/js/gui-elements.js @@ -34,8 +34,8 @@ gui.providers.link = function(event) { var tableId = gui.providers.table({ rowSelect : 'multi', - rowSelectFnc : function(nodes) { - gui.doLog(nodes); + rowSelectFnc : function(data) { + gui.doLog(data); gui.doLog(this); gui.doLog(this.fnGetSelectedData()); }, @@ -65,15 +65,15 @@ gui.authenticators.link = function(event) { container : 'auths-placeholder', rowSelect : 'single', buttons : [ 'edit', 'refresh', 'delete', 'xls' ], - onRowSelect : function(nodes) { + onRowSelect : function(selected) { api.tools.blockUI(); - var id = this.fnGetSelectedData()[0].id; + var id = selected[0].id; var user = new GuiElement(api.authenticators.detail(id, 'users'), 'users'); user.table({ container : 'users-placeholder', rowSelect : 'multi', buttons : [ 'edit', 'refresh', 'delete', 'xls' ], - scroll : true, + scrollToTable : true, onLoad: function(k) { api.tools.unblockUI(); }, diff --git a/server/src/uds/static/adm/js/gui.js b/server/src/uds/static/adm/js/gui.js index afbb70dc4..929351dd9 100644 --- a/server/src/uds/static/adm/js/gui.js +++ b/server/src/uds/static/adm/js/gui.js @@ -12,9 +12,11 @@ } }; - - // Several convenience "constants" - gui.dataTablesLanguage = { + + gui.config = gui.config || {}; + + // Several convenience "constants" for tables + gui.config.dataTablesLanguage = { "sLengthMenu" : gettext("_MENU_ records per page"), "sZeroRecords" : gettext("Empty"), "sInfo" : gettext("Records _START_ to _END_ of _TOTAL_"), @@ -30,6 +32,14 @@ "sPrevious" : gettext("Previous"), } }; + + gui.config.dataTableButtonsText = { + 'new': ' ' + gettext('New') + '', + 'edit': ' ' + gettext('Edit') + '', + 'delete': ' ' + gettext('Delete') + '', + 'refresh': ' ' + gettext('Refresh') + '', + 'xls': ' ' + gettext('Xls') + '', + }; gui.table = function(title, table_id, options) { if (options === undefined) @@ -158,19 +168,48 @@ GuiElement.prototype = { }, }); }, + // Options: dictionary + // container: container ID of parent for the table. If undefined, table will be appended to workspace + // buttons: array of visible buttons (strings), valid are [ 'new', 'edit', 'refresh', 'delete', 'xls' ], + // rowSelect: type of allowed row selection, valid values are 'single' and 'multi' + // scrollToTable: if True, will scroll page to show table + // + // onLoad: Event (function). If defined, will be invoked when table is fully loaded. + // Receives 1 parameter, that is the gui element (GuiElement) used to render table + // onRowSelect: Event (function). If defined, will be invoked when a row of table is selected + // Receives 3 parameters: + // 1.- the array of selected items data (objects, as got from api...get) + // 2.- the DataTable that raised the event + // 3.- the DataTableTools that raised the event + // onRowDeselect: Event (function). If defined, will be invoked when a row of table is deselected + // Receives 3 parameters: + // 1.- the array of selected items data (objects, as got from api...get) + // 2.- the DataTable that raised the event + // 3.- the DataTableTools that raised the event + // onNew: Event (function). If defined, will be invoked when "new" button is pressed + // Receives 4 parameters: + // 1.- the selected item data (single object, as got from api...get) + // 2.- the event that fired this (new, delete, edit, ..) + // 3.- the DataTable that raised the event + // 4.- the DataTableTools that raised the event + // onEdit: Event (function). If defined, will be invoked when "edit" button is pressed + // Receives 4 parameters: + // 1.- the selected item data (single object, as got from api...get) + // 2.- the event that fired this (new, delete, edit, ..) + // 3.- the DataTable that raised the event + // 4.- the DataTableTools that raised the event + // onDelete: Event (function). If defined, will be invoked when "delete" button is pressed + // Receives 4 parameters: + // 1.- the selected item data (single object, as got from api...get) + // 2.- the event that fired this (new, delete, edit, ..) + // 4.- the DataTable that raised the event + // 5.- the DataTableTools that raised the event table : function(options) { "use strict"; - // Options (all are optionals) - // rowSelect: 'single' or 'multi' - // container: ID of the element that will hold this table (will be - // emptied) - // rowSelectFnc: function to invoke on row selection. receives 1 array - - // node : TR elements that were selected - // rowDeselectFnc: function to invoke on row deselection. receives 1 - // array - node : TR elements that were selected + options = options || {}; gui.doLog('Composing table for ' + this.name); var tableId = this.name + '-table'; - var $this = this; + var $this = this; // Store this for child functions // Empty cells transform var renderEmptyCell = function(data) { @@ -212,21 +251,28 @@ GuiElement.prototype = { return dict[data] || renderEmptyCell(''); }; }; - this.rest.tableInfo({ success: function(data) { var title = data.title; var columns = []; $.each(data.fields, function(index, value) { for ( var v in value) { - var options = value[v]; + var opts = value[v]; var column = { mData : v, }; - column.sTitle = options.title; + column.sTitle = opts.title; column.mRender = renderEmptyCell; - if (options.type !== undefined) { - switch(options.type) { + if (opts.width) + column.sWidth = opts.width; + column.bVisible = opts.visible === undefined ? true : opts.visible; + if (opts.sortable !== undefined) + column.bSortable = opts.sortable; + if (opts.searchable !== undefined) + column.bSearchable = opts.searchable; + + if (opts.type !== undefined && column.bVisible ) { + switch(opts.type) { case 'date': column.sType = 'date'; column.mRender = renderDate(api.tools.djangoFormat(get_format('SHORT_DATE_FORMAT'))); @@ -243,31 +289,23 @@ GuiElement.prototype = { column.mRender = renderTypeIcon; break; case 'icon': - if( options.icon !== undefined ) { - column.mRender = renderIcon(options.icon); + if( opts.icon !== undefined ) { + column.mRender = renderIcon(opts.icon); } break; case 'dict': - if( options.dict !== undefined ) { - column.mRender = renderTextTransform(options.dict); + if( opts.dict !== undefined ) { + column.mRender = renderTextTransform(opts.dict); } break; default: - column.sType = options.type; + column.sType = opts.type; } } - if (options.width) - column.sWidth = options.width; - if (options.visible !== undefined) - column.bVisible = options.visible; - if (options.sortable !== undefined) - column.bSortable = options.sortable; - if (options.searchable !== undefined) - column.bSearchable = options.searchable; columns.push(column); } }); - // Generate styles for responsibe table, just the name of fields + // Generate styles for responsible table, just the name of fields var respStyles = []; var counter = 0; $.each(columns, function(col, value) { @@ -295,17 +333,18 @@ GuiElement.prototype = { var btns = []; if (options.buttons) { + var clickHandlerFor = function(handler, action) { + var handleFnc = handler || function(val, action, tbl, tbltools) {gui.doLog('Default handler called for ' + action + ': ' + JSON.stringify(val));}; + return function(btn) { + var tblTools = this; + var table = $('#' + tableId).dataTable(); + var val = this.fnGetSelectedData()[0]; + setTimeout(function() { + handleFnc(val, action, table, tblTools); + }, 0); + }; + }; - // methods for buttons click - var editFnc = function() { - gui.doLog('Edit'); - gui.doLog(this); - }; - var deleteFnc = function() { - gui.doLog('Delete'); - gui.doLog(this); - }; - // What execute on refresh button push var onRefresh = options.onRefresh || function(){}; @@ -354,28 +393,37 @@ GuiElement.prototype = { $.each(options.buttons, function(index, value) { var btn; switch (value) { + case 'new': + btn = { + "sExtends" : "text", + "sButtonText" : gui.config.dataTableButtonsText['new'], + "fnSelect" : deleteSelected, + "fnClick" : clickHandlerFor(options.onDelete, 'delete'), + "sButtonClass" : "disabled btn3d btn3d-tables" + }; + break; case 'edit': btn = { "sExtends" : "text", - "sButtonText" : gettext('Edit'), + "sButtonText" : gui.config.dataTableButtonsText['edit'], "fnSelect" : editSelected, - "fnClick" : editFnc, + "fnClick" : clickHandlerFor(options.onEdit, 'edit'), "sButtonClass" : "disabled btn3d btn3d-tables" }; break; case 'delete': btn = { "sExtends" : "text", - "sButtonText" : gettext('Delete'), + "sButtonText" : gui.config.dataTableButtonsText['delete'], "fnSelect" : deleteSelected, - "fnClick" : deleteFnc, + "fnClick" : clickHandlerFor(options.onDelete, 'delete'), "sButtonClass" : "disabled btn3d btn3d-tables" }; break; case 'refresh': btn = { "sExtends" : "text", - "sButtonText" : gettext('Refresh'), + "sButtonText" : gui.config.dataTableButtonsText['refresh'], "fnClick" : refreshFnc, "sButtonClass" : "btn3d-primary btn3d btn3d-tables" }; @@ -383,7 +431,7 @@ GuiElement.prototype = { case 'xls': btn = { "sExtends" : "text", - "sButtonText" : 'xls', + "sButtonText" : gui.config.dataTableButtonsText['xls'], "fnClick" : function(){ api.templates.get('spreadsheet', function(tmpl) { var styles = { 'bold': 's21', }; @@ -417,7 +465,6 @@ GuiElement.prototype = { rows_count: rows.length, rows: rows.join('\n') }; - // window.location.href = uri + base64(api.templates.evaluate(tmpl, ctx)); setTimeout( function() { saveAs(new Blob([api.templates.evaluate(tmpl, ctx)], {type: 'application/vnd.ms-excel'} ), title + '.xls'); @@ -437,20 +484,29 @@ GuiElement.prototype = { var oTableTools = { "aButtons" : btns }; + + // Type of row selection if (options.rowSelect) { oTableTools.sRowSelect = options.rowSelect; } + if (options.onRowSelect) { - oTableTools.fnRowSelected = options.onRowSelect; + var rowSelectedFnc = options.onRowSelect; + oTableTools.fnRowSelected = function() { + rowSelectedFnc(this.fnGetSelectedData(), $('#' + tableId).dataTable(), this); + }; } if (options.onRowDeselect) { - oTableTools.fnRowDeselected = options.onRowDeselect; + var rowDeselectedFnc = options.onRowDeselect; + oTableTools.fnRowDeselected = function() { + rowDeselectedFnc(this.fnGetSelectedData(), $('#' + tableId).dataTable(), this); + }; } $('#' + tableId).dataTable({ "aaData" : data, "aoColumns" : columns, - "oLanguage" : gui.dataTablesLanguage, + "oLanguage" : gui.config.dataTablesLanguage, "oTableTools" : oTableTools, // First is upper row, // second row is lower @@ -462,7 +518,7 @@ GuiElement.prototype = { api.tools.fix3dButtons('#' + tableId + '_wrapper .btn-group-3d'); // Fix form //$('#' + tableId + '_filter input').addClass('form-control'); - if (options.scroll !== undefined ) { + if (options.scrollToTable === true ) { var tableTop = $('#' + tableId).offset().top; $('html, body').scrollTop(tableTop); } diff --git a/server/src/uds/static/adm/js/strftime.js b/server/src/uds/static/adm/js/strftime.js deleted file mode 100644 index 293edf205..000000000 --- a/server/src/uds/static/adm/js/strftime.js +++ /dev/null @@ -1,392 +0,0 @@ -// -// strftime -// github.com/samsonjs/strftime -// @_sjs -// -// Copyright 2010 - 2013 Sami Samhuri -// -// MIT License -// http://sjs.mit-license.org -// - -;(function() { - - var namespace = api.tools; - - var dayNames = [ gettext('Sunday'), gettext('Monday'), gettext('Tuesday'), gettext('Wednesday'), - gettext('Thursday'), gettext('Friday'), gettext('Saturday') ]; - var monthNames = [ gettext('January'), gettext('February'), gettext('March'), gettext('April'), gettext('May'), - gettext('June'), gettext('July'), gettext('August'), gettext('September'), gettext('October'), - gettext('November'), gettext('December') ]; - - function initialsOf(arr) { - var res = []; - for ( var v in arr) { - res.push(arr[v].substr(0, 3)); - } - return res; - } - - var DefaultLocale = { - days : dayNames, - shortDays : initialsOf(dayNames), - months : monthNames, - shortMonths : initialsOf(monthNames), - AM : 'AM', - PM : 'PM', - am : 'am', - pm : 'pm', - }; - - // Added this to convert django format strings to c format string - // This is ofc, a "simplified" version, aimed to use date format used by - // DJANGO - namespace.djangoFormat = function(format) { - return format.replace(/./g, function(c) { - switch (c) { - case 'a': - case 'A': - return '%p'; - case 'b': - case 'd': - case 'm': - case 'w': - case 'W': - case 'y': - case 'Y': - return '%' + c; - case 'c': - return '%FT%TZ'; - case 'D': - return '%a'; - case 'e': - return '%z'; - case 'f': - return '%I:%M'; - case 'F': - return '%F'; - case 'h': - case 'g': - return '%I'; - case 'H': - case 'G': - return '%H'; - case 'i': - return '%M'; - case 'I': - return ''; // daylight saving - case 'j': - return '%d'; - case 'l': - return '%A'; - case 'L': - return ''; // if it is leap year - case 'M': - return '%b'; - case 'n': - return '%m'; - case 'N': - return '%b'; - case 'o': - return '%W'; // Not so sure, not important i thing anyway :-) - case 'O': - return '%z'; - case 'P': - return '%R %p'; - case 'r': - return '%a, %d %b %Y %T %z'; - case 's': - return '%S'; - case 'S': - return ''; // english ordinal suffix for day of month - case 't': - return ''; // number of days of specified month, not important - case 'T': - return '%Z'; - case 'u': - return '0'; // microseconds - case 'U': - return ''; // Seconds since EPOCH, not used - case 'z': - return '%j'; - case 'Z': - return 'z'; // Time zone offset in seconds, replaced by offset - // in ours/minutes :-) - default: - return c; - } - - }); - }; - - namespace.strftime = strftime; - function strftime(fmt, d, locale) { - return _strftime(fmt, d, locale); - } - - // locale is optional - namespace.strftimeTZ = strftime.strftimeTZ = strftimeTZ; - function strftimeTZ(fmt, d, locale, timezone) { - if (typeof locale == 'number' && timezone === null) { - timezone = locale; - locale = undefined; - } - return _strftime(fmt, d, locale, { - timezone : timezone - }); - } - - namespace.strftimeUTC = strftime.strftimeUTC = strftimeUTC; - function strftimeUTC(fmt, d, locale) { - return _strftime(fmt, d, locale, { - utc : true - }); - } - - namespace.localizedStrftime = strftime.localizedStrftime = localizedStrftime; - function localizedStrftime(locale) { - return function(fmt, d, options) { - return strftime(fmt, d, locale, options); - }; - } - - // d, locale, and options are optional, but you can't leave - // holes in the argument list. If you pass options you have to pass - // in all the preceding args as well. - // - // options: - // - locale [object] an object with the same structure as DefaultLocale - // - timezone [number] timezone offset in minutes from GMT - function _strftime(fmt, d, locale, options) { - options = options || {}; - - // d and locale are optional so check if d is really the locale - if (d && !quacksLikeDate(d)) { - locale = d; - d = undefined; - } - d = d || new Date(); - - locale = locale || DefaultLocale; - locale.formats = locale.formats || {}; - - // Hang on to this Unix timestamp because we might mess with it directly - // below. - var timestamp = d.getTime(); - - if (options.utc || typeof options.timezone == 'number') { - d = dateToUTC(d); - } - - if (typeof options.timezone == 'number') { - d = new Date(d.getTime() + (options.timezone * 60000)); - } - - // Most of the specifiers supported by C's strftime, and some from Ruby. - // Some other syntax extensions from Ruby are supported: %-, %_, and %0 - // to pad with nothing, space, or zero (respectively). - return fmt.replace(/%([-_0]?.)/g, function(_, c) { - var mod, padding; - if (c.length == 2) { - mod = c[0]; - // omit padding - if (mod == '-') { - padding = ''; - } - // pad with space - else if (mod == '_') { - padding = ' '; - } - // pad with zero - else if (mod == '0') { - padding = '0'; - } else { - // unrecognized, return the format - return _; - } - c = c[1]; - } - switch (c) { - case 'A': - return locale.days[d.getDay()]; - case 'a': - return locale.shortDays[d.getDay()]; - case 'B': - return locale.months[d.getMonth()]; - case 'b': - return locale.shortMonths[d.getMonth()]; - case 'C': - return pad(Math.floor(d.getFullYear() / 100), padding); - case 'D': - return _strftime(locale.formats.D || '%m/%d/%y', d, locale); - case 'd': - return pad(d.getDate(), padding); - case 'e': - return d.getDate(); - case 'F': - return _strftime(locale.formats.F || '%Y-%m-%d', d, locale); - case 'H': - return pad(d.getHours(), padding); - case 'h': - return locale.shortMonths[d.getMonth()]; - case 'I': - return pad(hours12(d), padding); - case 'j': - var y = new Date(d.getFullYear(), 0, 1); - var day = Math.ceil((d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); - return pad(day, 3); - case 'k': - return pad(d.getHours(), padding === null ? ' ' : padding); - case 'L': - return pad(Math.floor(timestamp % 1000), 3); - case 'l': - return pad(hours12(d), padding === null ? ' ' : padding); - case 'M': - return pad(d.getMinutes(), padding); - case 'm': - return pad(d.getMonth() + 1, padding); - case 'n': - return '\n'; - case 'o': - return String(d.getDate()) + ordinal(d.getDate()); - case 'P': - return d.getHours() < 12 ? locale.am : locale.pm; - case 'p': - return d.getHours() < 12 ? locale.AM : locale.PM; - case 'R': - return _strftime(locale.formats.R || '%H:%M', d, locale); - case 'r': - return _strftime(locale.formats.r || '%I:%M:%S %p', d, locale); - case 'S': - return pad(d.getSeconds(), padding); - case 's': - return Math.floor(timestamp / 1000); - case 'T': - return _strftime(locale.formats.T || '%H:%M:%S', d, locale); - case 't': - return '\t'; - case 'U': - return pad(weekNumber(d, 'sunday'), padding); - case 'u': - var dayu = d.getDay(); - return dayu === 0 ? 7 : dayu; // 1 - 7, Monday is first day of the - // week - case 'v': - return _strftime(locale.formats.v || '%e-%b-%Y', d, locale); - case 'W': - return pad(weekNumber(d, 'monday'), padding); - case 'w': - return d.getDay(); // 0 - 6, Sunday is first day of the - // week - case 'Y': - return d.getFullYear(); - case 'y': - var yy = String(d.getFullYear()); - return yy.slice(yy.length - 2); - case 'Z': - if (options.utc) { - return "GMT"; - } else { - var tz = d.toString().match(/\((\w+)\)/); - return tz && tz[1] || ''; - } - break; - case 'z': - if (options.utc) { - return "+0000"; - } else { - var off = typeof options.timezone == 'number' ? options.timezone : -d.getTimezoneOffset(); - return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); - } - break; - default: - return c; - } - }); - } - - function dateToUTC(d) { - var msDelta = (d.getTimezoneOffset() || 0) * 60000; - return new Date(d.getTime() + msDelta); - } - - var RequiredDateMethods = [ 'getTime', 'getTimezoneOffset', 'getDay', 'getDate', 'getMonth', 'getFullYear', - 'getYear', 'getHours', 'getMinutes', 'getSeconds' ]; - function quacksLikeDate(x) { - var i = 0, n = RequiredDateMethods.length; - for (i = 0; i < n; ++i) { - if (typeof x[RequiredDateMethods[i]] != 'function') { - return false; - } - } - return true; - } - - // Default padding is '0' and default length is 2, both are optional. - function pad(n, padding, length) { - // pad(n, ) - if (typeof padding === 'number') { - length = padding; - padding = '0'; - } - - // Defaults handle pad(n) and pad(n, ) - if (padding === null) { - padding = '0'; - } - length = length || 2; - - var s = String(n); - // padding may be an empty string, don't loop forever if it is - if (padding) { - while (s.length < length) - s = padding + s; - } - return s; - } - - function hours12(d) { - var hour = d.getHours(); - if (hour === 0) - hour = 12; - else if (hour > 12) - hour -= 12; - return hour; - } - - // Get the ordinal suffix for a number: st, nd, rd, or th - function ordinal(n) { - var i = n % 10, ii = n % 100; - if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) { - return 'th'; - } - switch (i) { - case 1: - return 'st'; - case 2: - return 'nd'; - case 3: - return 'rd'; - } - } - - // firstWeekday: 'sunday' or 'monday', default is 'sunday' - // - // Pilfered & ported from Ruby's strftime implementation. - function weekNumber(d, firstWeekday) { - firstWeekday = firstWeekday || 'sunday'; - - // This works by shifting the weekday back by one day if we - // are treating Monday as the first day of the week. - var wday = d.getDay(); - if (firstWeekday == 'monday') { - if (wday === 0) // Sunday - wday = 6; - else - wday--; - } - var firstDayOfYear = new Date(d.getFullYear(), 0, 1), yday = (d - firstDayOfYear) / 86400000, weekNum = (yday + 7 - wday) / 7; - return Math.floor(weekNum); - } - -}()); diff --git a/server/src/uds/templates/uds/admin/index.html b/server/src/uds/templates/uds/admin/index.html index 31ecaeab2..a09b38e6b 100644 --- a/server/src/uds/templates/uds/admin/index.html +++ b/server/src/uds/templates/uds/admin/index.html @@ -55,10 +55,6 @@ - - - - @@ -80,18 +76,22 @@ + + + - + - - + + +