feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
77
modules/backend/widgets/table/assets/css/table.css
Normal file
77
modules/backend/widgets/table/assets/css/table.css
Normal file
@@ -0,0 +1,77 @@
|
||||
.control-table .table-container{border:1px solid #e0e0e0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;overflow:hidden;margin-bottom:15px}
|
||||
.control-table .table-container:last-child{margin-bottom:0}
|
||||
.control-table:not([data-records-per-page="false"]) .table-container{border-bottom-right-radius:0;border-bottom-left-radius:0}
|
||||
.control-table.active .table-container{border-color:#e0e0e0}
|
||||
.control-table table{width:100%;border-collapse:collapse;table-layout:fixed}
|
||||
.control-table table td,
|
||||
.control-table table th{padding:0;font-size:13px;color:#555}
|
||||
.control-table table [data-view-container]{padding:5px 10px;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:28px}
|
||||
.control-table table.headers:after{content:' ';display:block;position:absolute;left:1px;right:1px;margin-top:-1px;border-bottom:1px solid #e0e0e0}
|
||||
.control-table table.headers th{padding:7px 10px;font-weight:normal;text-transform:uppercase;font-size:11px;color:#333;background:white;border-right:1px solid #ecf0f1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.control-table table.headers th [data-view-container]{padding-bottom:6px}
|
||||
.control-table table.headers th:last-child{border-right:none}
|
||||
.control-table.active table.headers:after{border-bottom-color:#e0e0e0}
|
||||
.control-table table.data td{border:1px solid #ecf0f1}
|
||||
.control-table table.data td .content-container{position:relative;padding:1px;outline:none}
|
||||
.control-table table.data td .content-container.readonly{background:#f7f7f7}
|
||||
.control-table table.data td.active{border-color:#103141 !important}
|
||||
.control-table table.data td.active .content-container{padding:0;border:1px solid #103141}
|
||||
.control-table table.data td.active .content-container:before,
|
||||
.control-table table.data td.active .content-container:after{content:' ';background:#103141;position:absolute;left:-2px;top:-2px}
|
||||
.control-table table.data td.active .content-container:before{width:1px;bottom:-2px}
|
||||
.control-table table.data td.active .content-container:after{right:-2px;height:1px}
|
||||
.control-table table.data tr{background-color:#fff}
|
||||
.control-table table.data tr.error{background-color:#fbecec !important}
|
||||
.control-table table.data tr.error td.active.error{border-color:#ec0000 !important}
|
||||
.control-table table.data tr.error td.active.error .content-container{border-color:#ec0000 !important}
|
||||
.control-table table.data tr.error td.active.error .content-container:before,
|
||||
.control-table table.data tr.error td.active.error .content-container:after{background-color:#ec0000 !important}
|
||||
.control-table table.data tr:nth-child(2n){background-color:#fafafa}
|
||||
.control-table table.data tr:first-child td{border-top:none}
|
||||
.control-table table.data tr:last-child td{border-bottom:none}
|
||||
.control-table table.data td:first-child{border-left:none}
|
||||
.control-table table.data td:last-child{border-right:none}
|
||||
.control-table .control-scrollbar>div{border-bottom-right-radius:4px;border-bottom-left-radius:4px;overflow:hidden}
|
||||
.control-table .control-scrollbar table.data tr:last-child td{border-bottom:1px solid #ecf0f1}
|
||||
.control-table .toolbar{background:white;border-bottom:1px solid #e0e0e0}
|
||||
.control-table .toolbar:before,
|
||||
.control-table .toolbar:after{content:" ";display:table}
|
||||
.control-table .toolbar:after{clear:both}
|
||||
.control-table .toolbar a.btn{color:#323e50;padding:8px 10px;opacity:0.5;filter:alpha(opacity=50);-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none}
|
||||
.control-table .toolbar a.btn:hover{opacity:1;filter:alpha(opacity=100)}
|
||||
.control-table .toolbar .table-search{float:right;margin:3px 3px 3px 0}
|
||||
.control-table .toolbar .table-search .table-search-input{height:auto;padding:5px 13px 5px}
|
||||
.control-table .toolbar a.table-icon:before{display:inline-block;content:' ';width:16px;height:16px;margin-right:8px;position:relative;top:3px;background:transparent url(../images/table-icons.gif) no-repeat 0 0;background-size:32px auto}
|
||||
.control-table .toolbar a.table-icon.add-table-row-above:before{background-position:0 -56px}
|
||||
.control-table .toolbar a.table-icon.delete-table-row:before{background-position:0 -113px}
|
||||
.control-table.active .toolbar{border-bottom-color:#e0e0e0}
|
||||
.control-table .pagination{margin:-15px 0 15px;padding:7px 10px;background:white;border:1px solid #e0e0e0;border-top:none;border-bottom-right-radius:4px;border-bottom-left-radius:4px}
|
||||
.control-table .pagination ul{padding:0;margin:0}
|
||||
.control-table .pagination ul li{list-style:none;display:inline-block;margin-right:5px;font-size:12px;line-height:100%}
|
||||
.control-table .pagination ul li a{display:inline-block;text-decoration:none;color:#95a5a6;padding:4px 6px;background:#ecf0f1;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;outline:none;line-height:100%}
|
||||
.control-table .pagination ul li a:focus-visible{outline:auto}
|
||||
.control-table .pagination ul li.active a{background:#6cc551;color:#fff}
|
||||
@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:1.5),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.control-table .toolbar a:before{background-position:0 -9px;background-size:16px auto}.control-table .toolbar a.add-table-row-above:before{background-position:0 -39px}.control-table .toolbar a.delete-table-row:before{background-position:0 -66px}}.control-table td[data-column-type=string] input[type=text],
|
||||
.control-table td[data-column-type=autocomplete] input[type=text]{width:100%;height:100%;display:block;outline:none;border:none;padding:6px 10px 7px}
|
||||
html.chrome .control-table td[data-column-type=string] input[type=text],
|
||||
html.chrome .control-table td[data-column-type=autocomplete] input[type=text]{padding:6px 10px 7px!important}
|
||||
html.safari .control-table td[data-column-type=string] input[type=text],
|
||||
html.gecko .control-table td[data-column-type=string] input[type=text],
|
||||
html.safari .control-table td[data-column-type=autocomplete] input[type=text],
|
||||
html.gecko .control-table td[data-column-type=autocomplete] input[type=text]{padding:5px 10px 5px}
|
||||
ul.table-widget-autocomplete{background:white;font-size:13px;margin-top:0;border:1px solid #808c8d;border-top:1px solid #ecf0f1;border-bottom-right-radius:4px;border-bottom-left-radius:4px}
|
||||
ul.table-widget-autocomplete li a{padding:5px 10px}.control-table td[data-column-type=checkbox] div[data-checkbox-element]{width:16px;height:16px;border-radius:3px;background-color:#FFF;border:1px solid #999;margin:6px 5px 6px 10px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.control-table td[data-column-type=checkbox] div[data-checkbox-element]:hover{border-color:#808080;color:#4d4d4d}
|
||||
.control-table td[data-column-type=checkbox] div[data-checkbox-element].checked{border-width:2px}
|
||||
.control-table td[data-column-type=checkbox] div[data-checkbox-element].checked:before{font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f00c";font-size:10px;position:relative;left:1px;top:-4px}
|
||||
.control-table td[data-column-type=checkbox] div[data-checkbox-element]:focus{border-color:#103141;outline:none}.control-table td[data-column-type=dropdown] .content-container:not(.readonly){-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.control-table td[data-column-type=dropdown] .content-container:not(.readonly) [data-view-container]{padding-right:20px;position:relative;cursor:pointer}
|
||||
.control-table td[data-column-type=dropdown] .content-container:not(.readonly) [data-view-container]:after{font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f107";font-size:13px;line-height:100%;color:#95a5a6;position:absolute;top:8px;right:10px}
|
||||
.control-table td[data-column-type=dropdown] .content-container:not(.readonly) [data-view-container]:hover:after{color:#2da7c7}
|
||||
.control-table td[data-column-type=dropdown] [data-dropdown-open=true]{background:white}
|
||||
.control-table td[data-column-type=dropdown] [data-dropdown-open=true] [data-view-container]:after{font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f106"}.widget-field.frameless .control-table .table-container{border-top:none;border-left:none;border-right:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
|
||||
.widget-field.frameless .control-table .toolbar{background:transparent}html.cssanimations .control-table td[data-column-type=dropdown] [data-view-container].loading:after{background:url('../../../../../../modules/system/assets/ui/images/loader-transparent.svg') 50% 50%;background-size:15px 15px;position:absolute;width:15px;height:15px;top:6px;right:5px;content:' ';-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}
|
||||
.table-control-dropdown-list{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:absolute;background:white;border:1px solid #e0e0e0;border-top:none;padding-top:1px;overflow:hidden;z-index:1000;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-bottom-right-radius:4px;border-bottom-left-radius:4px}
|
||||
.table-control-dropdown-list ul{border-top:1px solid #ecf0f1;padding:0;margin:0;max-height:200px;overflow:auto}
|
||||
.table-control-dropdown-list li{list-style:none;font-size:13px;color:#555;padding:5px 10px;cursor:pointer;outline:none}
|
||||
.table-control-dropdown-list li:focus{background:#103141;color:white}
|
||||
BIN
modules/backend/widgets/table/assets/images/table-icons.gif
Normal file
BIN
modules/backend/widgets/table/assets/images/table-icons.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
773
modules/backend/widgets/table/assets/js/build-min.js
vendored
Normal file
773
modules/backend/widgets/table/assets/js/build-min.js
vendored
Normal file
@@ -0,0 +1,773 @@
|
||||
+function($){"use strict";if($.wn===undefined)$.wn={}
|
||||
if($.oc===undefined)$.oc=$.wn
|
||||
if($.wn.table===undefined)$.wn.table={}
|
||||
var Table=function(element,options){this.el=element
|
||||
this.$el=$(element)
|
||||
this.options=options
|
||||
this.disposed=false
|
||||
this.dataSource=null
|
||||
this.cellProcessors={}
|
||||
this.activeCellProcessor=null
|
||||
this.activeCell=null
|
||||
this.tableContainer=null
|
||||
this.dataTableContainer=null
|
||||
this.editedRowKey=null
|
||||
this.dataTable=null
|
||||
this.headerTable=null
|
||||
this.toolbar=null
|
||||
this.clickHandler=this.onClick.bind(this)
|
||||
this.keydownHandler=this.onKeydown.bind(this)
|
||||
this.documentClickHandler=this.onDocumentClick.bind(this)
|
||||
this.toolbarClickHandler=this.onToolbarClick.bind(this)
|
||||
if(this.options.postback&&this.options.clientDataSourceClass=='client'){if(!this.options.postbackHandlerName.length){var formHandler=this.$el.closest('form').data('request')
|
||||
this.options.postbackHandlerName=[formHandler||'onSave']}else if(typeof this.options.postbackHandlerName==='string'){this.options.postbackHandlerName=this.options.postbackHandlerName.split(',')}this.formSubmitHandler=this.onFormSubmit.bind(this)}this.navigation=null
|
||||
this.search=null
|
||||
this.recordsAddedOrDeleted=0
|
||||
this.disposeBound=this.dispose.bind(this)
|
||||
this.init()
|
||||
$.wn.foundation.controlUtils.markDisposable(element)}
|
||||
Table.prototype.init=function(){this.createDataSource()
|
||||
this.initCellProcessors()
|
||||
this.navigation=new $.wn.table.helper.navigation(this)
|
||||
this.search=new $.wn.table.helper.search(this)
|
||||
this.buildUi()
|
||||
this.registerHandlers()}
|
||||
Table.prototype.disposeCellProcessors=function(){for(var i=0,len=this.options.columns.length;i<len;i++){var column=this.options.columns[i].key
|
||||
this.cellProcessors[column].dispose()
|
||||
this.cellProcessors[column]=null}this.cellProcessors=null
|
||||
this.activeCellProcessor=null}
|
||||
Table.prototype.createDataSource=function(){var dataSourceClass=this.options.clientDataSourceClass
|
||||
if($.wn.table.datasource===undefined||$.wn.table.datasource[dataSourceClass]==undefined)throw new Error('The table client-side data source class "'+dataSourceClass+'" is not '+'found in the $.wn.table.datasource namespace.')
|
||||
this.dataSource=new $.wn.table.datasource[dataSourceClass](this)}
|
||||
Table.prototype.registerHandlers=function(){this.el.addEventListener('click',this.clickHandler)
|
||||
this.el.addEventListener('keydown',this.keydownHandler)
|
||||
this.$el.one('dispose-control',this.disposeBound)
|
||||
document.addEventListener('click',this.documentClickHandler)
|
||||
if(this.options.postback&&this.options.clientDataSourceClass=='client')this.$el.closest('form').bind('oc.beforeRequest',this.formSubmitHandler)
|
||||
var toolbar=this.getToolbar()
|
||||
if(toolbar)toolbar.addEventListener('click',this.toolbarClickHandler);}
|
||||
Table.prototype.unregisterHandlers=function(){this.el.removeEventListener('click',this.clickHandler);document.removeEventListener('click',this.documentClickHandler)
|
||||
this.clickHandler=null
|
||||
this.el.removeEventListener('keydown',this.keydownHandler);this.keydownHandler=null
|
||||
var toolbar=this.getToolbar()
|
||||
if(toolbar)toolbar.removeEventListener('click',this.toolbarClickHandler);this.toolbarClickHandler=null
|
||||
if(this.formSubmitHandler){this.$el.closest('form').unbind('oc.beforeRequest',this.formSubmitHandler)
|
||||
this.formSubmitHandler=null}}
|
||||
Table.prototype.initCellProcessors=function(){for(var i=0,len=this.options.columns.length;i<len;i++){var columnConfiguration=this.options.columns[i],column=columnConfiguration.key,columnType=columnConfiguration.type
|
||||
if(columnType===undefined){columnType='string'
|
||||
this.options.columns[i].type=columnType}if($.wn.table.processor===undefined||$.wn.table.processor[columnType]==undefined)throw new Error('The table cell processor for the column type "'+columnType+'" is not '+'found in the $.wn.table.processor namespace.')
|
||||
this.cellProcessors[column]=new $.wn.table.processor[columnType](this,column,columnConfiguration)}}
|
||||
Table.prototype.getCellProcessor=function(columnName){return this.cellProcessors[columnName]}
|
||||
Table.prototype.buildUi=function(){this.tableContainer=document.createElement('div')
|
||||
this.tableContainer.setAttribute('class','table-container')
|
||||
if(this.options.toolbar){this.buildToolbar()}this.tableContainer.appendChild(this.buildHeaderTable())
|
||||
this.el.insertBefore(this.tableContainer,this.el.children[0])
|
||||
if(!this.options.height){this.dataTableContainer=this.tableContainer}else{this.dataTableContainer=this.buildScrollbar()}this.updateDataTable()}
|
||||
Table.prototype.buildToolbar=function(){if(!this.options.adding&&!this.options.deleting&&!this.options.searching){return}this.toolbar=$($('[data-table-toolbar]',this.el).html()).appendTo(this.tableContainer).get(0)
|
||||
if(!this.options.adding){$('[data-cmd^="record-add"]',this.toolbar).remove()}else{if(this.navigation.paginationEnabled()||!this.options.rowSorting){$('[data-cmd=record-add-below], [data-cmd=record-add-above]',this.toolbar).remove()}else{$('[data-cmd=record-add]',this.toolbar).remove()}}if(!this.options.deleting){$('[data-cmd="record-delete"]',this.toolbar).remove()}}
|
||||
Table.prototype.buildScrollbar=function(){var scrollbar=document.createElement('div'),scrollbarContent=document.createElement('div')
|
||||
scrollbar.setAttribute('class','control-scrollbar')
|
||||
if(this.options.dynamicHeight)scrollbar.setAttribute('style','max-height: '+this.options.height+'px')
|
||||
else scrollbar.setAttribute('style','height: '+this.options.height+'px')
|
||||
scrollbar.appendChild(scrollbarContent)
|
||||
this.tableContainer.appendChild(scrollbar)
|
||||
$(scrollbar).scrollbar({animation:false})
|
||||
return scrollbarContent}
|
||||
Table.prototype.buildHeaderTable=function(){var headersTable=document.createElement('table'),row=document.createElement('tr')
|
||||
headersTable.className='headers'
|
||||
headersTable.appendChild(row)
|
||||
for(var i=0,len=this.options.columns.length;i<len;i++){var header=document.createElement('th')
|
||||
if(this.options.columns[i].width)header.setAttribute('style','width: '+this.options.columns[i].width)
|
||||
header.textContent!==undefined?header.textContent=this.options.columns[i].title:header.innerText=this.options.columns[i].title
|
||||
row.appendChild(header)}this.headerTable=headersTable
|
||||
return headersTable}
|
||||
Table.prototype.updateDataTable=function(onSuccess){var self=this
|
||||
this.unfocusTable()
|
||||
this.fetchRecords(function onUpdateDataTableSuccess(records,totalCount){self.buildDataTable(records,totalCount)
|
||||
if(onSuccess)onSuccess()
|
||||
if(totalCount==0)self.addRecord('above',true)
|
||||
self.$el.trigger('oc.tableUpdateData',[records,totalCount])
|
||||
self=null})}
|
||||
Table.prototype.updateColumnWidth=function(){var headerCells=this.headerTable.querySelectorAll('th'),dataCells=this.dataTable.querySelectorAll('tr:first-child td')
|
||||
for(var i=0,len=headerCells.length;i<len;i++){if(dataCells[i])dataCells[i].setAttribute('style',headerCells[i].getAttribute('style'))}}
|
||||
Table.prototype.buildDataTable=function(records,totalCount){var dataTable=document.createElement('table'),tbody=document.createElement('tbody'),keyColumn=this.options.keyColumn
|
||||
dataTable.setAttribute('class','data')
|
||||
for(var i=0,len=records.length;i<len;i++){var row=document.createElement('tr')
|
||||
if(records[i][keyColumn]===undefined)throw new Error('The row attribute '+keyColumn+' is not set for the row #'+i);row.setAttribute('data-row',records[i][keyColumn])
|
||||
for(var j=0,colsLen=this.options.columns.length;j<colsLen;j++){var cell=document.createElement('td'),dataContainer=document.createElement('input'),cellContentContainer=document.createElement('div'),column=this.options.columns[j],columnName=column.key,cellProcessor=this.getCellProcessor(columnName)
|
||||
cell.setAttribute('data-column',columnName)
|
||||
cell.setAttribute('data-column-type',column.type)
|
||||
dataContainer.setAttribute('type','hidden')
|
||||
dataContainer.setAttribute('data-container','data-container')
|
||||
dataContainer.value=this.formatDataContainerValue(records[i][columnName])
|
||||
cellContentContainer.setAttribute('class','content-container')
|
||||
cell.appendChild(cellContentContainer)
|
||||
row.appendChild(cell)
|
||||
cell.appendChild(dataContainer)
|
||||
cellProcessor.renderCell(records[i][columnName],cellContentContainer)}tbody.appendChild(row)}dataTable.appendChild(tbody)
|
||||
if(this.dataTable!==null)this.dataTableContainer.replaceChild(dataTable,this.dataTable)
|
||||
else this.dataTableContainer.appendChild(dataTable)
|
||||
this.dataTable=dataTable
|
||||
this.updateColumnWidth()
|
||||
this.updateScrollbar()
|
||||
this.navigation.buildPagination(totalCount)
|
||||
this.search.buildSearchForm()}
|
||||
Table.prototype.formatDataContainerValue=function(value){if(value===undefined){return''}if(typeof value==='boolean'){return value?1:''}return value}
|
||||
Table.prototype.fetchRecords=function(onSuccess){if(this.search.hasQuery()){this.dataSource.searchRecords(this.search.getQuery(),this.navigation.getPageFirstRowOffset(),this.options.recordsPerPage,onSuccess)}else{this.dataSource.getRecords(this.navigation.getPageFirstRowOffset(),this.options.recordsPerPage,onSuccess)}}
|
||||
Table.prototype.updateScrollbar=function(){if(!this.options.height)return
|
||||
$(this.dataTableContainer.parentNode).data('oc.scrollbar').update()}
|
||||
Table.prototype.scrollCellIntoView=function(){if(!this.options.height||!this.activeCell)return
|
||||
$(this.dataTableContainer.parentNode).data('oc.scrollbar').gotoElement(this.activeCell)}
|
||||
Table.prototype.disposeScrollbar=function(){if(!this.options.height)return
|
||||
$(this.dataTableContainer.parentNode).data('oc.scrollbar').dispose()
|
||||
$(this.dataTableContainer.parentNode).data('oc.scrollbar',null)}
|
||||
Table.prototype.setActiveProcessor=function(processor){if(this.activeCellProcessor)this.activeCellProcessor.onUnfocus()
|
||||
this.activeCellProcessor=processor}
|
||||
Table.prototype.commitEditedRow=function(){if(this.editedRowKey===null)return
|
||||
var editedRow=this.dataTable.querySelector('tr[data-row="'+this.editedRowKey+'"]')
|
||||
if(!editedRow)return
|
||||
if(editedRow.getAttribute('data-dirty')!=1)return
|
||||
var cells=editedRow.children,data={}
|
||||
for(var i=0,len=cells.length;i<len;i++){var cell=cells[i]
|
||||
data[cell.getAttribute('data-column')]=this.getCellValue(cell)}this.dataSource.updateRecord(this.editedRowKey,data)
|
||||
editedRow.setAttribute('data-dirty',0)}
|
||||
Table.prototype.unfocusTable=function(){this.elementRemoveClass(this.el,'active')
|
||||
if(this.activeCellProcessor)this.activeCellProcessor.onUnfocus()
|
||||
this.commitEditedRow()
|
||||
this.activeCellProcessor=null
|
||||
if(this.activeCell)this.activeCell.setAttribute('class','')
|
||||
this.activeCell=null}
|
||||
Table.prototype.focusTable=function(){this.elementAddClass(this.el,'active')}
|
||||
Table.prototype.focusCell=function(cellElement,isClick){var columnName=cellElement.getAttribute('data-column')
|
||||
if(columnName===null)return
|
||||
this.focusTable()
|
||||
var processor=this.getCellProcessor(columnName)
|
||||
if(!processor)throw new Error("Cell processor not found for the column "+columnName)
|
||||
if(this.activeCell!==cellElement){if(this.activeCell)this.elementRemoveClass(this.activeCell,'active')
|
||||
this.setActiveProcessor(processor)
|
||||
this.activeCell=cellElement
|
||||
if(processor.isCellFocusable())this.elementAddClass(this.activeCell,'active')}var rowKey=this.getCellRowKey(cellElement)
|
||||
if(this.editedRowKey!==null&&rowKey!=this.editedRowKey)this.commitEditedRow()
|
||||
this.editedRowKey=rowKey
|
||||
processor.onFocus(cellElement,isClick)
|
||||
this.scrollCellIntoView()}
|
||||
Table.prototype.markCellRowDirty=function(cellElement){cellElement.parentNode.setAttribute('data-dirty',1)}
|
||||
Table.prototype.addRecord=function(placement,noFocus){if(!this.activeCell||this.navigation.paginationEnabled()||!this.options.rowSorting)placement='bottom'
|
||||
var relativeToKey=null,currentRowIndex=null
|
||||
if(placement=='above'||placement=='below'){relativeToKey=this.getCellRowKey(this.activeCell)
|
||||
currentRowIndex=this.getCellRowIndex(this.activeCell)}this.unfocusTable()
|
||||
if(this.navigation.paginationEnabled()){var newPageIndex=this.navigation.getNewRowPage(placement,currentRowIndex)
|
||||
if(newPageIndex!=this.navigation.pageIndex){if(!this.validate())return}this.navigation.pageIndex=newPageIndex}this.recordsAddedOrDeleted++
|
||||
var keyColumn=this.options.keyColumn,recordData={},self=this
|
||||
recordData[keyColumn]=-1*this.recordsAddedOrDeleted
|
||||
this.$el.trigger('oc.tableNewRow',[recordData])
|
||||
this.dataSource.createRecord(recordData,placement,relativeToKey,this.navigation.getPageFirstRowOffset(),this.options.recordsPerPage,function onAddRecordDataTableSuccess(records,totalCount){self.buildDataTable(records,totalCount)
|
||||
var row=self.findRowByKey(recordData[keyColumn])
|
||||
if(!row)throw new Error('New row is not found in the updated table: '+recordData[keyColumn])
|
||||
if(!noFocus)self.navigation.focusCell(row,0)
|
||||
self=null})}
|
||||
Table.prototype.deleteRecord=function(){if(!this.activeCell)return
|
||||
var currentRowIndex=this.getCellRowIndex(this.activeCell),key=this.getCellRowKey(this.activeCell),self=this,paginationEnabled=this.navigation.paginationEnabled(),currentPageIndex=this.navigation.pageIndex,currentCellIndex=this.activeCell.cellIndex
|
||||
if(paginationEnabled)this.navigation.pageIndex=this.navigation.getPageAfterDeletion(currentRowIndex)
|
||||
this.recordsAddedOrDeleted++
|
||||
var keyColumn=this.options.keyColumn,newRecordData={}
|
||||
newRecordData[keyColumn]=-1*this.recordsAddedOrDeleted
|
||||
this.dataSource.deleteRecord(key,newRecordData,this.navigation.getPageFirstRowOffset(),this.options.recordsPerPage,function onDeleteRecordDataTableSuccess(records,totalCount){self.buildDataTable(records,totalCount)
|
||||
if(!paginationEnabled)self.navigation.focusCellInReplacedRow(currentRowIndex,currentCellIndex)
|
||||
else{if(currentPageIndex!=self.navigation.pageIndex)self.navigation.focusCell('bottom',currentCellIndex)
|
||||
else self.navigation.focusCellInReplacedRow(currentRowIndex,currentCellIndex)}self=null})}
|
||||
Table.prototype.notifyRowProcessorsOnChange=function(cellElement){var columnName=cellElement.getAttribute('data-column'),row=cellElement.parentNode
|
||||
for(var i=0,len=row.children.length;i<len;i++){var column=this.options.columns[i].key
|
||||
this.cellProcessors[column].onRowValueChanged(columnName,row.children[i])}}
|
||||
Table.prototype.getToolbar=function(){return this.tableContainer.querySelector('div.toolbar')}
|
||||
Table.prototype.validate=function(){var rows=this.dataTable.querySelectorAll('tbody tr[data-row]')
|
||||
for(var i=0,len=rows.length;i<len;i++){var row=rows[i]
|
||||
this.elementRemoveClass(row,'error')}for(var i=0,rowsLen=rows.length;i<rowsLen;i++){var row=rows[i],rowData=this.getRowData(row)
|
||||
for(var j=0,colsLen=row.children.length;j<colsLen;j++)this.elementRemoveClass(row.children[j],'error')
|
||||
for(var columnName in rowData){var cellProcessor=this.getCellProcessor(columnName),message=cellProcessor.validate(rowData[columnName],rowData)
|
||||
if(message!==undefined){var cell=row.querySelector('td[data-column="'+columnName+'"]'),self=this
|
||||
this.elementAddClass(row,'error')
|
||||
this.elementAddClass(cell,'error')
|
||||
$.wn.flashMsg({text:message,'class':'error'})
|
||||
window.setTimeout(function(){self.focusCell(cell,false)
|
||||
cell=null
|
||||
self=null
|
||||
cellProcessor=null},100)
|
||||
return false}}}return true}
|
||||
Table.prototype.onClick=function(ev){this.focusTable()
|
||||
if(this.navigation.onClick(ev)===false)return
|
||||
if(this.search.onClick(ev)===false)return
|
||||
for(var i=0,len=this.options.columns.length;i<len;i++){var column=this.options.columns[i].key
|
||||
this.cellProcessors[column].onClick(ev)}var target=this.getEventTarget(ev,'TD')
|
||||
if(!target){this.unfocusTable();return;}if(target.tagName!='TD'){this.unfocusTable();return;}this.focusCell(target,true)}
|
||||
Table.prototype.onKeydown=function(ev){if((ev.key==='a'||ev.key==='A')&&ev.altKey&&this.options.adding){if(!ev.shiftKey){this.addRecord('below')}else{this.addRecord('above')}this.stopEvent(ev)
|
||||
return}if((ev.key==='d'||ev.key==='D')&&ev.altKey&&this.options.deleting){this.deleteRecord()
|
||||
this.stopEvent(ev)
|
||||
return}for(var i=0,len=this.options.columns.length;i<len;i++){var column=this.options.columns[i].key
|
||||
if(this.cellProcessors[column].onKeyDown(ev)===false){return}}if(this.navigation.onKeydown(ev)===false){return}if(this.search.onKeydown(ev)===false){return}}
|
||||
Table.prototype.onFormSubmit=function(ev,data){if(this.options.postbackHandlerName.indexOf(data.handler)>-1){this.unfocusTable()
|
||||
if(!this.validate()){ev.preventDefault()
|
||||
return}data.options.data[this.options.fieldName]=this.dataSource.getAllData()}}
|
||||
Table.prototype.onToolbarClick=function(ev){var target=this.getEventTarget(ev),cmd=target.getAttribute('data-cmd')
|
||||
if(!cmd){return}switch(cmd){case'record-add':case'record-add-below':this.addRecord('below')
|
||||
break
|
||||
case'record-add-above':this.addRecord('above')
|
||||
break
|
||||
case'record-delete':this.deleteRecord()
|
||||
break}this.stopEvent(ev)}
|
||||
Table.prototype.onDocumentClick=function(ev){var target=this.getEventTarget(ev)
|
||||
if(this.parentContainsElement(this.el,target))return
|
||||
if(this.activeCellProcessor&&this.activeCellProcessor.elementBelongsToProcessor(target))return
|
||||
this.unfocusTable()}
|
||||
Table.prototype.dispose=function(){if(this.disposed){return}this.disposed=true
|
||||
this.disposeBound=true
|
||||
this.unfocusTable()
|
||||
this.dataSource.dispose()
|
||||
this.dataSource=null
|
||||
this.unregisterHandlers()
|
||||
this.dataTable=null
|
||||
this.headerTable=null
|
||||
this.toolbar=null
|
||||
this.disposeCellProcessors()
|
||||
this.navigation.dispose()
|
||||
this.navigation=null
|
||||
this.disposeScrollbar()
|
||||
this.el=null
|
||||
this.tableContainer=null
|
||||
this.$el=null
|
||||
this.dataTableContainer=null
|
||||
this.activeCell=null}
|
||||
Table.prototype.setRowValues=function(rowIndex,rowValues){var row=this.findRowByIndex(rowIndex)
|
||||
if(!row){return false}var dataUpdated=false
|
||||
for(var i=0,len=row.children.length;i<len;i++){var cell=row.children[i],cellColumnName=this.getCellColumnName(cell)
|
||||
for(var rowColumnName in rowValues){if(rowColumnName==cellColumnName){this.setCellValue(cell,rowValues[rowColumnName],true)
|
||||
dataUpdated=true}}}if(dataUpdated){var originalEditedRowKey=this.editedRowKey
|
||||
this.editedRowKey=this.getRowKey(row)
|
||||
this.commitEditedRow()
|
||||
this.editedRowKey=originalEditedRowKey}return true}
|
||||
Table.prototype.getElement=function(){return this.el}
|
||||
Table.prototype.getAlias=function(){return this.options.alias}
|
||||
Table.prototype.getTableContainer=function(){return this.tableContainer}
|
||||
Table.prototype.getDataTableBody=function(){return this.dataTable.children[0]}
|
||||
Table.prototype.getEventTarget=function(ev,tag){var target=ev.target?ev.target:ev.srcElement
|
||||
if(tag===undefined)return target
|
||||
var tagName=target.tagName
|
||||
while(tagName!=tag){target=target.parentNode
|
||||
if(!target)return null
|
||||
tagName=target.tagName}return target}
|
||||
Table.prototype.stopEvent=function(ev){if(ev.stopPropagation)ev.stopPropagation()
|
||||
else ev.cancelBubble=true
|
||||
if(ev.preventDefault)ev.preventDefault()
|
||||
else ev.returnValue=false}
|
||||
Table.prototype.elementHasClass=function(el,className){if(el.classList)return el.classList.contains(className);return new RegExp('(^| )'+className+'( |$)','gi').test(el.className);}
|
||||
Table.prototype.elementAddClass=function(el,className){if(this.elementHasClass(el,className))return
|
||||
if(el.classList)el.classList.add(className);else el.className+=' '+className;}
|
||||
Table.prototype.elementRemoveClass=function(el,className){if(el.classList)el.classList.remove(className);else el.className=el.className.replace(new RegExp('(^|\\b)'+className.split(' ').join('|')+'(\\b|$)','gi'),' ');}
|
||||
Table.prototype.parentContainsElement=function(parent,element){while(element&&element!=parent){element=element.parentNode}return element?true:false}
|
||||
Table.prototype.getCellValue=function(cellElement){return cellElement.querySelector('[data-container]').value}
|
||||
Table.prototype.getCellRowKey=function(cellElement){return parseInt(cellElement.parentNode.getAttribute('data-row'))}
|
||||
Table.prototype.getRowKey=function(rowElement){return parseInt(rowElement.getAttribute('data-row'))}
|
||||
Table.prototype.findRowByKey=function(key){return this.dataTable.querySelector('tbody tr[data-row="'+key+'"]')}
|
||||
Table.prototype.findRowByIndex=function(index){return this.getDataTableBody().children[index]}
|
||||
Table.prototype.getCellRowIndex=function(cellElement){return parseInt(cellElement.parentNode.rowIndex)}
|
||||
Table.prototype.getRowCellValueByColumnName=function(row,columnName){var cell=row.querySelector('td[data-column="'+columnName+'"]')
|
||||
if(!cell)return cell
|
||||
return this.getCellValue(cell)}
|
||||
Table.prototype.getRowData=function(row){var result={}
|
||||
for(var i=0,len=row.children.length;i<len;i++){var cell=row.children[i]
|
||||
result[cell.getAttribute('data-column')]=this.getCellValue(cell)}return result}
|
||||
Table.prototype.getCellColumnName=function(cellElement){return cellElement.getAttribute('data-column')}
|
||||
Table.prototype.setCellValue=function(cellElement,value,suppressEvents){var dataContainer=cellElement.querySelector('[data-container]')
|
||||
if(dataContainer.value!=value){dataContainer.value=value
|
||||
this.markCellRowDirty(cellElement)
|
||||
this.notifyRowProcessorsOnChange(cellElement)
|
||||
if(suppressEvents===undefined||!suppressEvents){this.$el.trigger('oc.tableCellChanged',[this.getCellColumnName(cellElement),value,this.getCellRowIndex(cellElement)])}}}
|
||||
Table.DEFAULTS={clientDataSourceClass:'client',keyColumn:'id',recordsPerPage:false,data:null,postback:true,postbackHandlerName:[],adding:true,deleting:true,toolbar:true,searching:false,rowSorting:false,height:false,dynamicHeight:false}
|
||||
var old=$.fn.table
|
||||
$.fn.table=function(option){var args=Array.prototype.slice.call(arguments,1),result=undefined
|
||||
this.each(function(){var $this=$(this)
|
||||
var data=$this.data('oc.table')
|
||||
var options=$.extend({},Table.DEFAULTS,$this.data(),typeof option=='object'&&option)
|
||||
if(!data)$this.data('oc.table',(data=new Table(this,options)))
|
||||
if(typeof option=='string')result=data[option].apply(data,args)
|
||||
if(typeof result!='undefined')return false})
|
||||
return result?result:this}
|
||||
$.fn.table.Constructor=Table
|
||||
$.wn.table.table=Table
|
||||
$.fn.table.noConflict=function(){$.fn.table=old
|
||||
return this}
|
||||
$(document).on('render',function(){$('div[data-control=table]').table()})}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.helper===undefined)$.wn.table.helper={}
|
||||
var Navigation=function(tableObj){this.tableObj=tableObj
|
||||
this.pageIndex=0
|
||||
this.pageCount=0
|
||||
this.init()};Navigation.prototype.init=function(){}
|
||||
Navigation.prototype.dispose=function(){this.tableObj=null}
|
||||
Navigation.prototype.paginationEnabled=function(){return this.tableObj.options.recordsPerPage>0;}
|
||||
Navigation.prototype.getPageFirstRowOffset=function(){return this.pageIndex*this.tableObj.options.recordsPerPage}
|
||||
Navigation.prototype.buildPagination=function(recordCount){if(!this.paginationEnabled())return
|
||||
var paginationContainer=this.tableObj.getElement().querySelector('.pagination'),newPaginationContainer=false,curRecordCount=0
|
||||
this.pageCount=this.calculatePageCount(recordCount,this.tableObj.options.recordsPerPage)
|
||||
if(!paginationContainer){paginationContainer=document.createElement('div')
|
||||
paginationContainer.setAttribute('class','pagination')
|
||||
newPaginationContainer=true}else{curRecordCount=this.getRecordCount(paginationContainer)}if(newPaginationContainer||curRecordCount!=recordCount){paginationContainer.setAttribute('data-record-count',recordCount)
|
||||
var pageList=this.buildPaginationLinkList(recordCount,this.tableObj.options.recordsPerPage,this.pageIndex)
|
||||
if(!newPaginationContainer){paginationContainer.replaceChild(pageList,paginationContainer.children[0])}else{paginationContainer.appendChild(pageList)
|
||||
this.tableObj.getElement().appendChild(paginationContainer)}}else{this.markActiveLinkItem(paginationContainer,this.pageIndex)}}
|
||||
Navigation.prototype.calculatePageCount=function(recordCount,recordsPerPage){var pageCount=Math.ceil(recordCount/recordsPerPage)
|
||||
if(!pageCount)pageCount=1
|
||||
return pageCount}
|
||||
Navigation.prototype.getRecordCount=function(paginationContainer){var container=paginationContainer?paginationContainer:this.tableObj.getElement().querySelector('.pagination')
|
||||
return parseInt(container.getAttribute('data-record-count'))}
|
||||
Navigation.prototype.buildPaginationLinkList=function(recordCount,recordsPerPage,pageIndex){var pageCount=this.calculatePageCount(recordCount,recordsPerPage),pageList=document.createElement('ul')
|
||||
for(var i=0;i<pageCount;i++){var item=document.createElement('li'),link=document.createElement('a')
|
||||
if(i==pageIndex)item.setAttribute('class','active')
|
||||
link.innerText=i+1
|
||||
link.setAttribute('data-page-index',i)
|
||||
link.setAttribute('href','#')
|
||||
item.appendChild(link)
|
||||
pageList.appendChild(item)
|
||||
$(link).addClass('pagination-link')}return pageList}
|
||||
Navigation.prototype.markActiveLinkItem=function(paginationContainer,pageIndex){var activeItem=paginationContainer.querySelector('.active'),list=paginationContainer.children[0]
|
||||
activeItem.setAttribute('class','')
|
||||
for(var i=0,len=list.children.length;i<len;i++){if(i==pageIndex){list.children[i].setAttribute('class','active')}}}
|
||||
Navigation.prototype.gotoPage=function(pageIndex,onSuccess){this.tableObj.unfocusTable()
|
||||
if(!this.tableObj.validate())return
|
||||
this.pageIndex=pageIndex
|
||||
this.tableObj.updateDataTable(onSuccess)}
|
||||
Navigation.prototype.getRowCountOnPage=function(cellElement){return this.tableObj.getDataTableBody().children.length}
|
||||
Navigation.prototype.getNewRowPage=function(placement,currentRowIndex){var curRecordCount=this.getRecordCount()
|
||||
if(placement==='bottom')return this.calculatePageCount(curRecordCount+1,this.tableObj.options.recordsPerPage)-1
|
||||
if(placement=='above')return this.pageIndex
|
||||
if(placement=='below'){if(currentRowIndex==(this.tableObj.options.recordsPerPage-1))return this.pageIndex+1
|
||||
return this.pageIndex}return this.pageIndex}
|
||||
Navigation.prototype.getPageAfterDeletion=function(currentRowIndex){if(currentRowIndex==0&&this.getRowCountOnPage()==1)return this.pageIndex==0?0:this.pageIndex-1
|
||||
return this.pageIndex}
|
||||
Navigation.prototype.navigateDown=function(ev,forceCellIndex){if(!this.tableObj.activeCell)return
|
||||
if(this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor.keyNavigationAllowed(ev,'down'))return
|
||||
var row=this.tableObj.activeCell.parentNode,newRow=!ev.shiftKey?row.nextElementSibling:row.parentNode.children[row.parentNode.children.length-1],cellIndex=forceCellIndex!==undefined?forceCellIndex:this.tableObj.activeCell.cellIndex
|
||||
if(newRow){var cell=newRow.children[cellIndex]
|
||||
if(cell)this.tableObj.focusCell(cell)}else{if(!this.paginationEnabled())return
|
||||
if(this.pageIndex<this.pageCount-1){var self=this
|
||||
this.gotoPage(this.pageIndex+1,function navDownPageSuccess(){self.focusCell('top',cellIndex)
|
||||
self=null})}}}
|
||||
Navigation.prototype.navigateUp=function(ev,forceCellIndex,isTab){if(!this.tableObj.activeCell)return
|
||||
if(this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor.keyNavigationAllowed(ev,'up'))return
|
||||
var row=this.tableObj.activeCell.parentNode,newRow=(!ev.shiftKey||isTab)?row.previousElementSibling:row.parentNode.children[0],cellIndex=forceCellIndex!==undefined?forceCellIndex:this.tableObj.activeCell.cellIndex
|
||||
if(newRow){var cell=newRow.children[cellIndex]
|
||||
if(cell)this.tableObj.focusCell(cell)}else{if(!this.paginationEnabled())return
|
||||
if(this.pageIndex>0){var self=this
|
||||
this.gotoPage(this.pageIndex-1,function navUpPageSuccess(){self.focusCell('bottom',cellIndex)
|
||||
self=null})}}}
|
||||
Navigation.prototype.navigateLeft=function(ev,isTab){if(!this.tableObj.activeCell)return
|
||||
if(!isTab&&this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor.keyNavigationAllowed(ev,'left'))return
|
||||
var row=this.tableObj.activeCell.parentNode,newIndex=(!ev.shiftKey||isTab)?this.tableObj.activeCell.cellIndex-1:0
|
||||
var cell=row.children[newIndex]
|
||||
if(cell){this.tableObj.focusCell(cell)}else{this.navigateUp(ev,row.children.length-1,isTab)}}
|
||||
Navigation.prototype.navigateRight=function(ev,isTab){if(!this.tableObj.activeCell)return
|
||||
if(!isTab&&this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor.keyNavigationAllowed(ev,'right'))return
|
||||
var row=this.tableObj.activeCell.parentNode,newIndex=!ev.shiftKey?this.tableObj.activeCell.cellIndex+1:row.children.length-1
|
||||
var cell=row.children[newIndex]
|
||||
if(cell){this.tableObj.focusCell(cell)}else{this.navigateDown(ev,0)}}
|
||||
Navigation.prototype.navigateNext=function(ev){if(!this.tableObj.activeCell)return
|
||||
if(this.tableObj.activeCellProcessor&&!this.tableObj.activeCellProcessor.keyNavigationAllowed(ev,'tab'))return
|
||||
if(!ev.shiftKey)this.navigateRight(ev,true)
|
||||
else this.navigateLeft(ev,true)
|
||||
this.tableObj.stopEvent(ev)}
|
||||
Navigation.prototype.focusCell=function(rowReference,cellIndex){var row=null,tbody=this.tableObj.getDataTableBody()
|
||||
if(typeof rowReference==='object'){row=rowReference}else{if(rowReference=='bottom'){row=tbody.children[tbody.children.length-1]}else if(rowReference=='top'){row=tbody.children[0]}}if(!row)return
|
||||
var cell=row.children[cellIndex]
|
||||
if(cell)this.tableObj.focusCell(cell)}
|
||||
Navigation.prototype.focusCellInReplacedRow=function(rowIndex,cellIndex){if(rowIndex==0){this.focusCell('top',cellIndex)}else{var focusRow=this.tableObj.findRowByIndex(rowIndex)
|
||||
if(!focusRow)focusRow=this.tableObj.findRowByIndex(rowIndex-1)
|
||||
if(focusRow)this.focusCell(focusRow,cellIndex)
|
||||
else this.focusCell('top',cellIndex)}}
|
||||
Navigation.prototype.onKeydown=function(ev){if(ev.key==='ArrowDown')return this.navigateDown(ev)
|
||||
else if(ev.key==='ArrowUp')return this.navigateUp(ev)
|
||||
else if(ev.key==='ArrowLeft')return this.navigateLeft(ev)
|
||||
if(ev.key==='ArrowRight')return this.navigateRight(ev)
|
||||
if(ev.key==='Tab')return this.navigateNext(ev)}
|
||||
Navigation.prototype.onClick=function(ev){var target=this.tableObj.getEventTarget(ev,'A')
|
||||
if(!target||!$(target).hasClass('pagination-link'))return
|
||||
var pageIndex=parseInt(target.getAttribute('data-page-index'))
|
||||
if(pageIndex===null)return
|
||||
this.gotoPage(pageIndex)
|
||||
this.tableObj.stopEvent(ev)
|
||||
return false}
|
||||
$.wn.table.helper.navigation=Navigation;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.helper===undefined)$.wn.table.helper={}
|
||||
var Search=function(tableObj){this.tableObj=tableObj
|
||||
this.searchForm=null
|
||||
this.searchInput=null
|
||||
this.inputTrackTimer=null
|
||||
this.activeQuery=null
|
||||
this.isActive=false
|
||||
this.init()};Search.prototype.init=function(){}
|
||||
Search.prototype.dispose=function(){this.tableObj=null
|
||||
this.searchForm=null
|
||||
this.searchInput=null}
|
||||
Search.prototype.buildSearchForm=function(){if(!this.searchEnabled())return
|
||||
var el=this.tableObj.getElement(),toolbar=this.tableObj.getToolbar(),searchForm=toolbar.querySelector('.table-search')
|
||||
if(!searchForm){this.searchForm=$($('[data-table-toolbar-search]',el).html()).appendTo(toolbar).get(0)
|
||||
this.searchInput=$('.table-search-input',this.searchForm).get(0)}}
|
||||
Search.prototype.getQuery=function(){return $.trim(this.activeQuery)}
|
||||
Search.prototype.hasQuery=function(){return this.searchEnabled()&&$.trim(this.activeQuery).length>0}
|
||||
Search.prototype.searchEnabled=function(){return this.tableObj.options.searching}
|
||||
Search.prototype.getSearchableColumns=function(){const columns=[];this.tableObj.options.columns.forEach(function(column){if(column.type==='checkbox'){return;}if(!column.searchable){return;}columns.push(column.key);});return columns;}
|
||||
Search.prototype.performSearch=function(query,onSuccess){var isDirty=this.activeQuery!=query
|
||||
this.activeQuery=query
|
||||
if(isDirty){this.tableObj.updateDataTable(onSuccess)}}
|
||||
Search.prototype.onKeydown=function(ev){if(ev.key==='Tab'){this.onClick(ev)
|
||||
return}if(!this.isActive){return}var self=this
|
||||
this.inputTrackTimer=window.setTimeout(function(){self.performSearch(self.searchInput.value)},300)}
|
||||
Search.prototype.onClick=function(ev){var target=this.tableObj.getEventTarget(ev,'INPUT')
|
||||
this.isActive=target&&$(target).hasClass('table-search-input')}
|
||||
$.wn.table.helper.search=Search;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.datasource===undefined)$.wn.table.datasource={}
|
||||
var Base=function(tableObj){this.tableObj=tableObj}
|
||||
Base.prototype.dispose=function(){this.tableObj=null}
|
||||
Base.prototype.getRecords=function(offset,count,onSuccess){onSuccess([])}
|
||||
Base.prototype.searchRecords=function(query,offset,count,onSuccess){onSuccess([])}
|
||||
Base.prototype.createRecord=function(recordData,placement,relativeToKey,offset,count,onSuccess){onSuccess([],0)}
|
||||
Base.prototype.updateRecord=function(key,recordData){}
|
||||
Base.prototype.deleteRecord=function(key,newRecordData,offset,count,onSuccess){onSuccess([],0)}
|
||||
$.wn.table.datasource.base=Base;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.datasource===undefined)throw new Error("The $.wn.table.datasource namespace is not defined. Make sure that the table.datasource.base.js script is loaded.");var Base=$.wn.table.datasource.base,BaseProto=Base.prototype
|
||||
var Client=function(tableObj){Base.call(this,tableObj)
|
||||
var dataString=tableObj.getElement().getAttribute('data-data')
|
||||
if(dataString===null||dataString===undefined)throw new Error('The required data-data attribute is not found on the table control element.')
|
||||
this.data=JSON.parse(dataString)};Client.prototype=Object.create(BaseProto)
|
||||
Client.prototype.constructor=Client
|
||||
Client.prototype.dispose=function(){BaseProto.dispose.call(this)
|
||||
this.data=null}
|
||||
Client.prototype.getRecords=function(offset,count,onSuccess){if(!count){onSuccess(this.data,this.data.length)}else{onSuccess(this.data.slice(offset,offset+count),this.data.length)}}
|
||||
Client.prototype.createRecord=function(recordData,placement,relativeToKey,offset,count,onSuccess){if(placement==='bottom'){this.data.push(recordData)}else if(placement=='above'||placement=='below'){var recordIndex=this.getIndexOfKey(relativeToKey)
|
||||
if(placement=='below')recordIndex++
|
||||
this.data.splice(recordIndex,0,recordData)}this.getRecords(offset,count,onSuccess)}
|
||||
Client.prototype.searchRecords=function(query,offset,count,onSuccess){const searchFields=this.tableObj.search.getSearchableColumns();const matched=this.data.filter(function(record){for(let i=0;i<searchFields.length;i++){const value=record[searchFields[i]];if(value===undefined){continue;}if(value.toString().toLowerCase().includes(query.toLowerCase())){return true;}}return false;});if(matched.length===0){onSuccess([]);return;}if(!count){onSuccess(matched,matched.length);}else{onSuccess(matched.slice(offset,offset+count),matched.length);}}
|
||||
Client.prototype.updateRecord=function(key,recordData){var recordIndex=this.getIndexOfKey(key)
|
||||
if(recordIndex!==-1){recordData[this.tableObj.options.keyColumn]=key
|
||||
this.data[recordIndex]=recordData}else{throw new Error('Record with they key '+key+' is not found in the data set')}}
|
||||
Client.prototype.deleteRecord=function(key,newRecordData,offset,count,onSuccess){var recordIndex=this.getIndexOfKey(key)
|
||||
if(recordIndex!==-1){this.data.splice(recordIndex,1)
|
||||
if(this.data.length==0)this.data.push(newRecordData)
|
||||
this.getRecords(offset,count,onSuccess)}else{throw new Error('Record with they key '+key+' is not found in the data set')}}
|
||||
Client.prototype.getIndexOfKey=function(key){var keyColumn=this.tableObj.options.keyColumn
|
||||
return this.data.map(function(record){return record[keyColumn]+""}).indexOf(key+"")}
|
||||
Client.prototype.getAllData=function(){return this.data}
|
||||
$.wn.table.datasource.client=Client}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.datasource===undefined)throw new Error("The $.wn.table.datasource namespace is not defined. Make sure that the table.datasource.base.js script is loaded.");var Base=$.wn.table.datasource.base,BaseProto=Base.prototype
|
||||
var Server=function(tableObj){Base.call(this,tableObj)
|
||||
var dataString=tableObj.getElement().getAttribute('data-data')
|
||||
if(dataString===null||dataString===undefined)throw new Error('The required data-data attribute is not found on the table control element.')
|
||||
this.data=JSON.parse(dataString)};Server.prototype=Object.create(BaseProto)
|
||||
Server.prototype.constructor=Server
|
||||
Server.prototype.dispose=function(){BaseProto.dispose.call(this)
|
||||
this.data=null}
|
||||
Server.prototype.getRecords=function(offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerGetRecords'
|
||||
this.tableObj.$el.request(handlerName,{data:{offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
|
||||
Server.prototype.searchRecords=function(query,offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerSearchRecords'
|
||||
this.tableObj.$el.request(handlerName,{data:{query:query,offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
|
||||
Server.prototype.createRecord=function(recordData,placement,relativeToKey,offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerCreateRecord'
|
||||
this.tableObj.$el.request(handlerName,{data:{recordData:recordData,placement:placement,relativeToKey:relativeToKey,offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
|
||||
Server.prototype.updateRecord=function(key,recordData){var handlerName=this.tableObj.getAlias()+'::onServerUpdateRecord'
|
||||
this.tableObj.$el.request(handlerName,{data:{key:key,recordData:recordData}})}
|
||||
Server.prototype.deleteRecord=function(key,newRecordData,offset,count,onSuccess){var handlerName=this.tableObj.getAlias()+'::onServerDeleteRecord'
|
||||
this.tableObj.$el.request(handlerName,{data:{key:key,offset:offset,count:count}}).done(function(data){onSuccess(data.records,data.count)})}
|
||||
$.wn.table.datasource.server=Server}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.processor===undefined)$.wn.table.processor={}
|
||||
var Base=function(tableObj,columnName,columnConfiguration){this.tableObj=tableObj
|
||||
this.columnName=columnName
|
||||
this.columnConfiguration=columnConfiguration
|
||||
this.activeCell=null
|
||||
this.validators=[]
|
||||
this.registerHandlers()
|
||||
this.initValidators()}
|
||||
Base.prototype.dispose=function(){this.unregisterHandlers()
|
||||
this.tableObj=null
|
||||
this.activeCell=null}
|
||||
Base.prototype.renderCell=function(value,cellContentContainer){}
|
||||
Base.prototype.registerHandlers=function(){}
|
||||
Base.prototype.unregisterHandlers=function(){}
|
||||
Base.prototype.onFocus=function(cellElement,isClick){}
|
||||
Base.prototype.onUnfocus=function(){}
|
||||
Base.prototype.onKeyDown=function(ev){}
|
||||
Base.prototype.onClick=function(ev){}
|
||||
Base.prototype.onRowValueChanged=function(columnName,cellElement){}
|
||||
Base.prototype.keyNavigationAllowed=function(ev,direction){return true}
|
||||
Base.prototype.isCellFocusable=function(){return true}
|
||||
Base.prototype.getCellContentContainer=function(cellElement){return cellElement.querySelector('.content-container')}
|
||||
Base.prototype.createViewContainer=function(cellContentContainer,value){var viewContainer=document.createElement('div')
|
||||
viewContainer.setAttribute('data-view-container','data-view-container')
|
||||
viewContainer.textContent=value===undefined?'':value
|
||||
cellContentContainer.appendChild(viewContainer)
|
||||
return viewContainer}
|
||||
Base.prototype.getViewContainer=function(cellElement){return cellElement.querySelector('[data-view-container]')}
|
||||
Base.prototype.showViewContainer=function(cellElement){return this.getViewContainer(cellElement).setAttribute('class','')}
|
||||
Base.prototype.hideViewContainer=function(cellElement){return this.getViewContainer(cellElement).setAttribute('class','hide')}
|
||||
Base.prototype.setViewContainerValue=function(cellElement,value){return this.getViewContainer(cellElement).textContent=value}
|
||||
Base.prototype.elementBelongsToProcessor=function(element){return false}
|
||||
Base.prototype.initValidators=function(){if(this.columnConfiguration.validation===undefined)return
|
||||
for(var validatorName in this.columnConfiguration.validation){if($.wn.table.validator===undefined||$.wn.table.validator[validatorName]==undefined)throw new Error('The table cell validator "'+validatorName+'" for the column "'+this.columnName+'" is not '+'found in the $.wn.table.validator namespace.')
|
||||
var validator=new $.wn.table.validator[validatorName](this.columnConfiguration.validation[validatorName])
|
||||
this.validators.push(validator)}}
|
||||
Base.prototype.validate=function(value,rowData){for(var i=0,len=this.validators.length;i<len;i++){var message=this.validators[i].validate(value,rowData)
|
||||
if(message!==undefined)return message}}
|
||||
$.wn.table.processor.base=Base}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.processor===undefined)throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.wn.table.processor.base,BaseProto=Base.prototype
|
||||
var StringProcessor=function(tableObj,columnName,columnConfiguration){Base.call(this,tableObj,columnName,columnConfiguration)}
|
||||
StringProcessor.prototype=Object.create(BaseProto)
|
||||
StringProcessor.prototype.constructor=StringProcessor
|
||||
StringProcessor.prototype.dispose=function(){BaseProto.dispose.call(this)}
|
||||
StringProcessor.prototype.renderCell=function(value,cellContentContainer){this.createViewContainer(cellContentContainer,value);if(this.columnConfiguration.readonly||this.columnConfiguration.readOnly){cellContentContainer.classList.add('readonly');cellContentContainer.setAttribute('tabindex',0);}}
|
||||
StringProcessor.prototype.onFocus=function(cellElement,isClick){if(this.activeCell===cellElement)return
|
||||
this.activeCell=cellElement
|
||||
if(!this.columnConfiguration.readonly&&!this.columnConfiguration.readOnly){this.buildEditor(cellElement,this.getCellContentContainer(cellElement))}else{this.getCellContentContainer(cellElement).focus()}}
|
||||
StringProcessor.prototype.onUnfocus=function(){if(!this.activeCell)return
|
||||
var editor=this.activeCell.querySelector('.string-input')
|
||||
if(editor){this.tableObj.setCellValue(this.activeCell,editor.value)
|
||||
this.setViewContainerValue(this.activeCell,editor.value)
|
||||
editor.parentNode.removeChild(editor)}this.showViewContainer(this.activeCell)
|
||||
this.activeCell=null}
|
||||
StringProcessor.prototype.buildEditor=function(cellElement,cellContentContainer){this.hideViewContainer(this.activeCell)
|
||||
var input=document.createElement('input')
|
||||
input.setAttribute('type','text')
|
||||
input.setAttribute('class','string-input')
|
||||
input.value=this.tableObj.getCellValue(cellElement)
|
||||
cellContentContainer.appendChild(input)
|
||||
input.focus();this.setCaretPosition(input,0);}
|
||||
StringProcessor.prototype.keyNavigationAllowed=function(ev,direction){if(direction!='left'&&direction!='right')return true
|
||||
if(!this.activeCell)return true
|
||||
var editor=this.activeCell.querySelector('.string-input')
|
||||
if(!editor)return true
|
||||
var caretPosition=this.getCaretPosition(editor)
|
||||
if(direction=='left')return caretPosition==0
|
||||
if(direction=='right')return caretPosition==editor.value.length
|
||||
return true}
|
||||
StringProcessor.prototype.onRowValueChanged=function(columnName,cellElement){if(columnName!=this.columnName){return}var value=this.tableObj.getCellValue(cellElement)
|
||||
this.setViewContainerValue(cellElement,value)}
|
||||
StringProcessor.prototype.getCaretPosition=function(input){if(document.selection){var selection=document.selection.createRange()
|
||||
selection.moveStart('character',-input.value.length)
|
||||
return selection.text.length}if(input.selectionStart!==undefined)return input.selectionStart
|
||||
return 0}
|
||||
StringProcessor.prototype.setCaretPosition=function(input,position){if(document.selection){var range=input.createTextRange()
|
||||
setTimeout(function(){range.collapse(true)
|
||||
range.moveStart("character",position)
|
||||
range.moveEnd("character",0)
|
||||
range.select()},0)}if(input.selectionStart!==undefined){setTimeout(function(){input.selectionStart=position
|
||||
input.selectionEnd=position},0)}return 0}
|
||||
$.wn.table.processor.string=StringProcessor;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.processor===undefined)throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.wn.table.processor.base,BaseProto=Base.prototype
|
||||
var CheckboxProcessor=function(tableObj,columnName,columnConfiguration){Base.call(this,tableObj,columnName,columnConfiguration)}
|
||||
CheckboxProcessor.prototype=Object.create(BaseProto)
|
||||
CheckboxProcessor.prototype.constructor=CheckboxProcessor
|
||||
CheckboxProcessor.prototype.dispose=function(){BaseProto.dispose.call(this)}
|
||||
CheckboxProcessor.prototype.isCellFocusable=function(){return false}
|
||||
CheckboxProcessor.prototype.renderCell=function(value,cellContentContainer){var checkbox=document.createElement('div')
|
||||
checkbox.setAttribute('data-checkbox-element','true')
|
||||
checkbox.setAttribute('tabindex','0')
|
||||
if(value&&value!=0&&value!="false"){checkbox.setAttribute('class','checked')}cellContentContainer.appendChild(checkbox)
|
||||
if(this.columnConfiguration.readonly||this.columnConfiguration.readOnly){cellContentContainer.classList.add('readonly');}}
|
||||
CheckboxProcessor.prototype.onFocus=function(cellElement,isClick){cellElement.querySelector('div[data-checkbox-element]').focus()}
|
||||
CheckboxProcessor.prototype.onKeyDown=function(ev){if(ev.key==='(Space character)'||ev.key==='Spacebar'||ev.key===' ')this.onClick(ev)}
|
||||
CheckboxProcessor.prototype.onClick=function(ev){if(this.columnConfiguration.readonly||this.columnConfiguration.readOnly){return}var target=this.tableObj.getEventTarget(ev,'DIV')
|
||||
if(target.getAttribute('data-checkbox-element')){var container=this.getCheckboxContainerNode(target)
|
||||
if(container.getAttribute('data-column')!==this.columnName){return}this.changeState(target)
|
||||
$(ev.target).trigger('change')}}
|
||||
CheckboxProcessor.prototype.changeState=function(divElement){var cell=divElement.parentNode.parentNode
|
||||
if(divElement.getAttribute('class')=='checked'){divElement.setAttribute('class','')
|
||||
this.tableObj.setCellValue(cell,0)}else{divElement.setAttribute('class','checked')
|
||||
this.tableObj.setCellValue(cell,1)}}
|
||||
CheckboxProcessor.prototype.getCheckboxContainerNode=function(checkbox){return checkbox.parentNode.parentNode}
|
||||
CheckboxProcessor.prototype.onRowValueChanged=function(columnName,cellElement){if(columnName!=this.columnName){return}var checkbox=cellElement.querySelector('div[data-checkbox-element]'),value=this.tableObj.getCellValue(cellElement)
|
||||
if(value&&value!=0&&value!="false"){checkbox.setAttribute('class','checked')}else{checkbox.setAttribute('class','')}}
|
||||
$.wn.table.processor.checkbox=CheckboxProcessor;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.processor===undefined)throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.wn.table.processor.base,BaseProto=Base.prototype
|
||||
var DropdownProcessor=function(tableObj,columnName,columnConfiguration){this.itemListElement=null
|
||||
this.cachedOptionPromises={}
|
||||
this.searching=false
|
||||
this.searchQuery=null
|
||||
this.searchInterval=null
|
||||
this.itemClickHandler=this.onItemClick.bind(this)
|
||||
this.itemKeyDownHandler=this.onItemKeyDown.bind(this)
|
||||
this.itemMouseMoveHandler=this.onItemMouseMove.bind(this)
|
||||
Base.call(this,tableObj,columnName,columnConfiguration)}
|
||||
DropdownProcessor.prototype=Object.create(BaseProto)
|
||||
DropdownProcessor.prototype.constructor=DropdownProcessor
|
||||
DropdownProcessor.prototype.dispose=function(){this.unregisterListHandlers()
|
||||
this.itemClickHandler=null
|
||||
this.itemKeyDownHandler=null
|
||||
this.itemMouseMoveHandler=null
|
||||
this.itemListElement=null
|
||||
this.cachedOptionPromises=null
|
||||
BaseProto.dispose.call(this)}
|
||||
DropdownProcessor.prototype.unregisterListHandlers=function(){if(this.itemListElement){this.itemListElement.removeEventListener('click',this.itemClickHandler)
|
||||
this.itemListElement.removeEventListener('keydown',this.itemKeyDownHandler)
|
||||
this.itemListElement.removeEventListener('mousemove',this.itemMouseMoveHandler)}}
|
||||
DropdownProcessor.prototype.renderCell=function(value,cellContentContainer){let viewContainer;if(this.columnConfiguration.readonly||this.columnConfiguration.readOnly){viewContainer=this.createViewContainer(cellContentContainer,value)
|
||||
cellContentContainer.classList.add('readonly');cellContentContainer.setAttribute('tabindex',0);return;}viewContainer=this.createViewContainer(cellContentContainer,'...')
|
||||
this.fetchOptions(cellContentContainer.parentNode,function renderCellFetchOptions(options){if(options[value]!==undefined)viewContainer.textContent=options[value]
|
||||
cellContentContainer.setAttribute('tabindex',0)})}
|
||||
DropdownProcessor.prototype.onFocus=function(cellElement,isClick){if(this.activeCell===cellElement){this.showDropdown()
|
||||
return}this.activeCell=cellElement
|
||||
if(!this.columnConfiguration.readonly&&!this.columnConfiguration.readOnly){var cellContentContainer=this.getCellContentContainer(cellElement)
|
||||
this.buildEditor(cellElement,cellContentContainer,isClick)
|
||||
if(!isClick){cellContentContainer.focus()}}else{this.getCellContentContainer(cellElement).focus()}}
|
||||
DropdownProcessor.prototype.onUnfocus=function(){if(!this.activeCell)return
|
||||
this.unregisterListHandlers()
|
||||
this.hideDropdown()
|
||||
this.itemListElement=null
|
||||
this.activeCell=null}
|
||||
DropdownProcessor.prototype.buildEditor=function(cellElement,cellContentContainer,isClick){var currentValue=this.tableObj.getCellValue(cellElement),containerPosition=this.getAbsolutePosition(cellContentContainer),self=this
|
||||
this.itemListElement=document.createElement('div')
|
||||
this.itemListElement.addEventListener('click',this.itemClickHandler)
|
||||
this.itemListElement.addEventListener('keydown',this.itemKeyDownHandler)
|
||||
this.itemListElement.addEventListener('mousemove',this.itemMouseMoveHandler)
|
||||
this.itemListElement.setAttribute('class','table-control-dropdown-list')
|
||||
this.itemListElement.style.width=cellContentContainer.offsetWidth+'px'
|
||||
this.itemListElement.style.left=containerPosition.left+'px'
|
||||
this.itemListElement.style.top=containerPosition.top-2+cellContentContainer.offsetHeight+'px'
|
||||
this.fetchOptions(cellElement,function renderCellFetchOptions(options){var listElement=document.createElement('ul')
|
||||
for(var value in options){var itemElement=document.createElement('li')
|
||||
itemElement.setAttribute('data-value',value)
|
||||
itemElement.textContent=options[value]
|
||||
itemElement.setAttribute('tabindex',0)
|
||||
if(value==currentValue)itemElement.setAttribute('class','selected')
|
||||
listElement.appendChild(itemElement)}self.itemListElement.appendChild(listElement)
|
||||
if(isClick)self.showDropdown()
|
||||
self=null})}
|
||||
DropdownProcessor.prototype.hideDropdown=function(){if(this.itemListElement&&this.activeCell&&this.itemListElement.parentNode){var cellContentContainer=this.getCellContentContainer(this.activeCell)
|
||||
cellContentContainer.setAttribute('data-dropdown-open','false')
|
||||
this.itemListElement.parentNode.removeChild(this.itemListElement)
|
||||
cellContentContainer.focus()}}
|
||||
DropdownProcessor.prototype.showDropdown=function(){if(this.itemListElement&&this.itemListElement.parentNode!==document.body){this.getCellContentContainer(this.activeCell).setAttribute('data-dropdown-open','true')
|
||||
document.body.appendChild(this.itemListElement)
|
||||
var activeItemElement=this.itemListElement.querySelector('ul li.selected')
|
||||
if(!activeItemElement){activeItemElement=this.itemListElement.querySelector('ul li:first-child')
|
||||
if(activeItemElement)activeItemElement.setAttribute('class','selected')}if(activeItemElement){window.setTimeout(function(){activeItemElement.focus()},0)}}}
|
||||
DropdownProcessor.prototype.fetchOptions=function(cellElement,onSuccess){if(this.columnConfiguration.options){onSuccess(this.columnConfiguration.options)}else{var row=cellElement.parentNode,cachingKey=this.createOptionsCachingKey(row),viewContainer=this.getViewContainer(cellElement)
|
||||
viewContainer.setAttribute('class','loading')
|
||||
if(!this.cachedOptionPromises[cachingKey]){var requestData={column:this.columnName,rowData:this.tableObj.getRowData(row)},handlerName=this.tableObj.getAlias()+'::onGetDropdownOptions'
|
||||
this.cachedOptionPromises[cachingKey]=this.tableObj.$el.request(handlerName,{data:requestData})}this.cachedOptionPromises[cachingKey].done(function onDropDownLoadOptionsSuccess(data){onSuccess(data.options)}).always(function onDropDownLoadOptionsAlways(){viewContainer.setAttribute('class','')})}}
|
||||
DropdownProcessor.prototype.createOptionsCachingKey=function(row){var cachingKey='non-dependent',dependsOn=this.columnConfiguration.dependsOn
|
||||
if(dependsOn){if(typeof dependsOn=='object'){for(var i=0,len=dependsOn.length;i<len;i++)cachingKey+=dependsOn[i]+this.tableObj.getRowCellValueByColumnName(row,dependsOn[i])}else cachingKey=dependsOn+this.tableObj.getRowCellValueByColumnName(row,dependsOn)}return cachingKey}
|
||||
DropdownProcessor.prototype.getAbsolutePosition=function(element){var top=document.body.scrollTop,left=0
|
||||
do{top+=element.offsetTop||0;top-=element.scrollTop||0;left+=element.offsetLeft||0;element=element.offsetParent;}while(element)return{top:top,left:left}}
|
||||
DropdownProcessor.prototype.updateCellFromFocusedItem=function(focusedItem){if(!focusedItem){focusedItem=this.findFocusedItem();}this.setSelectedItem(focusedItem);}
|
||||
DropdownProcessor.prototype.findSelectedItem=function(){if(this.itemListElement)return this.itemListElement.querySelector('ul li.selected')
|
||||
return null}
|
||||
DropdownProcessor.prototype.setSelectedItem=function(item){if(!this.itemListElement)return null;if(item.tagName=='LI'&&this.itemListElement.contains(item)){this.itemListElement.querySelectorAll('ul li').forEach(function(option){option.removeAttribute('class');});item.setAttribute('class','selected');}this.tableObj.setCellValue(this.activeCell,item.getAttribute('data-value'))
|
||||
this.setViewContainerValue(this.activeCell,item.textContent)}
|
||||
DropdownProcessor.prototype.findFocusedItem=function(){if(this.itemListElement)return this.itemListElement.querySelector('ul li:focus')
|
||||
return null}
|
||||
DropdownProcessor.prototype.onItemClick=function(ev){var target=this.tableObj.getEventTarget(ev)
|
||||
if(target.tagName=='LI'){target.focus();this.updateCellFromFocusedItem(target)
|
||||
this.hideDropdown()}}
|
||||
DropdownProcessor.prototype.onItemKeyDown=function(ev){if(!this.itemListElement)return
|
||||
if(ev.key==='ArrowDown'||ev.key==='ArrowUp'){var focused=this.findFocusedItem(),newFocusedItem=focused.nextElementSibling
|
||||
if(ev.key==='ArrowUp')newFocusedItem=focused.previousElementSibling
|
||||
if(newFocusedItem){newFocusedItem.focus()}return}if(ev.key==='Enter'||ev.key==='(Space character)'||ev.key==='Spacebar'||ev.key===' '){this.updateCellFromFocusedItem()
|
||||
this.hideDropdown()
|
||||
return}if(ev.key==='Tab'){this.updateCellFromFocusedItem()
|
||||
this.tableObj.navigation.navigateNext(ev)
|
||||
this.tableObj.stopEvent(ev)
|
||||
return}if(ev.key==='Escape'){this.hideDropdown()
|
||||
return}this.searchByTextInput(ev,true);}
|
||||
DropdownProcessor.prototype.onItemMouseMove=function(ev){if(!this.itemListElement)return
|
||||
var target=this.tableObj.getEventTarget(ev)
|
||||
if(target.tagName=='LI'){target.focus();}}
|
||||
DropdownProcessor.prototype.onKeyDown=function(ev){if(!this.itemListElement)return
|
||||
if((ev.key==='(Space character)'||ev.key==='Spacebar'||ev.key===' ')&&!this.searching){this.showDropdown()}else if(ev.key==='ArrowDown'||ev.key==='ArrowUp'){var selected=this.findSelectedItem(),newSelectedItem;if(!selected){if(ev.key==='ArrowUp'){return false}newSelectedItem=this.itemListElement.querySelector('ul li:first-child')}else{newSelectedItem=selected.nextElementSibling
|
||||
if(ev.key==='ArrowUp')newSelectedItem=selected.previousElementSibling}if(newSelectedItem){this.setSelectedItem(newSelectedItem);}return false}else{this.searchByTextInput(ev);}}
|
||||
DropdownProcessor.prototype.onRowValueChanged=function(columnName,cellElement){if(!this.columnConfiguration.dependsOn)return
|
||||
var dependsOnColumn=false,dependsOn=this.columnConfiguration.dependsOn
|
||||
if(typeof dependsOn=='object'){for(var i=0,len=dependsOn.length;i<len;i++){if(dependsOn[i]==columnName){dependsOnColumn=true
|
||||
break}}}else{dependsOnColumn=dependsOn==columnName}if(!dependsOnColumn)return
|
||||
var currentValue=this.tableObj.getCellValue(cellElement),viewContainer=this.getViewContainer(cellElement)
|
||||
this.fetchOptions(cellElement,function rowValueChangedFetchOptions(options){var value=options[currentValue]!==undefined?options[currentValue]:'...'
|
||||
viewContainer.textContent=value
|
||||
viewContainer=null})}
|
||||
DropdownProcessor.prototype.elementBelongsToProcessor=function(element){if(!this.itemListElement)return false
|
||||
return this.tableObj.parentContainsElement(this.itemListElement,element)}
|
||||
DropdownProcessor.prototype.searchByTextInput=function(ev,focusOnly){if(focusOnly===undefined){focusOnly=false;}var character=ev.key;if(character.length===1||character==='Space'){if(!this.searching){this.searching=true;this.searchQuery='';}this.searchQuery+=(character==='Space')?' ':character;var validItem=null;var query=this.searchQuery;this.itemListElement.querySelectorAll('ul li').forEach(function(item){if(validItem===null&&item.dataset.value&&item.dataset.value.toLowerCase().indexOf(query.toLowerCase())===0){validItem=item;}});if(validItem){if(focusOnly===true){validItem.focus();}else{this.setSelectedItem(validItem);}if(this.searchInterval){clearTimeout(this.searchInterval);}this.searchInterval=setTimeout(this.cancelTextSearch.bind(this),1000);}else{this.cancelTextSearch();}}}
|
||||
DropdownProcessor.prototype.cancelTextSearch=function(){this.searching=false;this.searchQuery=null;this.searchInterval=null;}
|
||||
$.wn.table.processor.dropdown=DropdownProcessor;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.processor===undefined)throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.wn.table.processor.string,BaseProto=Base.prototype
|
||||
var AutocompleteProcessor=function(tableObj,columnName,columnConfiguration){this.cachedOptionPromises={}
|
||||
Base.call(this,tableObj,columnName,columnConfiguration)}
|
||||
AutocompleteProcessor.prototype=Object.create(BaseProto)
|
||||
AutocompleteProcessor.prototype.constructor=AutocompleteProcessor
|
||||
AutocompleteProcessor.prototype.dispose=function(){this.cachedOptionPromises=null
|
||||
BaseProto.dispose.call(this)}
|
||||
AutocompleteProcessor.prototype.onUnfocus=function(){if(!this.activeCell)return
|
||||
this.removeAutocomplete()
|
||||
BaseProto.onUnfocus.call(this)}
|
||||
AutocompleteProcessor.prototype.renderCell=function(value,cellContentContainer){BaseProto.renderCell.call(this,value,cellContentContainer)}
|
||||
AutocompleteProcessor.prototype.buildEditor=function(cellElement,cellContentContainer,isClick){BaseProto.buildEditor.call(this,cellElement,cellContentContainer,isClick)
|
||||
var self=this
|
||||
this.fetchOptions(cellElement,function autocompleteFetchOptions(options){self.buildAutoComplete(options)
|
||||
self=null})}
|
||||
AutocompleteProcessor.prototype.fetchOptions=function(cellElement,onSuccess){if(this.columnConfiguration.options){if(onSuccess!==undefined){onSuccess(this.columnConfiguration.options)}}else{if(this.triggerGetOptions(onSuccess)===false){return}var row=cellElement.parentNode,cachingKey=this.createOptionsCachingKey(row),viewContainer=this.getViewContainer(cellElement)
|
||||
$.wn.foundation.element.addClass(viewContainer,'loading')
|
||||
if(!this.cachedOptionPromises[cachingKey]){var requestData={column:this.columnName,rowData:this.tableObj.getRowData(row)},handlerName=this.tableObj.getAlias()+'::onGetAutocompleteOptions'
|
||||
this.cachedOptionPromises[cachingKey]=this.tableObj.$el.request(handlerName,{data:requestData})}this.cachedOptionPromises[cachingKey].done(function onAutocompleteLoadOptionsSuccess(data){if(onSuccess!==undefined){onSuccess(data.options)}}).always(function onAutocompleteLoadOptionsAlways(){$.wn.foundation.element.removeClass(viewContainer,'loading')})}}
|
||||
AutocompleteProcessor.prototype.createOptionsCachingKey=function(row){var cachingKey='non-dependent',dependsOn=this.columnConfiguration.dependsOn
|
||||
if(dependsOn){if(typeof dependsOn=='object'){for(var i=0,len=dependsOn.length;i<len;i++)cachingKey+=dependsOn[i]+this.tableObj.getRowCellValueByColumnName(row,dependsOn[i])}else cachingKey=dependsOn+this.tableObj.getRowCellValueByColumnName(row,dependsOn)}return cachingKey}
|
||||
AutocompleteProcessor.prototype.triggerGetOptions=function(callback){var tableElement=this.tableObj.getElement()
|
||||
if(!tableElement){return}var optionsEvent=$.Event('autocompleteitems.oc.table'),values={}
|
||||
$(tableElement).trigger(optionsEvent,[{values:values,callback:callback,column:this.columnName,columnConfiguration:this.columnConfiguration}])
|
||||
if(optionsEvent.isDefaultPrevented()){return false}return true}
|
||||
AutocompleteProcessor.prototype.getInput=function(){if(!this.activeCell){return null}return this.activeCell.querySelector('.string-input')}
|
||||
AutocompleteProcessor.prototype.buildAutoComplete=function(items){if(!this.activeCell){return}var input=this.getInput()
|
||||
if(!input){return}if(items===undefined){items=[]}$(input).autocomplete({source:this.prepareItems(items),matchWidth:true,menu:'<ul class="autocomplete dropdown-menu table-widget-autocomplete"></ul>',bodyContainer:true})}
|
||||
AutocompleteProcessor.prototype.prepareItems=function(items){var result={}
|
||||
if($.isArray(items)){for(var i=0,len=items.length;i<len;i++){result[items[i]]=items[i]}}else{result=items}return result}
|
||||
AutocompleteProcessor.prototype.removeAutocomplete=function(){var input=this.getInput()
|
||||
$(input).autocomplete('destroy')}
|
||||
$.wn.table.processor.autocomplete=AutocompleteProcessor;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)$.wn.table.validator={}
|
||||
var Base=function(options){this.options=options}
|
||||
Base.prototype.validate=function(value,rowData){if(this.options.requiredWith!==undefined&&!this.rowHasValue(this.options.requiredWith,rowData))return
|
||||
return this.validateValue(value,rowData)}
|
||||
Base.prototype.validateValue=function(value,rowData){}
|
||||
Base.prototype.trim=function(value){if(String.prototype.trim)return value.trim()
|
||||
return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,'')}
|
||||
Base.prototype.getMessage=function(defaultValue){if(this.options.message!==undefined)return this.options.message
|
||||
return defaultValue}
|
||||
Base.prototype.rowHasValue=function(columnName,rowData){if(rowData[columnName]===undefined)return false
|
||||
if(typeof rowData[columnName]=='boolean')return rowData[columnName]
|
||||
var value=this.trim(String(rowData[columnName]))
|
||||
return value.length>0}
|
||||
$.wn.table.validator.base=Base;}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");var Base=$.wn.table.validator.base,BaseProto=Base.prototype
|
||||
var Required=function(options){Base.call(this,options)};Required.prototype=Object.create(BaseProto)
|
||||
Required.prototype.constructor=Required
|
||||
Required.prototype.validateValue=function(value,rowData){value=this.trim(value)
|
||||
if(value.length===0)return this.getMessage("The value should not be empty.")
|
||||
return}
|
||||
$.wn.table.validator.required=Required}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");var Base=$.wn.table.validator.base,BaseProto=Base.prototype
|
||||
var BaseNumber=function(options){Base.call(this,options)};BaseNumber.prototype=Object.create(BaseProto)
|
||||
BaseNumber.prototype.constructor=BaseNumber
|
||||
BaseNumber.prototype.doCommonChecks=function(value){if(this.options.min!==undefined||this.options.max!==undefined){if(this.options.min!==undefined){if(this.options.min.value===undefined)throw new Error('The min.value parameter is not defined in the table validator configuration')
|
||||
if(value<this.options.min.value){return this.options.min.message!==undefined?this.options.min.message:"The value should not be less than "+this.options.min.value}}if(this.options.max!==undefined){if(this.options.max.value===undefined)throw new Error('The max.value parameter is not defined in the table validator configuration')
|
||||
if(value>this.options.max.value){return this.options.max.message!==undefined?this.options.max.message:"The value should not be more than "+this.options.max.value}}}return}
|
||||
$.wn.table.validator.baseNumber=BaseNumber}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");if($.wn.table.validator.baseNumber===undefined)throw new Error("The $.wn.table.validator.baseNumber namespace is not defined. Make sure that the table.validator.baseNumber.js script is loaded.");var Base=$.wn.table.validator.baseNumber,BaseProto=Base.prototype
|
||||
var Integer=function(options){Base.call(this,options)};Integer.prototype=Object.create(BaseProto)
|
||||
Integer.prototype.constructor=Integer
|
||||
Integer.prototype.validateValue=function(value,rowData){value=this.trim(value)
|
||||
if(value.length==0)return
|
||||
var testResult=this.options.allowNegative?/^\-?[0-9]*$/.test(value):/^[0-9]*$/.test(value)
|
||||
if(!testResult){var defaultMessage=this.options.allowNegative?'The value should be an integer.':'The value should be a positive integer';return this.getMessage(defaultMessage)}return this.doCommonChecks(parseInt(value))}
|
||||
$.wn.table.validator.integer=Integer}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");if($.wn.table.validator.baseNumber===undefined)throw new Error("The $.wn.table.validator.baseNumber namespace is not defined. Make sure that the table.validator.baseNumber.js script is loaded.");var Base=$.wn.table.validator.baseNumber,BaseProto=Base.prototype
|
||||
var Float=function(options){Base.call(this,options)};Float.prototype=Object.create(BaseProto)
|
||||
Float.prototype.constructor=Float
|
||||
Float.prototype.validateValue=function(value,rowData){value=this.trim(value)
|
||||
if(value.length==0)return
|
||||
var testResult=this.options.allowNegative?/^[-]?([0-9]+\.[0-9]+|[0-9]+)$/.test(value):/^([0-9]+\.[0-9]+|[0-9]+)$/.test(value)
|
||||
if(!testResult){var defaultMessage=this.options.allowNegative?'The value should be a floating point number.':'The value should be a positive floating point number';return this.getMessage(defaultMessage)}return this.doCommonChecks(parseFloat(value))}
|
||||
$.wn.table.validator.float=Float}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");var Base=$.wn.table.validator.base,BaseProto=Base.prototype
|
||||
var Length=function(options){Base.call(this,options)};Length.prototype=Object.create(BaseProto)
|
||||
Length.prototype.constructor=Length
|
||||
Length.prototype.validateValue=function(value,rowData){value=this.trim(value)
|
||||
if(value.length==0)return
|
||||
if(this.options.min!==undefined||this.options.max!==undefined){if(this.options.min!==undefined){if(this.options.min.value===undefined)throw new Error('The min.value parameter is not defined in the Length table validator configuration')
|
||||
if(value.length<this.options.min.value){return this.options.min.message!==undefined?this.options.min.message:"The string should not be shorter than "+this.options.min.value}}if(this.options.max!==undefined){if(this.options.max.value===undefined)throw new Error('The max.value parameter is not defined in the Length table validator configuration')
|
||||
if(value.length>this.options.max.value){return this.options.max.message!==undefined?this.options.max.message:"The string should not be longer than "+this.options.max.value}}}return}
|
||||
$.wn.table.validator.length=Length}(window.jQuery);+function($){"use strict";if($.wn.table===undefined)throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");if($.wn.table.validator===undefined)throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");var Base=$.wn.table.validator.base,BaseProto=Base.prototype
|
||||
var Regex=function(options){Base.call(this,options)};Regex.prototype=Object.create(BaseProto)
|
||||
Regex.prototype.constructor=Regex
|
||||
Regex.prototype.validateValue=function(value,rowData){value=this.trim(value)
|
||||
if(value.length==0)return
|
||||
if(this.options.pattern===undefined)throw new Error('The pattern parameter is not defined in the Regex table validator configuration')
|
||||
var regexObj=new RegExp(this.options.pattern,this.options.modifiers)
|
||||
if(!regexObj.test(value))return this.getMessage("Invalid value format.")
|
||||
return}
|
||||
$.wn.table.validator.regex=Regex}(window.jQuery);
|
||||
28
modules/backend/widgets/table/assets/js/build.js
Normal file
28
modules/backend/widgets/table/assets/js/build.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This is a bundle file, you can compile this by running
|
||||
*
|
||||
* php artisan winter:util compile assets
|
||||
*
|
||||
* @see build-min.js
|
||||
*
|
||||
|
||||
=require table.js
|
||||
=require table.helper.navigation.js
|
||||
=require table.helper.search.js
|
||||
=require table.datasource.base.js
|
||||
=require table.datasource.client.js
|
||||
=require table.datasource.server.js
|
||||
=require table.processor.base.js
|
||||
=require table.processor.string.js
|
||||
=require table.processor.checkbox.js
|
||||
=require table.processor.dropdown.js
|
||||
=require table.processor.autocomplete.js
|
||||
=require table.validator.base.js
|
||||
=require table.validator.required.js
|
||||
=require table.validator.basenumber.js
|
||||
=require table.validator.integer.js
|
||||
=require table.validator.float.js
|
||||
=require table.validator.length.js
|
||||
=require table.validator.regex.js
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Base class for the table data sources.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// DATASOURCE NAMESPACES
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.datasource === undefined)
|
||||
$.wn.table.datasource = {}
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = function(tableObj) {
|
||||
//
|
||||
// State properties
|
||||
//
|
||||
|
||||
this.tableObj = tableObj
|
||||
}
|
||||
|
||||
Base.prototype.dispose = function() {
|
||||
this.tableObj = null
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetches records from the underlying data source and
|
||||
* passes them to the onSuccess callback function.
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
* Each record contains the key field which uniquely identifies
|
||||
* the record. The name of the key field is defined with the table
|
||||
* widget options.
|
||||
*/
|
||||
Base.prototype.getRecords = function(offset, count, onSuccess) {
|
||||
onSuccess([])
|
||||
}
|
||||
|
||||
/*
|
||||
* Identical to getRecords except using a search query.
|
||||
*/
|
||||
Base.prototype.searchRecords = function(query, offset, count, onSuccess) {
|
||||
onSuccess([])
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a record with the passed data and returns the updated page records
|
||||
* to the onSuccess callback function.
|
||||
*
|
||||
* - recordData - the record fields
|
||||
* - placement - "bottom" (the end of the data set), "above", "below"
|
||||
* - relativeToKey - a row key, required if the placement is not "bottom"
|
||||
* - offset - the current page's first record index (zero-based)
|
||||
* - count - number of records to return
|
||||
* - onSuccess - a callback function to execute when the updated data gets available.
|
||||
*
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
*/
|
||||
Base.prototype.createRecord = function(recordData, placement, relativeToKey, offset, count, onSuccess) {
|
||||
onSuccess([], 0)
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates a record with the specified key with the passed data
|
||||
*
|
||||
* - key - the record key in the dataset (primary key, etc)
|
||||
* - recordData - the record fields.
|
||||
*/
|
||||
Base.prototype.updateRecord = function(key, recordData) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes a record with the specified key.
|
||||
*
|
||||
* - key - the record key in the dataset (primary key, etc).
|
||||
* - newRecordData - replacement record to add to the dataset if the deletion
|
||||
* empties it.
|
||||
* - offset - the current page's first record key (zero-based)
|
||||
* - count - number of records to return
|
||||
* - onSuccess - a callback function to execute when the updated data gets available.
|
||||
*
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
*/
|
||||
Base.prototype.deleteRecord = function(key, newRecordData, offset, count, onSuccess) {
|
||||
onSuccess([], 0)
|
||||
}
|
||||
|
||||
$.wn.table.datasource.base = Base;
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Client memory data source for the table control.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.datasource === undefined)
|
||||
throw new Error("The $.wn.table.datasource namespace is not defined. Make sure that the table.datasource.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.datasource.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Client = function(tableObj) {
|
||||
Base.call(this, tableObj)
|
||||
|
||||
var dataString = tableObj.getElement().getAttribute('data-data')
|
||||
|
||||
if (dataString === null || dataString === undefined)
|
||||
throw new Error('The required data-data attribute is not found on the table control element.')
|
||||
|
||||
this.data = JSON.parse(dataString)
|
||||
};
|
||||
|
||||
Client.prototype = Object.create(BaseProto)
|
||||
Client.prototype.constructor = Client
|
||||
|
||||
Client.prototype.dispose = function() {
|
||||
BaseProto.dispose.call(this)
|
||||
this.data = null
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetches records from the underlying data source and
|
||||
* passes them to the onSuccess callback function.
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
* Each record contains the key field which uniquely identifies
|
||||
* the record. The name of the key field is defined with the table
|
||||
* widget options.
|
||||
*/
|
||||
Client.prototype.getRecords = function(offset, count, onSuccess) {
|
||||
if (!count) {
|
||||
// Return all records
|
||||
onSuccess(this.data, this.data.length)
|
||||
}
|
||||
else {
|
||||
// Return a subset of records
|
||||
onSuccess(this.data.slice(offset, offset+count), this.data.length)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a record with the passed data and returns the updated page records
|
||||
* to the onSuccess callback function.
|
||||
*
|
||||
* - recordData - the record fields
|
||||
* - placement - "bottom" (the end of the data set), "above", "below"
|
||||
* - relativeToKey - a row key, required if the placement is not "bottom"
|
||||
* - offset - the current page's first record index (zero-based)
|
||||
* - count - number of records to return
|
||||
* - onSuccess - a callback function to execute when the updated data gets available.
|
||||
*
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
*/
|
||||
Client.prototype.createRecord = function(recordData, placement, relativeToKey, offset, count, onSuccess) {
|
||||
if (placement === 'bottom') {
|
||||
// Add record to the bottom of the dataset
|
||||
this.data.push(recordData)
|
||||
}
|
||||
else if (placement == 'above' || placement == 'below') {
|
||||
// Add record above or below the passed record key
|
||||
var recordIndex = this.getIndexOfKey(relativeToKey)
|
||||
if (placement == 'below')
|
||||
recordIndex ++
|
||||
|
||||
this.data.splice(recordIndex, 0, recordData)
|
||||
}
|
||||
|
||||
this.getRecords(offset, count, onSuccess)
|
||||
}
|
||||
|
||||
/*
|
||||
* Identical to getRecords except using a search query.
|
||||
*/
|
||||
Client.prototype.searchRecords = function(query, offset, count, onSuccess) {
|
||||
const searchFields = this.tableObj.search.getSearchableColumns();
|
||||
|
||||
const matched = this.data.filter(function(record) {
|
||||
for (let i = 0; i < searchFields.length; i++) {
|
||||
const value = record[searchFields[i]];
|
||||
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value.toString().toLowerCase().includes(query.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (matched.length === 0) {
|
||||
onSuccess([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
// Return all records
|
||||
onSuccess(matched, matched.length);
|
||||
} else {
|
||||
// Return a subset of records
|
||||
onSuccess(matched.slice(offset, offset + count), matched.length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates a record with the specified key with the passed data
|
||||
*
|
||||
* - key - the record key in the dataset (primary key, etc)
|
||||
* - recordData - the record fields.
|
||||
*/
|
||||
Client.prototype.updateRecord = function(key, recordData) {
|
||||
var recordIndex = this.getIndexOfKey(key)
|
||||
|
||||
if (recordIndex !== -1) {
|
||||
recordData[this.tableObj.options.keyColumn] = key
|
||||
this.data[recordIndex] = recordData
|
||||
}
|
||||
else {
|
||||
throw new Error('Record with they key '+key+ ' is not found in the data set')
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes a record with the specified key.
|
||||
*
|
||||
* - key - the record key in the dataset (primary key, etc).
|
||||
* - newRecordData - replacement record to add to the dataset if the deletion
|
||||
* empties it.
|
||||
* - offset - the current page's first record key (zero-based)
|
||||
* - count - number of records to return
|
||||
* - onSuccess - a callback function to execute when the updated data gets available.
|
||||
*
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
*/
|
||||
Client.prototype.deleteRecord = function(key, newRecordData, offset, count, onSuccess) {
|
||||
var recordIndex = this.getIndexOfKey(key)
|
||||
|
||||
if (recordIndex !== -1) {
|
||||
this.data.splice(recordIndex, 1)
|
||||
|
||||
if (this.data.length == 0)
|
||||
this.data.push(newRecordData)
|
||||
|
||||
this.getRecords(offset, count, onSuccess)
|
||||
}
|
||||
else {
|
||||
throw new Error('Record with they key '+key+ ' is not found in the data set')
|
||||
}
|
||||
}
|
||||
|
||||
Client.prototype.getIndexOfKey = function(key) {
|
||||
var keyColumn = this.tableObj.options.keyColumn
|
||||
|
||||
return this.data.map(function(record) {
|
||||
return record[keyColumn] + ""
|
||||
}).indexOf(key + "")
|
||||
}
|
||||
|
||||
Client.prototype.getAllData = function() {
|
||||
return this.data
|
||||
}
|
||||
|
||||
$.wn.table.datasource.client = Client
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Server memory data source for the table control.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.datasource === undefined)
|
||||
throw new Error("The $.wn.table.datasource namespace is not defined. Make sure that the table.datasource.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.datasource.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Server = function(tableObj) {
|
||||
Base.call(this, tableObj)
|
||||
|
||||
var dataString = tableObj.getElement().getAttribute('data-data')
|
||||
|
||||
if (dataString === null || dataString === undefined)
|
||||
throw new Error('The required data-data attribute is not found on the table control element.')
|
||||
|
||||
this.data = JSON.parse(dataString)
|
||||
};
|
||||
|
||||
Server.prototype = Object.create(BaseProto)
|
||||
Server.prototype.constructor = Server
|
||||
|
||||
Server.prototype.dispose = function() {
|
||||
BaseProto.dispose.call(this)
|
||||
this.data = null
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetches records from the underlying data source and
|
||||
* passes them to the onSuccess callback function.
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
* Each record contains the key field which uniquely identifies
|
||||
* the record. The name of the key field is defined with the table
|
||||
* widget options.
|
||||
*/
|
||||
Server.prototype.getRecords = function(offset, count, onSuccess) {
|
||||
var handlerName = this.tableObj.getAlias()+'::onServerGetRecords'
|
||||
this.tableObj.$el.request(handlerName, {
|
||||
data: {
|
||||
offset: offset,
|
||||
count: count
|
||||
}
|
||||
}).done(function(data) {
|
||||
onSuccess(data.records, data.count)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Identical to getRecords except using a search query.
|
||||
*/
|
||||
Server.prototype.searchRecords = function(query, offset, count, onSuccess) {
|
||||
var handlerName = this.tableObj.getAlias()+'::onServerSearchRecords'
|
||||
this.tableObj.$el.request(handlerName, {
|
||||
data: {
|
||||
query: query,
|
||||
offset: offset,
|
||||
count: count
|
||||
}
|
||||
}).done(function(data) {
|
||||
onSuccess(data.records, data.count)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a record with the passed data and returns the updated page records
|
||||
* to the onSuccess callback function.
|
||||
*
|
||||
* - recordData - the record fields
|
||||
* - placement - "bottom" (the end of the data set), "above", "below"
|
||||
* - relativeToKey - a row key, required if the placement is not "bottom"
|
||||
* - offset - the current page's first record index (zero-based)
|
||||
* - count - number of records to return
|
||||
* - onSuccess - a callback function to execute when the updated data gets available.
|
||||
*
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
*/
|
||||
Server.prototype.createRecord = function(recordData, placement, relativeToKey, offset, count, onSuccess) {
|
||||
var handlerName = this.tableObj.getAlias()+'::onServerCreateRecord'
|
||||
this.tableObj.$el.request(handlerName, {
|
||||
data: {
|
||||
recordData: recordData,
|
||||
placement: placement,
|
||||
relativeToKey: relativeToKey,
|
||||
offset: offset,
|
||||
count: count
|
||||
}
|
||||
}).done(function(data) {
|
||||
onSuccess(data.records, data.count)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates a record with the specified key with the passed data
|
||||
*
|
||||
* - key - the record key in the dataset (primary key, etc)
|
||||
* - recordData - the record fields.
|
||||
*/
|
||||
Server.prototype.updateRecord = function(key, recordData) {
|
||||
var handlerName = this.tableObj.getAlias()+'::onServerUpdateRecord'
|
||||
this.tableObj.$el.request(handlerName, {
|
||||
data: {
|
||||
key: key,
|
||||
recordData: recordData
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes a record with the specified key.
|
||||
*
|
||||
* - key - the record key in the dataset (primary key, etc).
|
||||
* - newRecordData - replacement record to add to the dataset if the deletion
|
||||
* empties it.
|
||||
* - offset - the current page's first record key (zero-based)
|
||||
* - count - number of records to return
|
||||
* - onSuccess - a callback function to execute when the updated data gets available.
|
||||
*
|
||||
* The onSuccess callback parameters: records, totalCount.
|
||||
*/
|
||||
Server.prototype.deleteRecord = function(key, newRecordData, offset, count, onSuccess) {
|
||||
var handlerName = this.tableObj.getAlias()+'::onServerDeleteRecord'
|
||||
this.tableObj.$el.request(handlerName, {
|
||||
data: {
|
||||
key: key,
|
||||
offset: offset,
|
||||
count: count
|
||||
}
|
||||
}).done(function(data) {
|
||||
onSuccess(data.records, data.count)
|
||||
})
|
||||
}
|
||||
|
||||
$.wn.table.datasource.server = Server
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Navigation helper for the table widget.
|
||||
* Implements the keyboard navigation within the current page
|
||||
* and pagination.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.helper === undefined)
|
||||
$.wn.table.helper = {}
|
||||
|
||||
// NAVIGATION CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Navigation = function(tableObj) {
|
||||
// Reference to the table object
|
||||
this.tableObj = tableObj
|
||||
|
||||
// The current page index
|
||||
this.pageIndex = 0
|
||||
|
||||
// Event handlers
|
||||
|
||||
// Number of pages in the pagination
|
||||
this.pageCount = 0
|
||||
|
||||
this.init()
|
||||
};
|
||||
|
||||
Navigation.prototype.init = function() {
|
||||
}
|
||||
|
||||
Navigation.prototype.dispose = function() {
|
||||
// Remove the reference to the table object
|
||||
this.tableObj = null
|
||||
}
|
||||
|
||||
// PAGINATION
|
||||
// ============================
|
||||
|
||||
Navigation.prototype.paginationEnabled = function() {
|
||||
return this.tableObj.options.recordsPerPage > 0;
|
||||
}
|
||||
|
||||
Navigation.prototype.getPageFirstRowOffset = function() {
|
||||
return this.pageIndex * this.tableObj.options.recordsPerPage
|
||||
}
|
||||
|
||||
Navigation.prototype.buildPagination = function(recordCount) {
|
||||
if (!this.paginationEnabled())
|
||||
return
|
||||
|
||||
var paginationContainer = this.tableObj.getElement().querySelector('.pagination'),
|
||||
newPaginationContainer = false,
|
||||
curRecordCount = 0
|
||||
|
||||
this.pageCount = this.calculatePageCount(recordCount, this.tableObj.options.recordsPerPage)
|
||||
|
||||
if (!paginationContainer) {
|
||||
paginationContainer = document.createElement('div')
|
||||
paginationContainer.setAttribute('class', 'pagination')
|
||||
newPaginationContainer = true
|
||||
}
|
||||
else {
|
||||
curRecordCount = this.getRecordCount(paginationContainer)
|
||||
}
|
||||
|
||||
// Generate the new page list only if the record count has changed
|
||||
if (newPaginationContainer || curRecordCount != recordCount) {
|
||||
paginationContainer.setAttribute('data-record-count', recordCount)
|
||||
|
||||
var pageList = this.buildPaginationLinkList(
|
||||
recordCount,
|
||||
this.tableObj.options.recordsPerPage,
|
||||
this.pageIndex
|
||||
)
|
||||
|
||||
if (!newPaginationContainer) {
|
||||
paginationContainer.replaceChild(pageList, paginationContainer.children[0])
|
||||
}
|
||||
else {
|
||||
paginationContainer.appendChild(pageList)
|
||||
this.tableObj.getElement().appendChild(paginationContainer)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Do not re-generate the pages if the record count hasn't changed,
|
||||
// but mark the new active item in the pagination list
|
||||
this.markActiveLinkItem(paginationContainer, this.pageIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.prototype.calculatePageCount = function(recordCount, recordsPerPage) {
|
||||
var pageCount = Math.ceil(recordCount/recordsPerPage)
|
||||
|
||||
if (!pageCount)
|
||||
pageCount = 1
|
||||
|
||||
return pageCount
|
||||
}
|
||||
|
||||
Navigation.prototype.getRecordCount = function(paginationContainer) {
|
||||
var container = paginationContainer ? paginationContainer : this.tableObj.getElement().querySelector('.pagination')
|
||||
|
||||
return parseInt(container.getAttribute('data-record-count'))
|
||||
}
|
||||
|
||||
Navigation.prototype.buildPaginationLinkList = function(recordCount, recordsPerPage, pageIndex) {
|
||||
// This method could be refactored and moved to a pagination
|
||||
// helper if we want to support other pagination markup options.
|
||||
|
||||
var pageCount = this.calculatePageCount(recordCount, recordsPerPage),
|
||||
pageList = document.createElement('ul')
|
||||
|
||||
for (var i=0; i < pageCount; i++) {
|
||||
var item = document.createElement('li'),
|
||||
link = document.createElement('a')
|
||||
|
||||
if (i == pageIndex)
|
||||
item.setAttribute('class', 'active')
|
||||
|
||||
|
||||
link.innerText = i+1
|
||||
link.setAttribute('data-page-index', i)
|
||||
link.setAttribute('href', '#')
|
||||
|
||||
item.appendChild(link)
|
||||
pageList.appendChild(item)
|
||||
|
||||
$(link).addClass('pagination-link')
|
||||
}
|
||||
|
||||
return pageList
|
||||
}
|
||||
|
||||
Navigation.prototype.markActiveLinkItem = function(paginationContainer, pageIndex) {
|
||||
// This method could be refactored and moved to a pagination
|
||||
// helper if we want to support other pagination markup options.
|
||||
|
||||
var activeItem = paginationContainer.querySelector('.active'),
|
||||
list = paginationContainer.children[0]
|
||||
|
||||
activeItem.setAttribute('class', '')
|
||||
|
||||
for (var i=0, len = list.children.length; i < len; i++) {
|
||||
if (i == pageIndex) {
|
||||
list.children[i].setAttribute('class', 'active')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.prototype.gotoPage = function(pageIndex, onSuccess) {
|
||||
this.tableObj.unfocusTable()
|
||||
|
||||
if (!this.tableObj.validate())
|
||||
return
|
||||
|
||||
this.pageIndex = pageIndex
|
||||
|
||||
this.tableObj.updateDataTable(onSuccess)
|
||||
}
|
||||
|
||||
Navigation.prototype.getRowCountOnPage = function(cellElement) {
|
||||
return this.tableObj.getDataTableBody().children.length
|
||||
}
|
||||
|
||||
Navigation.prototype.getNewRowPage = function(placement, currentRowIndex) {
|
||||
var curRecordCount = this.getRecordCount()
|
||||
|
||||
if (placement === 'bottom')
|
||||
return this.calculatePageCount(curRecordCount + 1, this.tableObj.options.recordsPerPage) - 1
|
||||
|
||||
// When a row is added above a current row, the current row just moves down,
|
||||
// so it's safe to return the current page index
|
||||
if (placement == 'above')
|
||||
return this.pageIndex
|
||||
|
||||
if (placement == 'below') {
|
||||
if (currentRowIndex == (this.tableObj.options.recordsPerPage - 1))
|
||||
return this.pageIndex + 1
|
||||
|
||||
return this.pageIndex
|
||||
}
|
||||
|
||||
return this.pageIndex
|
||||
}
|
||||
|
||||
Navigation.prototype.getPageAfterDeletion = function(currentRowIndex) {
|
||||
if (currentRowIndex == 0 && this.getRowCountOnPage() == 1)
|
||||
return this.pageIndex == 0 ? 0 : this.pageIndex - 1
|
||||
|
||||
return this.pageIndex
|
||||
}
|
||||
|
||||
// KEYBOARD NAVIGATION
|
||||
// ============================
|
||||
|
||||
Navigation.prototype.navigateDown = function(ev, forceCellIndex) {
|
||||
if (!this.tableObj.activeCell)
|
||||
return
|
||||
|
||||
if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'down'))
|
||||
return
|
||||
|
||||
var row = this.tableObj.activeCell.parentNode,
|
||||
newRow = !ev.shiftKey
|
||||
? row.nextElementSibling
|
||||
: row.parentNode.children[row.parentNode.children.length - 1],
|
||||
cellIndex = forceCellIndex !== undefined
|
||||
? forceCellIndex
|
||||
: this.tableObj.activeCell.cellIndex
|
||||
|
||||
if (newRow) {
|
||||
var cell = newRow.children[cellIndex]
|
||||
|
||||
if (cell)
|
||||
this.tableObj.focusCell(cell)
|
||||
}
|
||||
else {
|
||||
// Try to switch to the next page if that's possible
|
||||
|
||||
if (!this.paginationEnabled())
|
||||
return
|
||||
|
||||
if (this.pageIndex < this.pageCount - 1) {
|
||||
var self = this
|
||||
|
||||
this.gotoPage(this.pageIndex + 1, function navDownPageSuccess() {
|
||||
self.focusCell('top', cellIndex)
|
||||
self = null
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.prototype.navigateUp = function(ev, forceCellIndex, isTab) {
|
||||
if (!this.tableObj.activeCell)
|
||||
return
|
||||
|
||||
if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'up'))
|
||||
return
|
||||
|
||||
var row = this.tableObj.activeCell.parentNode,
|
||||
newRow = (!ev.shiftKey || isTab)
|
||||
? row.previousElementSibling
|
||||
: row.parentNode.children[0],
|
||||
cellIndex = forceCellIndex !== undefined
|
||||
? forceCellIndex
|
||||
: this.tableObj.activeCell.cellIndex
|
||||
|
||||
if (newRow) {
|
||||
var cell = newRow.children[cellIndex]
|
||||
|
||||
if (cell)
|
||||
this.tableObj.focusCell(cell)
|
||||
}
|
||||
else {
|
||||
// Try to switch to the previous page if that's possible
|
||||
|
||||
if (!this.paginationEnabled())
|
||||
return
|
||||
|
||||
if (this.pageIndex > 0) {
|
||||
var self = this
|
||||
|
||||
this.gotoPage(this.pageIndex - 1, function navUpPageSuccess(){
|
||||
self.focusCell('bottom', cellIndex)
|
||||
self = null
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.prototype.navigateLeft = function(ev, isTab) {
|
||||
if (!this.tableObj.activeCell)
|
||||
return
|
||||
|
||||
if (!isTab && this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'left'))
|
||||
return
|
||||
|
||||
var row = this.tableObj.activeCell.parentNode,
|
||||
newIndex = (!ev.shiftKey || isTab)
|
||||
? this.tableObj.activeCell.cellIndex - 1
|
||||
: 0
|
||||
|
||||
var cell = row.children[newIndex]
|
||||
|
||||
if (cell) {
|
||||
this.tableObj.focusCell(cell)
|
||||
}
|
||||
else {
|
||||
// Try to navigate up if that's possible
|
||||
this.navigateUp(ev, row.children.length - 1, isTab)
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.prototype.navigateRight = function(ev, isTab) {
|
||||
if (!this.tableObj.activeCell)
|
||||
return
|
||||
|
||||
if (!isTab && this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'right'))
|
||||
return
|
||||
|
||||
var row = this.tableObj.activeCell.parentNode,
|
||||
newIndex = !ev.shiftKey
|
||||
? this.tableObj.activeCell.cellIndex + 1
|
||||
: row.children.length - 1
|
||||
|
||||
var cell = row.children[newIndex]
|
||||
|
||||
if (cell) {
|
||||
this.tableObj.focusCell(cell)
|
||||
}
|
||||
else {
|
||||
// Try to navigate down if that's possible
|
||||
this.navigateDown(ev, 0)
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.prototype.navigateNext = function(ev) {
|
||||
if (!this.tableObj.activeCell)
|
||||
return
|
||||
|
||||
if (this.tableObj.activeCellProcessor && !this.tableObj.activeCellProcessor.keyNavigationAllowed(ev, 'tab'))
|
||||
return
|
||||
|
||||
if (!ev.shiftKey)
|
||||
this.navigateRight(ev, true)
|
||||
else
|
||||
this.navigateLeft(ev, true)
|
||||
|
||||
this.tableObj.stopEvent(ev)
|
||||
}
|
||||
|
||||
Navigation.prototype.focusCell = function(rowReference, cellIndex) {
|
||||
var row = null,
|
||||
tbody = this.tableObj.getDataTableBody()
|
||||
|
||||
if (typeof rowReference === 'object') {
|
||||
row = rowReference
|
||||
}
|
||||
else {
|
||||
if (rowReference == 'bottom') {
|
||||
row = tbody.children[tbody.children.length-1]
|
||||
}
|
||||
else if (rowReference == 'top') {
|
||||
row = tbody.children[0]
|
||||
}
|
||||
}
|
||||
|
||||
if (!row)
|
||||
return
|
||||
|
||||
var cell = row.children[cellIndex]
|
||||
if (cell)
|
||||
this.tableObj.focusCell(cell)
|
||||
}
|
||||
|
||||
Navigation.prototype.focusCellInReplacedRow = function(rowIndex, cellIndex) {
|
||||
if (rowIndex == 0) {
|
||||
this.focusCell('top', cellIndex)
|
||||
}
|
||||
else {
|
||||
var focusRow = this.tableObj.findRowByIndex(rowIndex)
|
||||
|
||||
if (!focusRow)
|
||||
focusRow = this.tableObj.findRowByIndex(rowIndex-1)
|
||||
|
||||
if (focusRow)
|
||||
this.focusCell(focusRow, cellIndex)
|
||||
else
|
||||
this.focusCell('top', cellIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
Navigation.prototype.onKeydown = function(ev) {
|
||||
// The navigation object uses the table's keydown handler
|
||||
// and doesn't register own handler.
|
||||
|
||||
if (ev.key === 'ArrowDown')
|
||||
return this.navigateDown(ev)
|
||||
else if (ev.key === 'ArrowUp')
|
||||
return this.navigateUp(ev)
|
||||
else if (ev.key === 'ArrowLeft')
|
||||
return this.navigateLeft(ev)
|
||||
|
||||
if (ev.key === 'ArrowRight')
|
||||
return this.navigateRight(ev)
|
||||
|
||||
if (ev.key === 'Tab')
|
||||
return this.navigateNext(ev)
|
||||
}
|
||||
|
||||
Navigation.prototype.onClick = function(ev) {
|
||||
// The navigation object uses the table's click handler
|
||||
// and doesn't register own click handler.
|
||||
|
||||
var target = this.tableObj.getEventTarget(ev, 'A')
|
||||
|
||||
if (!target || !$(target).hasClass('pagination-link'))
|
||||
return
|
||||
|
||||
var pageIndex = parseInt(target.getAttribute('data-page-index'))
|
||||
|
||||
if (pageIndex === null)
|
||||
return
|
||||
|
||||
this.gotoPage(pageIndex)
|
||||
this.tableObj.stopEvent(ev)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
$.wn.table.helper.navigation = Navigation;
|
||||
|
||||
}(window.jQuery);
|
||||
134
modules/backend/widgets/table/assets/js/table.helper.search.js
Normal file
134
modules/backend/widgets/table/assets/js/table.helper.search.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Search helper for the table widget.
|
||||
* Implements searching within the table.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.helper === undefined)
|
||||
$.wn.table.helper = {}
|
||||
|
||||
// SEARCH CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Search = function(tableObj) {
|
||||
// Reference to the table object
|
||||
this.tableObj = tableObj
|
||||
|
||||
// The search form element
|
||||
this.searchForm = null
|
||||
this.searchInput = null
|
||||
|
||||
// Timer used for tracking input changes
|
||||
this.inputTrackTimer = null
|
||||
|
||||
// Event handlers
|
||||
|
||||
// Active search query
|
||||
this.activeQuery = null
|
||||
|
||||
this.isActive = false
|
||||
|
||||
this.init()
|
||||
};
|
||||
|
||||
Search.prototype.init = function() {
|
||||
}
|
||||
|
||||
Search.prototype.dispose = function() {
|
||||
// Remove the reference to the table object
|
||||
this.tableObj = null
|
||||
this.searchForm = null
|
||||
this.searchInput = null
|
||||
}
|
||||
|
||||
Search.prototype.buildSearchForm = function() {
|
||||
if (!this.searchEnabled())
|
||||
return
|
||||
|
||||
var el = this.tableObj.getElement(),
|
||||
toolbar = this.tableObj.getToolbar(),
|
||||
searchForm = toolbar.querySelector('.table-search')
|
||||
|
||||
if (!searchForm) {
|
||||
this.searchForm = $($('[data-table-toolbar-search]', el).html()).appendTo(toolbar).get(0)
|
||||
this.searchInput = $('.table-search-input', this.searchForm).get(0)
|
||||
}
|
||||
}
|
||||
|
||||
Search.prototype.getQuery = function() {
|
||||
return $.trim(this.activeQuery)
|
||||
}
|
||||
|
||||
Search.prototype.hasQuery = function() {
|
||||
return this.searchEnabled() && $.trim(this.activeQuery).length > 0
|
||||
}
|
||||
|
||||
Search.prototype.searchEnabled = function() {
|
||||
return this.tableObj.options.searching
|
||||
}
|
||||
|
||||
Search.prototype.getSearchableColumns = function() {
|
||||
const columns = [];
|
||||
|
||||
this.tableObj.options.columns.forEach(function(column) {
|
||||
if (column.type === 'checkbox') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!column.searchable) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(column.key);
|
||||
});
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
Search.prototype.performSearch = function(query, onSuccess) {
|
||||
var isDirty = this.activeQuery != query
|
||||
|
||||
this.activeQuery = query
|
||||
|
||||
if (isDirty) {
|
||||
this.tableObj.updateDataTable(onSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
Search.prototype.onKeydown = function(ev) {
|
||||
// The navigation object uses the table's keydown handler
|
||||
// and doesn't register own handler.
|
||||
|
||||
// Tab pressed
|
||||
if (ev.key === 'Tab') {
|
||||
this.onClick(ev)
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.isActive) {
|
||||
return
|
||||
}
|
||||
|
||||
var self = this
|
||||
this.inputTrackTimer = window.setTimeout(function() {
|
||||
self.performSearch(self.searchInput.value)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
Search.prototype.onClick = function(ev) {
|
||||
var target = this.tableObj.getEventTarget(ev, 'INPUT')
|
||||
this.isActive = target && $(target).hasClass('table-search-input')
|
||||
}
|
||||
|
||||
$.wn.table.helper.search = Search;
|
||||
|
||||
}(window.jQuery);
|
||||
1170
modules/backend/widgets/table/assets/js/table.js
Normal file
1170
modules/backend/widgets/table/assets/js/table.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Autocomplete cell processor for the table control.
|
||||
*/
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.processor === undefined)
|
||||
throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.processor.string,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var AutocompleteProcessor = function(tableObj, columnName, columnConfiguration) {
|
||||
//
|
||||
// State properties
|
||||
//
|
||||
|
||||
this.cachedOptionPromises = {}
|
||||
|
||||
//
|
||||
// Parent constructor
|
||||
//
|
||||
|
||||
Base.call(this, tableObj, columnName, columnConfiguration)
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype = Object.create(BaseProto)
|
||||
AutocompleteProcessor.prototype.constructor = AutocompleteProcessor
|
||||
|
||||
AutocompleteProcessor.prototype.dispose = function() {
|
||||
this.cachedOptionPromises = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* Forces the processor to hide the editor when the user navigates
|
||||
* away from the cell. Processors can update the sell value in this method.
|
||||
* Processors must clear the reference to the active cell in this method.
|
||||
*/
|
||||
AutocompleteProcessor.prototype.onUnfocus = function() {
|
||||
if (!this.activeCell)
|
||||
return
|
||||
|
||||
this.removeAutocomplete()
|
||||
|
||||
BaseProto.onUnfocus.call(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* Renders the cell in the normal (no edit) mode
|
||||
*/
|
||||
AutocompleteProcessor.prototype.renderCell = function(value, cellContentContainer) {
|
||||
BaseProto.renderCell.call(this, value, cellContentContainer)
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.buildEditor = function(cellElement, cellContentContainer, isClick) {
|
||||
BaseProto.buildEditor.call(this, cellElement, cellContentContainer, isClick)
|
||||
|
||||
var self = this
|
||||
|
||||
this.fetchOptions(cellElement, function autocompleteFetchOptions(options) {
|
||||
self.buildAutoComplete(options)
|
||||
|
||||
self = null
|
||||
})
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.fetchOptions = function(cellElement, onSuccess) {
|
||||
if (this.columnConfiguration.options) {
|
||||
if (onSuccess !== undefined) {
|
||||
onSuccess(this.columnConfiguration.options)
|
||||
}
|
||||
} else {
|
||||
// If options are not provided and not found in the cache,
|
||||
// request them from the server. For dependent autocomplete editors
|
||||
// the caching key contains the master column values.
|
||||
|
||||
if (this.triggerGetOptions(onSuccess) === false) {
|
||||
return
|
||||
}
|
||||
|
||||
var row = cellElement.parentNode,
|
||||
cachingKey = this.createOptionsCachingKey(row),
|
||||
viewContainer = this.getViewContainer(cellElement)
|
||||
|
||||
// Request options from the server. When the table widget builds,
|
||||
// multiple cells in the column could require loading the options.
|
||||
// The AJAX promises are cached here so that we have a single
|
||||
// request per caching key.
|
||||
|
||||
$.wn.foundation.element.addClass(viewContainer, 'loading')
|
||||
|
||||
if (!this.cachedOptionPromises[cachingKey]) {
|
||||
var requestData = {
|
||||
column: this.columnName,
|
||||
rowData: this.tableObj.getRowData(row)
|
||||
},
|
||||
handlerName = this.tableObj.getAlias()+'::onGetAutocompleteOptions'
|
||||
|
||||
this.cachedOptionPromises[cachingKey] = this.tableObj.$el.request(handlerName, {data: requestData})
|
||||
}
|
||||
|
||||
this.cachedOptionPromises[cachingKey].done(function onAutocompleteLoadOptionsSuccess(data){
|
||||
if (onSuccess !== undefined) {
|
||||
onSuccess(data.options)
|
||||
}
|
||||
}).always(function onAutocompleteLoadOptionsAlways(){
|
||||
$.wn.foundation.element.removeClass(viewContainer, 'loading')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.createOptionsCachingKey = function(row) {
|
||||
var cachingKey = 'non-dependent',
|
||||
dependsOn = this.columnConfiguration.dependsOn
|
||||
|
||||
if (dependsOn) {
|
||||
if (typeof dependsOn == 'object') {
|
||||
for (var i = 0, len = dependsOn.length; i < len; i++ )
|
||||
cachingKey += dependsOn[i] + this.tableObj.getRowCellValueByColumnName(row, dependsOn[i])
|
||||
} else
|
||||
cachingKey = dependsOn + this.tableObj.getRowCellValueByColumnName(row, dependsOn)
|
||||
}
|
||||
|
||||
return cachingKey
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.triggerGetOptions = function(callback) {
|
||||
var tableElement = this.tableObj.getElement()
|
||||
if (!tableElement) {
|
||||
return
|
||||
}
|
||||
|
||||
var optionsEvent = $.Event('autocompleteitems.oc.table'),
|
||||
values = {} // TODO - implement loading values from the current row.
|
||||
|
||||
$(tableElement).trigger(optionsEvent, [{
|
||||
values: values,
|
||||
callback: callback,
|
||||
column: this.columnName,
|
||||
columnConfiguration: this.columnConfiguration
|
||||
}])
|
||||
|
||||
if (optionsEvent.isDefaultPrevented()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.getInput = function() {
|
||||
if (!this.activeCell) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.activeCell.querySelector('.string-input')
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.buildAutoComplete = function(items) {
|
||||
if (!this.activeCell) {
|
||||
return
|
||||
}
|
||||
|
||||
var input = this.getInput()
|
||||
if (!input) {
|
||||
return
|
||||
}
|
||||
|
||||
if (items === undefined) {
|
||||
items = []
|
||||
}
|
||||
|
||||
$(input).autocomplete({
|
||||
source: this.prepareItems(items),
|
||||
matchWidth: true,
|
||||
menu: '<ul class="autocomplete dropdown-menu table-widget-autocomplete"></ul>',
|
||||
bodyContainer: true
|
||||
})
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.prepareItems = function(items) {
|
||||
var result = {}
|
||||
|
||||
if ($.isArray(items)) {
|
||||
for (var i = 0, len = items.length; i < len; i++) {
|
||||
result[items[i]] = items[i]
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = items
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
AutocompleteProcessor.prototype.removeAutocomplete = function() {
|
||||
var input = this.getInput()
|
||||
|
||||
$(input).autocomplete('destroy')
|
||||
}
|
||||
|
||||
$.wn.table.processor.autocomplete = AutocompleteProcessor;
|
||||
}(window.jQuery);
|
||||
210
modules/backend/widgets/table/assets/js/table.processor.base.js
Normal file
210
modules/backend/widgets/table/assets/js/table.processor.base.js
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Base class for the table cell processors.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// PROCESSOR NAMESPACES
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.processor === undefined)
|
||||
$.wn.table.processor = {}
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = function(tableObj, columnName, columnConfiguration) {
|
||||
//
|
||||
// State properties
|
||||
//
|
||||
|
||||
this.tableObj = tableObj
|
||||
|
||||
this.columnName = columnName
|
||||
|
||||
this.columnConfiguration = columnConfiguration
|
||||
|
||||
this.activeCell = null
|
||||
|
||||
this.validators = []
|
||||
|
||||
// Register event handlers
|
||||
this.registerHandlers()
|
||||
|
||||
// Initialize validators
|
||||
this.initValidators()
|
||||
}
|
||||
|
||||
Base.prototype.dispose = function() {
|
||||
// Register event handlers
|
||||
this.unregisterHandlers()
|
||||
|
||||
// Remove references to the DOM
|
||||
this.tableObj = null
|
||||
|
||||
this.activeCell = null
|
||||
}
|
||||
|
||||
/*
|
||||
* Renders the cell in the normal (no edit) mode
|
||||
*/
|
||||
Base.prototype.renderCell = function(value, cellContentContainer) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers event handlers required for the cell processor.
|
||||
* Event handers should be bound to the container control element
|
||||
* (not to the table element).
|
||||
*/
|
||||
Base.prototype.registerHandlers = function() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregisters event handlers previously registered with
|
||||
* registerHandlers().
|
||||
*/
|
||||
Base.prototype.unregisterHandlers = function() {
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when the cell managed by the processor
|
||||
* is focused (clicked or navigated with the keyboard).
|
||||
*/
|
||||
Base.prototype.onFocus = function(cellElement, isClick) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Forces the processor to hide the editor when the user navigates
|
||||
* away from the cell. Processors can update the sell value in this method.
|
||||
* Processors must clear the reference to the active cell in this method.
|
||||
*/
|
||||
Base.prototype.onUnfocus = function() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for the keydown event. The table class calls this method
|
||||
* for all processors.
|
||||
*/
|
||||
Base.prototype.onKeyDown = function(ev) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for the click event. The table class calls this method
|
||||
* for all processors.
|
||||
*/
|
||||
Base.prototype.onClick = function(ev) {
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
Base.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the keyboard navigation in the specified direction is allowed
|
||||
* by the cell processor. Some processors could reject the navigation, for example
|
||||
* the string processor could cancel the left array navigation if the caret
|
||||
* in the text input is not in the beginning of the text.
|
||||
*/
|
||||
Base.prototype.keyNavigationAllowed = function(ev, direction) {
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the processor's cell is focusable.
|
||||
*/
|
||||
Base.prototype.isCellFocusable = function() {
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the content container element of a cell
|
||||
*/
|
||||
Base.prototype.getCellContentContainer = function(cellElement) {
|
||||
return cellElement.querySelector('.content-container')
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a cell view data container (a DIV element that contains
|
||||
* the current cell value). This functionality is required for most
|
||||
* of the processors, perhaps except the checkbox cell processor.
|
||||
*/
|
||||
Base.prototype.createViewContainer = function(cellContentContainer, value) {
|
||||
var viewContainer = document.createElement('div')
|
||||
|
||||
viewContainer.setAttribute('data-view-container', 'data-view-container')
|
||||
viewContainer.textContent = value === undefined ? '' : value
|
||||
|
||||
cellContentContainer.appendChild(viewContainer)
|
||||
|
||||
return viewContainer
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the cell's view container element.
|
||||
*/
|
||||
Base.prototype.getViewContainer = function(cellElement) {
|
||||
return cellElement.querySelector('[data-view-container]')
|
||||
}
|
||||
|
||||
/*
|
||||
* Displays the view container
|
||||
*/
|
||||
Base.prototype.showViewContainer = function(cellElement) {
|
||||
return this.getViewContainer(cellElement).setAttribute('class', '')
|
||||
}
|
||||
|
||||
/*
|
||||
* Hides the view container
|
||||
*/
|
||||
Base.prototype.hideViewContainer = function(cellElement) {
|
||||
return this.getViewContainer(cellElement).setAttribute('class', 'hide')
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets visual value for the view container
|
||||
*/
|
||||
Base.prototype.setViewContainerValue = function(cellElement, value) {
|
||||
return this.getViewContainer(cellElement).textContent = value
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the specified element is some element created by the
|
||||
* processor.
|
||||
*/
|
||||
Base.prototype.elementBelongsToProcessor = function(element) {
|
||||
return false
|
||||
}
|
||||
|
||||
Base.prototype.initValidators = function() {
|
||||
if (this.columnConfiguration.validation === undefined)
|
||||
return
|
||||
|
||||
for (var validatorName in this.columnConfiguration.validation) {
|
||||
if ($.wn.table.validator === undefined || $.wn.table.validator[validatorName] == undefined)
|
||||
throw new Error('The table cell validator "'+validatorName+'" for the column "'+this.columnName+'" is not ' +
|
||||
'found in the $.wn.table.validator namespace.')
|
||||
|
||||
var validator = new $.wn.table.validator[validatorName](
|
||||
this.columnConfiguration.validation[validatorName]
|
||||
)
|
||||
|
||||
this.validators.push(validator)
|
||||
}
|
||||
}
|
||||
|
||||
Base.prototype.validate = function(value, rowData) {
|
||||
for (var i=0, len=this.validators.length; i<len; i++) {
|
||||
var message = this.validators[i].validate(value, rowData)
|
||||
|
||||
if (message !== undefined)
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
$.wn.table.processor.base = Base
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Checkbox cell processor for the table control.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.processor === undefined)
|
||||
throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.processor.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var CheckboxProcessor = function(tableObj, columnName, columnConfiguration) {
|
||||
//
|
||||
// Parent constructor
|
||||
//
|
||||
|
||||
Base.call(this, tableObj, columnName, columnConfiguration)
|
||||
}
|
||||
|
||||
CheckboxProcessor.prototype = Object.create(BaseProto)
|
||||
CheckboxProcessor.prototype.constructor = CheckboxProcessor
|
||||
|
||||
CheckboxProcessor.prototype.dispose = function() {
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the processor's cell is focusable.
|
||||
*/
|
||||
CheckboxProcessor.prototype.isCellFocusable = function() {
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Renders the cell in the normal (no edit) mode
|
||||
*/
|
||||
CheckboxProcessor.prototype.renderCell = function(value, cellContentContainer) {
|
||||
var checkbox = document.createElement('div')
|
||||
checkbox.setAttribute('data-checkbox-element', 'true')
|
||||
checkbox.setAttribute('tabindex', '0')
|
||||
|
||||
if (value && value != 0 && value != "false") {
|
||||
checkbox.setAttribute('class', 'checked')
|
||||
}
|
||||
|
||||
cellContentContainer.appendChild(checkbox)
|
||||
|
||||
if (this.columnConfiguration.readonly || this.columnConfiguration.readOnly) {
|
||||
cellContentContainer.classList.add('readonly');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when the cell managed by the processor
|
||||
* is focused (clicked or navigated with the keyboard).
|
||||
*/
|
||||
CheckboxProcessor.prototype.onFocus = function(cellElement, isClick) {
|
||||
cellElement.querySelector('div[data-checkbox-element]').focus()
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for the keydown event. The table class calls this method
|
||||
* for all processors.
|
||||
*/
|
||||
CheckboxProcessor.prototype.onKeyDown = function(ev) {
|
||||
if (ev.key === '(Space character)' || ev.key === 'Spacebar' || ev.key === ' ')
|
||||
this.onClick(ev)
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for the click event. The table class calls this method
|
||||
* for all processors.
|
||||
*/
|
||||
CheckboxProcessor.prototype.onClick = function(ev) {
|
||||
if (this.columnConfiguration.readonly || this.columnConfiguration.readOnly) {
|
||||
return
|
||||
}
|
||||
|
||||
var target = this.tableObj.getEventTarget(ev, 'DIV')
|
||||
|
||||
if (target.getAttribute('data-checkbox-element')) {
|
||||
// The method is called for all processors, but we should
|
||||
// update only the checkbox in the clicked column.
|
||||
var container = this.getCheckboxContainerNode(target)
|
||||
if (container.getAttribute('data-column') !== this.columnName) {
|
||||
return
|
||||
}
|
||||
|
||||
this.changeState(target)
|
||||
$(ev.target).trigger('change')
|
||||
}
|
||||
}
|
||||
|
||||
CheckboxProcessor.prototype.changeState = function(divElement) {
|
||||
var cell = divElement.parentNode.parentNode
|
||||
|
||||
if (divElement.getAttribute('class') == 'checked') {
|
||||
divElement.setAttribute('class', '')
|
||||
this.tableObj.setCellValue(cell, 0)
|
||||
}
|
||||
else {
|
||||
divElement.setAttribute('class', 'checked')
|
||||
this.tableObj.setCellValue(cell, 1)
|
||||
}
|
||||
}
|
||||
|
||||
CheckboxProcessor.prototype.getCheckboxContainerNode = function(checkbox) {
|
||||
return checkbox.parentNode.parentNode
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
CheckboxProcessor.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
if (columnName != this.columnName) {
|
||||
return
|
||||
}
|
||||
|
||||
var checkbox = cellElement.querySelector('div[data-checkbox-element]'),
|
||||
value = this.tableObj.getCellValue(cellElement)
|
||||
|
||||
if (value && value != 0 && value != "false") {
|
||||
checkbox.setAttribute('class', 'checked')
|
||||
}
|
||||
else {
|
||||
checkbox.setAttribute('class', '')
|
||||
}
|
||||
}
|
||||
|
||||
$.wn.table.processor.checkbox = CheckboxProcessor;
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Drop-down cell processor for the table control.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: implement the search
|
||||
*/
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.processor === undefined)
|
||||
throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.processor.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var DropdownProcessor = function(tableObj, columnName, columnConfiguration) {
|
||||
//
|
||||
// State properties
|
||||
//
|
||||
|
||||
this.itemListElement = null
|
||||
|
||||
this.cachedOptionPromises = {}
|
||||
this.searching = false
|
||||
this.searchQuery = null
|
||||
this.searchInterval = null
|
||||
|
||||
// Event handlers
|
||||
this.itemClickHandler = this.onItemClick.bind(this)
|
||||
this.itemKeyDownHandler = this.onItemKeyDown.bind(this)
|
||||
this.itemMouseMoveHandler = this.onItemMouseMove.bind(this)
|
||||
|
||||
//
|
||||
// Parent constructor
|
||||
//
|
||||
|
||||
Base.call(this, tableObj, columnName, columnConfiguration)
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype = Object.create(BaseProto)
|
||||
DropdownProcessor.prototype.constructor = DropdownProcessor
|
||||
|
||||
DropdownProcessor.prototype.dispose = function() {
|
||||
this.unregisterListHandlers()
|
||||
this.itemClickHandler = null
|
||||
this.itemKeyDownHandler = null
|
||||
this.itemMouseMoveHandler = null
|
||||
this.itemListElement = null
|
||||
this.cachedOptionPromises = null
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.unregisterListHandlers = function() {
|
||||
if (this.itemListElement)
|
||||
{
|
||||
// This processor binds custom click handler to the item list,
|
||||
// the standard registerHandlers/unregisterHandlers functionality
|
||||
// can't be used here because the element belongs to the document
|
||||
// body, not to the table.
|
||||
this.itemListElement.removeEventListener('click', this.itemClickHandler)
|
||||
this.itemListElement.removeEventListener('keydown', this.itemKeyDownHandler)
|
||||
this.itemListElement.removeEventListener('mousemove', this.itemMouseMoveHandler)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Renders the cell in the normal (no edit) mode
|
||||
*/
|
||||
DropdownProcessor.prototype.renderCell = function(value, cellContentContainer) {
|
||||
let viewContainer;
|
||||
|
||||
if (this.columnConfiguration.readonly || this.columnConfiguration.readOnly) {
|
||||
viewContainer = this.createViewContainer(cellContentContainer, value)
|
||||
|
||||
cellContentContainer.classList.add('readonly');
|
||||
cellContentContainer.setAttribute('tabindex', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
viewContainer = this.createViewContainer(cellContentContainer, '...')
|
||||
|
||||
this.fetchOptions(cellContentContainer.parentNode, function renderCellFetchOptions(options) {
|
||||
if (options[value] !== undefined)
|
||||
viewContainer.textContent = options[value]
|
||||
|
||||
cellContentContainer.setAttribute('tabindex', 0)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when the cell managed by the processor
|
||||
* is focused (clicked or navigated with the keyboard).
|
||||
*/
|
||||
DropdownProcessor.prototype.onFocus = function(cellElement, isClick) {
|
||||
if (this.activeCell === cellElement) {
|
||||
this.showDropdown()
|
||||
return
|
||||
}
|
||||
|
||||
this.activeCell = cellElement
|
||||
if (!this.columnConfiguration.readonly && !this.columnConfiguration.readOnly) {
|
||||
var cellContentContainer = this.getCellContentContainer(cellElement)
|
||||
this.buildEditor(cellElement, cellContentContainer, isClick)
|
||||
|
||||
if (!isClick) {
|
||||
cellContentContainer.focus()
|
||||
}
|
||||
} else {
|
||||
this.getCellContentContainer(cellElement).focus()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Forces the processor to hide the editor when the user navigates
|
||||
* away from the cell. Processors can update the sell value in this method.
|
||||
* Processors must clear the reference to the active cell in this method.
|
||||
*/
|
||||
DropdownProcessor.prototype.onUnfocus = function() {
|
||||
if (!this.activeCell)
|
||||
return
|
||||
|
||||
this.unregisterListHandlers()
|
||||
|
||||
this.hideDropdown()
|
||||
this.itemListElement = null
|
||||
this.activeCell = null
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.buildEditor = function(cellElement, cellContentContainer, isClick) {
|
||||
// Create the select control
|
||||
var currentValue = this.tableObj.getCellValue(cellElement),
|
||||
containerPosition = this.getAbsolutePosition(cellContentContainer),
|
||||
self = this
|
||||
|
||||
this.itemListElement = document.createElement('div')
|
||||
|
||||
this.itemListElement.addEventListener('click', this.itemClickHandler)
|
||||
this.itemListElement.addEventListener('keydown', this.itemKeyDownHandler)
|
||||
this.itemListElement.addEventListener('mousemove', this.itemMouseMoveHandler)
|
||||
|
||||
this.itemListElement.setAttribute('class', 'table-control-dropdown-list')
|
||||
this.itemListElement.style.width = cellContentContainer.offsetWidth + 'px'
|
||||
this.itemListElement.style.left = containerPosition.left + 'px'
|
||||
this.itemListElement.style.top = containerPosition.top - 2 + cellContentContainer.offsetHeight + 'px'
|
||||
|
||||
this.fetchOptions(cellElement, function renderCellFetchOptions(options) {
|
||||
var listElement = document.createElement('ul')
|
||||
|
||||
for (var value in options) {
|
||||
var itemElement = document.createElement('li')
|
||||
itemElement.setAttribute('data-value', value)
|
||||
itemElement.textContent = options[value]
|
||||
itemElement.setAttribute('tabindex', 0)
|
||||
|
||||
if (value == currentValue)
|
||||
itemElement.setAttribute('class', 'selected')
|
||||
|
||||
listElement.appendChild(itemElement)
|
||||
}
|
||||
|
||||
self.itemListElement.appendChild(listElement)
|
||||
|
||||
if (isClick)
|
||||
self.showDropdown()
|
||||
|
||||
self = null
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide the drop-down, but don't delete it.
|
||||
*/
|
||||
DropdownProcessor.prototype.hideDropdown = function() {
|
||||
if (this.itemListElement && this.activeCell && this.itemListElement.parentNode) {
|
||||
var cellContentContainer = this.getCellContentContainer(this.activeCell)
|
||||
cellContentContainer.setAttribute('data-dropdown-open', 'false')
|
||||
|
||||
this.itemListElement.parentNode.removeChild(this.itemListElement)
|
||||
|
||||
cellContentContainer.focus()
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.showDropdown = function() {
|
||||
if (this.itemListElement && this.itemListElement.parentNode !== document.body) {
|
||||
this.getCellContentContainer(this.activeCell).setAttribute('data-dropdown-open', 'true')
|
||||
document.body.appendChild(this.itemListElement)
|
||||
|
||||
var activeItemElement = this.itemListElement.querySelector('ul li.selected')
|
||||
|
||||
if (!activeItemElement) {
|
||||
activeItemElement = this.itemListElement.querySelector('ul li:first-child')
|
||||
|
||||
if (activeItemElement)
|
||||
activeItemElement.setAttribute('class', 'selected')
|
||||
}
|
||||
|
||||
if (activeItemElement) {
|
||||
window.setTimeout(function(){
|
||||
activeItemElement.focus()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.fetchOptions = function(cellElement, onSuccess) {
|
||||
if (this.columnConfiguration.options) {
|
||||
onSuccess(this.columnConfiguration.options)
|
||||
}
|
||||
else {
|
||||
// If options are not provided and not found in the cache,
|
||||
// request them from the server. For dependent drop-downs
|
||||
// the caching key contains the master column values.
|
||||
|
||||
var row = cellElement.parentNode,
|
||||
cachingKey = this.createOptionsCachingKey(row),
|
||||
viewContainer = this.getViewContainer(cellElement)
|
||||
|
||||
// Request options from the server. When the table widget builds,
|
||||
// multiple cells in the column could require loading the options.
|
||||
// The AJAX promises are cached here so that we have a single
|
||||
// request per caching key.
|
||||
|
||||
viewContainer.setAttribute('class', 'loading')
|
||||
|
||||
if (!this.cachedOptionPromises[cachingKey]) {
|
||||
var requestData = {
|
||||
column: this.columnName,
|
||||
rowData: this.tableObj.getRowData(row)
|
||||
},
|
||||
handlerName = this.tableObj.getAlias()+'::onGetDropdownOptions'
|
||||
|
||||
this.cachedOptionPromises[cachingKey] = this.tableObj.$el.request(handlerName, {data: requestData})
|
||||
}
|
||||
|
||||
this.cachedOptionPromises[cachingKey].done(function onDropDownLoadOptionsSuccess(data){
|
||||
onSuccess(data.options)
|
||||
}).always(function onDropDownLoadOptionsAlways(){
|
||||
viewContainer.setAttribute('class', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.createOptionsCachingKey = function(row) {
|
||||
var cachingKey = 'non-dependent',
|
||||
dependsOn = this.columnConfiguration.dependsOn
|
||||
|
||||
if (dependsOn) {
|
||||
if (typeof dependsOn == 'object') {
|
||||
for (var i = 0, len = dependsOn.length; i < len; i++ )
|
||||
cachingKey += dependsOn[i] + this.tableObj.getRowCellValueByColumnName(row, dependsOn[i])
|
||||
} else
|
||||
cachingKey = dependsOn + this.tableObj.getRowCellValueByColumnName(row, dependsOn)
|
||||
}
|
||||
|
||||
return cachingKey
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.getAbsolutePosition = function(element) {
|
||||
// TODO: use the foundation library
|
||||
|
||||
var top = document.body.scrollTop,
|
||||
left = 0
|
||||
|
||||
do {
|
||||
top += element.offsetTop || 0;
|
||||
top -= element.scrollTop || 0;
|
||||
left += element.offsetLeft || 0;
|
||||
element = element.offsetParent;
|
||||
} while(element)
|
||||
|
||||
return {
|
||||
top: top,
|
||||
left: left
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.updateCellFromFocusedItem = function(focusedItem) {
|
||||
if (!focusedItem) {
|
||||
focusedItem = this.findFocusedItem();
|
||||
}
|
||||
this.setSelectedItem(focusedItem);
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.findSelectedItem = function() {
|
||||
if (this.itemListElement)
|
||||
return this.itemListElement.querySelector('ul li.selected')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.setSelectedItem = function(item) {
|
||||
if (!this.itemListElement)
|
||||
return null;
|
||||
|
||||
if (item.tagName == 'LI' && this.itemListElement.contains(item)) {
|
||||
this.itemListElement.querySelectorAll('ul li').forEach(function (option) {
|
||||
option.removeAttribute('class');
|
||||
});
|
||||
item.setAttribute('class', 'selected');
|
||||
}
|
||||
|
||||
this.tableObj.setCellValue(this.activeCell, item.getAttribute('data-value'))
|
||||
this.setViewContainerValue(this.activeCell, item.textContent)
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.findFocusedItem = function() {
|
||||
if (this.itemListElement)
|
||||
return this.itemListElement.querySelector('ul li:focus')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.onItemClick = function(ev) {
|
||||
var target = this.tableObj.getEventTarget(ev)
|
||||
|
||||
if (target.tagName == 'LI') {
|
||||
target.focus();
|
||||
this.updateCellFromFocusedItem(target)
|
||||
this.hideDropdown()
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.onItemKeyDown = function(ev) {
|
||||
if (!this.itemListElement)
|
||||
return
|
||||
|
||||
if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp')
|
||||
{
|
||||
// Up or down keys - find previous/next list item and select it
|
||||
var focused = this.findFocusedItem(),
|
||||
newFocusedItem = focused.nextElementSibling
|
||||
|
||||
if (ev.key === 'ArrowUp')
|
||||
newFocusedItem = focused.previousElementSibling
|
||||
|
||||
if (newFocusedItem) {
|
||||
newFocusedItem.focus()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.key === 'Enter' || ev.key === '(Space character)' || ev.key === 'Spacebar' || ev.key === ' ') {
|
||||
// Return or space keys - update the selected value and hide the editor
|
||||
this.updateCellFromFocusedItem()
|
||||
this.hideDropdown()
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.key === 'Tab') {
|
||||
// Tab - update the selected value and pass control to the table navigation
|
||||
this.updateCellFromFocusedItem()
|
||||
this.tableObj.navigation.navigateNext(ev)
|
||||
this.tableObj.stopEvent(ev)
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.key === 'Escape') {
|
||||
// Esc - hide the drop-down
|
||||
this.hideDropdown()
|
||||
return
|
||||
}
|
||||
|
||||
this.searchByTextInput(ev, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for mouse movements over options in the dropdown menu
|
||||
*/
|
||||
DropdownProcessor.prototype.onItemMouseMove = function(ev) {
|
||||
if (!this.itemListElement)
|
||||
return
|
||||
|
||||
var target = this.tableObj.getEventTarget(ev)
|
||||
|
||||
if (target.tagName == 'LI') {
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler for the keydown event. The table class calls this method
|
||||
* for all processors.
|
||||
*/
|
||||
DropdownProcessor.prototype.onKeyDown = function(ev) {
|
||||
if (!this.itemListElement)
|
||||
return
|
||||
|
||||
if ((ev.key === '(Space character)' || ev.key === 'Spacebar' || ev.key === ' ') && !this.searching) { // Spacebar
|
||||
this.showDropdown()
|
||||
} else if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') { // Up and down arrow keys
|
||||
var selected = this.findSelectedItem(),
|
||||
newSelectedItem;
|
||||
|
||||
if (!selected) {
|
||||
if (ev.key === 'ArrowUp') {
|
||||
// Only show an initial item when the down array key is pressed
|
||||
return false
|
||||
}
|
||||
newSelectedItem = this.itemListElement.querySelector('ul li:first-child')
|
||||
} else {
|
||||
newSelectedItem = selected.nextElementSibling
|
||||
|
||||
if (ev.key === 'ArrowUp')
|
||||
newSelectedItem = selected.previousElementSibling
|
||||
}
|
||||
|
||||
if (newSelectedItem) {
|
||||
this.setSelectedItem(newSelectedItem);
|
||||
}
|
||||
|
||||
return false // Stop propogation of event
|
||||
} else {
|
||||
this.searchByTextInput(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
DropdownProcessor.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
// Determine if this drop-down depends on the changed column
|
||||
// and update the option list if necessary
|
||||
|
||||
// TODO: setting drop-down values with table.setRowValues() is not implemented currently
|
||||
|
||||
if (!this.columnConfiguration.dependsOn)
|
||||
return
|
||||
|
||||
var dependsOnColumn = false,
|
||||
dependsOn = this.columnConfiguration.dependsOn
|
||||
|
||||
if (typeof dependsOn == 'object') {
|
||||
for (var i = 0, len = dependsOn.length; i < len; i++ ) {
|
||||
if (dependsOn[i] == columnName) {
|
||||
dependsOnColumn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dependsOnColumn = dependsOn == columnName
|
||||
}
|
||||
|
||||
if (!dependsOnColumn)
|
||||
return
|
||||
|
||||
var currentValue = this.tableObj.getCellValue(cellElement),
|
||||
viewContainer = this.getViewContainer(cellElement)
|
||||
|
||||
this.fetchOptions(cellElement, function rowValueChangedFetchOptions(options) {
|
||||
var value = options[currentValue] !== undefined
|
||||
? options[currentValue]
|
||||
: '...'
|
||||
|
||||
viewContainer.textContent = value
|
||||
viewContainer = null
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the specified element is some element created by the
|
||||
* processor.
|
||||
*/
|
||||
DropdownProcessor.prototype.elementBelongsToProcessor = function(element) {
|
||||
if (!this.itemListElement)
|
||||
return false
|
||||
|
||||
return this.tableObj.parentContainsElement(this.itemListElement, element)
|
||||
}
|
||||
|
||||
/*
|
||||
* Provides auto-complete like functionality for typing in a query and selecting
|
||||
* a matching list option
|
||||
*/
|
||||
DropdownProcessor.prototype.searchByTextInput = function(ev, focusOnly) {
|
||||
if (focusOnly === undefined) {
|
||||
focusOnly = false;
|
||||
}
|
||||
var character = ev.key;
|
||||
|
||||
if (character.length === 1 || character === 'Space') {
|
||||
if (!this.searching) {
|
||||
this.searching = true;
|
||||
this.searchQuery = '';
|
||||
}
|
||||
|
||||
this.searchQuery += (character === 'Space') ? ' ' : character;
|
||||
|
||||
// Search for a valid option in dropdown
|
||||
var validItem = null;
|
||||
var query = this.searchQuery;
|
||||
|
||||
this.itemListElement.querySelectorAll('ul li').forEach(function(item) {
|
||||
if (validItem === null && item.dataset.value && item.dataset.value.toLowerCase().indexOf(query.toLowerCase()) === 0) {
|
||||
validItem = item;
|
||||
}
|
||||
});
|
||||
|
||||
if (validItem) {
|
||||
// If a valid item is found, select item and allow for fine-tuning the search query
|
||||
if (focusOnly === true) {
|
||||
validItem.focus();
|
||||
} else {
|
||||
this.setSelectedItem(validItem);
|
||||
}
|
||||
|
||||
if (this.searchInterval) {
|
||||
clearTimeout(this.searchInterval);
|
||||
}
|
||||
|
||||
this.searchInterval = setTimeout(this.cancelTextSearch.bind(this), 1000);
|
||||
} else {
|
||||
this.cancelTextSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropdownProcessor.prototype.cancelTextSearch = function() {
|
||||
this.searching = false;
|
||||
this.searchQuery = null;
|
||||
this.searchInterval = null;
|
||||
}
|
||||
|
||||
|
||||
$.wn.table.processor.dropdown = DropdownProcessor;
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* String cell processor for the table control.
|
||||
* The string processor allows to edit cell values with a simple
|
||||
* input control.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.processor === undefined)
|
||||
throw new Error("The $.wn.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.processor.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var StringProcessor = function(tableObj, columnName, columnConfiguration) {
|
||||
//
|
||||
// Parent constructor
|
||||
//
|
||||
|
||||
Base.call(this, tableObj, columnName, columnConfiguration)
|
||||
}
|
||||
|
||||
StringProcessor.prototype = Object.create(BaseProto)
|
||||
StringProcessor.prototype.constructor = StringProcessor
|
||||
|
||||
StringProcessor.prototype.dispose = function() {
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* Renders the cell in the normal (no edit) mode
|
||||
*/
|
||||
StringProcessor.prototype.renderCell = function(value, cellContentContainer) {
|
||||
this.createViewContainer(cellContentContainer, value);
|
||||
|
||||
if (this.columnConfiguration.readonly || this.columnConfiguration.readOnly) {
|
||||
cellContentContainer.classList.add('readonly');
|
||||
cellContentContainer.setAttribute('tabindex', 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when the cell managed by the processor
|
||||
* is focused (clicked or navigated with the keyboard).
|
||||
*/
|
||||
StringProcessor.prototype.onFocus = function(cellElement, isClick) {
|
||||
if (this.activeCell === cellElement)
|
||||
return
|
||||
|
||||
this.activeCell = cellElement
|
||||
if (!this.columnConfiguration.readonly && !this.columnConfiguration.readOnly) {
|
||||
this.buildEditor(cellElement, this.getCellContentContainer(cellElement))
|
||||
} else {
|
||||
this.getCellContentContainer(cellElement).focus()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Forces the processor to hide the editor when the user navigates
|
||||
* away from the cell. Processors can update the sell value in this method.
|
||||
* Processors must clear the reference to the active cell in this method.
|
||||
*/
|
||||
StringProcessor.prototype.onUnfocus = function() {
|
||||
if (!this.activeCell)
|
||||
return
|
||||
|
||||
var editor = this.activeCell.querySelector('.string-input')
|
||||
if (editor) {
|
||||
// Update the cell value and remove the editor
|
||||
this.tableObj.setCellValue(this.activeCell, editor.value)
|
||||
this.setViewContainerValue(this.activeCell, editor.value)
|
||||
editor.parentNode.removeChild(editor)
|
||||
}
|
||||
|
||||
this.showViewContainer(this.activeCell)
|
||||
this.activeCell = null
|
||||
}
|
||||
|
||||
StringProcessor.prototype.buildEditor = function(cellElement, cellContentContainer) {
|
||||
// Hide the view container
|
||||
this.hideViewContainer(this.activeCell)
|
||||
|
||||
// Create the input control
|
||||
var input = document.createElement('input')
|
||||
input.setAttribute('type', 'text')
|
||||
input.setAttribute('class', 'string-input')
|
||||
input.value = this.tableObj.getCellValue(cellElement)
|
||||
|
||||
cellContentContainer.appendChild(input)
|
||||
|
||||
input.focus();
|
||||
this.setCaretPosition(input, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the keyboard navigation in the specified direction is allowed
|
||||
* by the cell processor. Some processors could reject the navigation, for example
|
||||
* the string processor could cancel the left array navigation if the caret
|
||||
* in the text input is not in the beginning of the text.
|
||||
*/
|
||||
StringProcessor.prototype.keyNavigationAllowed = function(ev, direction) {
|
||||
if (direction != 'left' && direction != 'right')
|
||||
return true
|
||||
|
||||
if (!this.activeCell)
|
||||
return true
|
||||
|
||||
var editor = this.activeCell.querySelector('.string-input')
|
||||
if (!editor)
|
||||
return true
|
||||
|
||||
var caretPosition = this.getCaretPosition(editor)
|
||||
|
||||
if (direction == 'left')
|
||||
return caretPosition == 0
|
||||
|
||||
if (direction == 'right')
|
||||
return caretPosition == editor.value.length
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
StringProcessor.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
if (columnName != this.columnName) {
|
||||
return
|
||||
}
|
||||
|
||||
var value = this.tableObj.getCellValue(cellElement)
|
||||
|
||||
this.setViewContainerValue(cellElement, value)
|
||||
}
|
||||
|
||||
StringProcessor.prototype.getCaretPosition = function(input) {
|
||||
// TODO: use the foundation library
|
||||
|
||||
if (document.selection) {
|
||||
var selection = document.selection.createRange()
|
||||
|
||||
selection.moveStart('character', -input.value.length)
|
||||
return selection.text.length
|
||||
}
|
||||
|
||||
if (input.selectionStart !== undefined)
|
||||
return input.selectionStart
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
StringProcessor.prototype.setCaretPosition = function(input, position) {
|
||||
// TODO: use the foundation library
|
||||
|
||||
if (document.selection) {
|
||||
var range = input.createTextRange()
|
||||
|
||||
setTimeout(function() {
|
||||
// Asynchronous layout update, better performance
|
||||
range.collapse(true)
|
||||
range.moveStart("character", position)
|
||||
range.moveEnd("character", 0)
|
||||
range.select()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
if (input.selectionStart !== undefined) {
|
||||
setTimeout(function() {
|
||||
// Asynchronous layout update
|
||||
input.selectionStart = position
|
||||
input.selectionEnd = position
|
||||
}, 0)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
$.wn.table.processor.string = StringProcessor;
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Base class for the table validators.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// VALIDATOR NAMESPACES
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
$.wn.table.validator = {}
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = function(options) {
|
||||
//
|
||||
// State properties
|
||||
//
|
||||
|
||||
this.options = options
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Base.prototype.validate = function(value, rowData) {
|
||||
if (this.options.requiredWith !== undefined && !this.rowHasValue(this.options.requiredWith, rowData))
|
||||
return
|
||||
|
||||
return this.validateValue(value, rowData)
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined. This method should be redefined
|
||||
* in descendant classes.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Base.prototype.validateValue = function(value, rowData) {
|
||||
|
||||
}
|
||||
|
||||
Base.prototype.trim = function(value) {
|
||||
if (String.prototype.trim)
|
||||
return value.trim()
|
||||
|
||||
return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
|
||||
}
|
||||
|
||||
Base.prototype.getMessage = function(defaultValue) {
|
||||
if (this.options.message !== undefined)
|
||||
return this.options.message
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
Base.prototype.rowHasValue = function(columnName, rowData) {
|
||||
if (rowData[columnName] === undefined)
|
||||
return false
|
||||
|
||||
if (typeof rowData[columnName] == 'boolean')
|
||||
return rowData[columnName]
|
||||
|
||||
var value = this.trim(String(rowData[columnName]))
|
||||
|
||||
return value.length > 0
|
||||
}
|
||||
|
||||
$.wn.table.validator.base = Base;
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Base class for number validators.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.validator.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var BaseNumber = function(options) {
|
||||
Base.call(this, options)
|
||||
};
|
||||
|
||||
BaseNumber.prototype = Object.create(BaseProto)
|
||||
BaseNumber.prototype.constructor = BaseNumber
|
||||
|
||||
BaseNumber.prototype.doCommonChecks = function(value) {
|
||||
if (this.options.min !== undefined || this.options.max !== undefined) {
|
||||
if (this.options.min !== undefined) {
|
||||
if (this.options.min.value === undefined)
|
||||
throw new Error('The min.value parameter is not defined in the table validator configuration')
|
||||
|
||||
if (value < this.options.min.value) {
|
||||
return this.options.min.message !== undefined ?
|
||||
this.options.min.message :
|
||||
"The value should not be less than " + this.options.min.value
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.max !== undefined) {
|
||||
if (this.options.max.value === undefined)
|
||||
throw new Error('The max.value parameter is not defined in the table validator configuration')
|
||||
|
||||
if (value > this.options.max.value) {
|
||||
return this.options.max.message !== undefined ?
|
||||
this.options.max.message :
|
||||
"The value should not be more than " + this.options.max.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
$.wn.table.validator.baseNumber = BaseNumber
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Float table validator.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator.baseNumber === undefined)
|
||||
throw new Error("The $.wn.table.validator.baseNumber namespace is not defined. Make sure that the table.validator.baseNumber.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.validator.baseNumber,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Float = function(options) {
|
||||
Base.call(this, options)
|
||||
};
|
||||
|
||||
Float.prototype = Object.create(BaseProto)
|
||||
Float.prototype.constructor = Float
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Float.prototype.validateValue = function(value, rowData) {
|
||||
value = this.trim(value)
|
||||
|
||||
if (value.length == 0)
|
||||
return
|
||||
|
||||
var testResult = this.options.allowNegative ?
|
||||
/^[-]?([0-9]+\.[0-9]+|[0-9]+)$/.test(value) :
|
||||
/^([0-9]+\.[0-9]+|[0-9]+)$/.test(value)
|
||||
|
||||
if (!testResult) {
|
||||
var defaultMessage = this.options.allowNegative ?
|
||||
'The value should be a floating point number.' :
|
||||
'The value should be a positive floating point number';
|
||||
|
||||
return this.getMessage(defaultMessage)
|
||||
}
|
||||
|
||||
return this.doCommonChecks(parseFloat(value))
|
||||
}
|
||||
|
||||
$.wn.table.validator.float = Float
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Integer table validator.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator.baseNumber === undefined)
|
||||
throw new Error("The $.wn.table.validator.baseNumber namespace is not defined. Make sure that the table.validator.baseNumber.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.validator.baseNumber,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Integer = function(options) {
|
||||
Base.call(this, options)
|
||||
};
|
||||
|
||||
Integer.prototype = Object.create(BaseProto)
|
||||
Integer.prototype.constructor = Integer
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Integer.prototype.validateValue = function(value, rowData) {
|
||||
value = this.trim(value)
|
||||
|
||||
if (value.length == 0)
|
||||
return
|
||||
|
||||
var testResult = this.options.allowNegative ?
|
||||
/^\-?[0-9]*$/.test(value) :
|
||||
/^[0-9]*$/.test(value)
|
||||
|
||||
if (!testResult) {
|
||||
var defaultMessage = this.options.allowNegative ?
|
||||
'The value should be an integer.' :
|
||||
'The value should be a positive integer';
|
||||
|
||||
return this.getMessage(defaultMessage)
|
||||
}
|
||||
|
||||
return this.doCommonChecks(parseInt(value))
|
||||
}
|
||||
|
||||
$.wn.table.validator.integer = Integer
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* String length table validator.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.validator.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Length = function(options) {
|
||||
Base.call(this, options)
|
||||
};
|
||||
|
||||
Length.prototype = Object.create(BaseProto)
|
||||
Length.prototype.constructor = Length
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Length.prototype.validateValue = function(value, rowData) {
|
||||
value = this.trim(value)
|
||||
|
||||
if (value.length == 0)
|
||||
return
|
||||
|
||||
if (this.options.min !== undefined || this.options.max !== undefined) {
|
||||
if (this.options.min !== undefined) {
|
||||
if (this.options.min.value === undefined)
|
||||
throw new Error('The min.value parameter is not defined in the Length table validator configuration')
|
||||
|
||||
if (value.length < this.options.min.value) {
|
||||
return this.options.min.message !== undefined ?
|
||||
this.options.min.message :
|
||||
"The string should not be shorter than " + this.options.min.value
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.max !== undefined) {
|
||||
if (this.options.max.value === undefined)
|
||||
throw new Error('The max.value parameter is not defined in the Length table validator configuration')
|
||||
|
||||
if (value.length > this.options.max.value) {
|
||||
return this.options.max.message !== undefined ?
|
||||
this.options.max.message :
|
||||
"The string should not be longer than " + this.options.max.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
$.wn.table.validator.length = Length
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Regex length table validator.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.validator.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Regex = function(options) {
|
||||
Base.call(this, options)
|
||||
};
|
||||
|
||||
Regex.prototype = Object.create(BaseProto)
|
||||
Regex.prototype.constructor = Regex
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Regex.prototype.validateValue = function(value, rowData) {
|
||||
value = this.trim(value)
|
||||
|
||||
if (value.length == 0)
|
||||
return
|
||||
|
||||
if (this.options.pattern === undefined)
|
||||
throw new Error('The pattern parameter is not defined in the Regex table validator configuration')
|
||||
|
||||
var regexObj = new RegExp(this.options.pattern, this.options.modifiers)
|
||||
|
||||
if (!regexObj.test(value))
|
||||
return this.getMessage("Invalid value format.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
$.wn.table.validator.regex = Regex
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Required table validator.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
// NAMESPACE CHECK
|
||||
// ============================
|
||||
|
||||
if ($.wn.table === undefined)
|
||||
throw new Error("The $.wn.table namespace is not defined. Make sure that the table.js script is loaded.");
|
||||
|
||||
if ($.wn.table.validator === undefined)
|
||||
throw new Error("The $.wn.table.validator namespace is not defined. Make sure that the table.validator.base.js script is loaded.");
|
||||
|
||||
// CLASS DEFINITION
|
||||
// ============================
|
||||
|
||||
var Base = $.wn.table.validator.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var Required = function(options) {
|
||||
Base.call(this, options)
|
||||
};
|
||||
|
||||
Required.prototype = Object.create(BaseProto)
|
||||
Required.prototype.constructor = Required
|
||||
|
||||
/*
|
||||
* Validates a value and returns the error message. If there
|
||||
* are no errors, returns undefined.
|
||||
* The rowData parameter is an object containing all values in the
|
||||
* target row.
|
||||
*/
|
||||
Required.prototype.validateValue = function(value, rowData) {
|
||||
value = this.trim(value)
|
||||
|
||||
if (value.length === 0)
|
||||
return this.getMessage("The value should not be empty.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
$.wn.table.validator.required = Required
|
||||
}(window.jQuery);
|
||||
509
modules/backend/widgets/table/assets/less/table.less
Normal file
509
modules/backend/widgets/table/assets/less/table.less
Normal file
@@ -0,0 +1,509 @@
|
||||
@import "../../../../../backend/assets/less/core/boot.less";
|
||||
|
||||
/*
|
||||
* General control styling
|
||||
*/
|
||||
|
||||
@table-active-border: #e0e0e0;
|
||||
@table-inactive-border: #e0e0e0;
|
||||
@table-odd-row: #ffffff;
|
||||
@table-even-row: #fafafa;
|
||||
@table-disabled-field: #f7f7f7;
|
||||
|
||||
.control-table {
|
||||
.table-container {
|
||||
border: 1px solid @table-inactive-border;
|
||||
.border-radius(4px);
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not([data-records-per-page="false"]) .table-container {
|
||||
.border-bottom-radius(0);
|
||||
}
|
||||
|
||||
&.active .table-container {
|
||||
border-color: @table-active-border;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
|
||||
td, th {
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
[data-view-container] {
|
||||
padding: 5px 10px;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
table.headers {
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 1px;
|
||||
right: 1px;
|
||||
margin-top: -1px;
|
||||
border-bottom: 1px solid @table-inactive-border;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 7px 10px;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
font-size: @font-size-base - 3;
|
||||
color: #333333;
|
||||
background: white;
|
||||
border-right: 1px solid #ecf0f1;
|
||||
[data-view-container] {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active table.headers:after {
|
||||
border-bottom-color: @table-active-border;
|
||||
}
|
||||
|
||||
table.data {
|
||||
td {
|
||||
border: 1px solid #ecf0f1;
|
||||
|
||||
.content-container {
|
||||
position: relative;
|
||||
padding: 1px;
|
||||
outline: none;
|
||||
|
||||
&.readonly {
|
||||
background: @table-disabled-field;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: @color-focus!important;
|
||||
|
||||
.content-container {
|
||||
padding: 0;
|
||||
border: 1px solid @color-focus;
|
||||
|
||||
&:before, &:after {
|
||||
content: ' ';
|
||||
background: @color-focus;
|
||||
position: absolute;
|
||||
left: -2px;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
width: 1px;
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
right: -2px;
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color: @table-odd-row;
|
||||
|
||||
&.error {
|
||||
background-color: #fbecec!important;
|
||||
|
||||
td.active.error {
|
||||
border-color: #ec0000!important;
|
||||
|
||||
.content-container {
|
||||
border-color: #ec0000!important;
|
||||
&:before, &:after {
|
||||
background-color: #ec0000!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr:nth-child(2n) {
|
||||
background-color: @table-even-row;
|
||||
}
|
||||
}
|
||||
|
||||
table.data {
|
||||
tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.control-scrollbar {
|
||||
> div {
|
||||
.border-bottom-radius(4px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.data {
|
||||
tr:last-child td {
|
||||
border-bottom: 1px solid #ecf0f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
background: white;
|
||||
border-bottom: 1px solid @table-inactive-border;
|
||||
.clearfix();
|
||||
|
||||
a.btn {
|
||||
color: #323e50;
|
||||
padding: 8px 10px;
|
||||
.opacity(0.5);
|
||||
.box-shadow(none) !important;
|
||||
text-shadow: none;
|
||||
|
||||
&:hover {
|
||||
.opacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
.table-search {
|
||||
float: right;
|
||||
margin: 3px 3px 3px 0;
|
||||
|
||||
.table-search-input {
|
||||
height: auto;
|
||||
padding: 5px 13px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
a.table-icon {
|
||||
&:before {
|
||||
display: inline-block;
|
||||
content: ' ';
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
background: transparent url(../images/table-icons.gif) no-repeat 0 0;
|
||||
background-size: 32px auto;
|
||||
}
|
||||
|
||||
&.add-table-row-above:before {
|
||||
background-position: 0 -56px;
|
||||
}
|
||||
|
||||
&.delete-table-row:before {
|
||||
background-position: 0 -113px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active .toolbar {
|
||||
border-bottom-color: @table-active-border;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin: -15px 0 15px;
|
||||
padding: 7px 10px;
|
||||
background: white;
|
||||
border: 1px solid @table-inactive-border;
|
||||
border-top: none;
|
||||
.border-bottom-radius(4px);
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
line-height: 100%;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
color: #95a5a6;
|
||||
padding: 4px 6px;
|
||||
background: #ecf0f1;
|
||||
.border-radius(2px);
|
||||
outline: none;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
a:focus-visible {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
&.active a {
|
||||
background: @brand-accent;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-devicepixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
|
||||
.control-table .toolbar {
|
||||
a {
|
||||
&:before {
|
||||
background-position: 0px -9px;
|
||||
background-size: 16px auto;
|
||||
}
|
||||
|
||||
&.add-table-row-above:before {
|
||||
background-position: 0 -39px;
|
||||
}
|
||||
|
||||
&.delete-table-row:before {
|
||||
background-position: 0 -66px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* String and autocomplete editors
|
||||
*/
|
||||
|
||||
.control-table {
|
||||
td[data-column-type=string],
|
||||
td[data-column-type=autocomplete] {
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 6px 10px 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html.chrome {
|
||||
.control-table {
|
||||
td[data-column-type=string],
|
||||
td[data-column-type=autocomplete] {
|
||||
input[type=text] {
|
||||
padding: 6px 10px 7px!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html.safari, html.gecko {
|
||||
.control-table {
|
||||
td[data-column-type=string],
|
||||
td[data-column-type=autocomplete] {
|
||||
input[type=text] {
|
||||
padding: 5px 10px 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.table-widget-autocomplete {
|
||||
background: white;
|
||||
font-size: 13px;
|
||||
margin-top: 0;
|
||||
border: 1px solid #808c8d;
|
||||
border-top: 1px solid #ecf0f1;
|
||||
.border-bottom-radius(4px);
|
||||
|
||||
li a {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checkbox editor
|
||||
*/
|
||||
|
||||
.control-table {
|
||||
td[data-column-type=checkbox] {
|
||||
div[data-checkbox-element] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: @border-radius-base;
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid @color-custom-input-border;
|
||||
margin: 6px 5px 6px 10px;
|
||||
cursor: pointer;
|
||||
|
||||
.user-select(none);
|
||||
|
||||
&:hover {
|
||||
border-color: darken(@color-custom-input-border, 10%);
|
||||
color: darken(@color-custom-input-icon, 10%);
|
||||
}
|
||||
|
||||
&.checked {
|
||||
border-width: 2px;
|
||||
|
||||
&:before {
|
||||
.icon(@check);
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
left: 1px;
|
||||
top: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: @color-focus;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dropdown editor
|
||||
*/
|
||||
|
||||
.control-table {
|
||||
td[data-column-type=dropdown] {
|
||||
.dropdown-arrow() {
|
||||
.icon(@angle-down);
|
||||
font-size: 13px;
|
||||
line-height: 100%;
|
||||
color: #95a5a6;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.content-container:not(.readonly) {
|
||||
.user-select(none);
|
||||
|
||||
[data-view-container] {
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
.dropdown-arrow();
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
color: @link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-dropdown-open=true] {
|
||||
background: white;
|
||||
[data-view-container]:after {
|
||||
.icon(@angle-up);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Frameless control styles start */
|
||||
|
||||
.widget-field.frameless .control-table {
|
||||
.table-container {
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
.border-radius(0);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Frameless control styles end */
|
||||
|
||||
html.cssanimations {
|
||||
.control-table td[data-column-type=dropdown] {
|
||||
[data-view-container].loading:after {
|
||||
background: url('../../../../../../modules/system/assets/ui/images/loader-transparent.svg') 50% 50%;
|
||||
background-size: 15px 15px;
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 6px;
|
||||
right: 5px;
|
||||
content: ' ';
|
||||
.animation(spin 1s linear infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-control-dropdown-list {
|
||||
.user-select(none);
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 1px solid @table-active-border;
|
||||
border-top: none;
|
||||
padding-top: 1px;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
.box-sizing(border-box);
|
||||
.border-bottom-radius(4px);
|
||||
|
||||
ul {
|
||||
border-top: 1px solid #ecf0f1;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
font-size: 13px;
|
||||
color: #555555;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
background: @color-focus;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user