diff --git a/ckanext/og_datatables_datefilterview/__init__.py b/ckanext/og_datatables_datefilterview/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ckanext/og_datatables_datefilterview/assets/.gitignore b/ckanext/og_datatables_datefilterview/assets/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/ckanext/og_datatables_datefilterview/assets/og_datatables_datefilterview.css b/ckanext/og_datatables_datefilterview/assets/og_datatables_datefilterview.css new file mode 100644 index 0000000..5a8f01a --- /dev/null +++ b/ckanext/og_datatables_datefilterview/assets/og_datatables_datefilterview.css @@ -0,0 +1,286 @@ +body { + background: none; +} + +/* this is important for DataTables to do column sizing properly */ +table { + max-width: none !important; +} + +/* so user does not see table being dynamically created */ +body.dt-view { + visibility: hidden; +} + +/* we need to do this as the dt-print-view is cloned from dt-view */ +body.dt-print-view { + visibility: visible; +} + +table.dataTable { + margin-left: 0px; +} + +div.dataTables_scroll{ + width: 100% !important; +} + +/* processing message should hover over table and be visible */ +#dtprv_processing { + z-index: 100; + display: block; +} + +#dtprv_processing.pre-init::after{ + content: ''; + display: block; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: white; + z-index: -1; +} + +/* for table header, don't wrap */ +table.dataTable th { + white-space: nowrap; + overflow: hidden; + text-overflow: clip; +} + +/* clip content if it will overrun column width */ +table.dataTable td { + white-space: nowrap; + overflow: hidden; + text-overflow: clip; +} + +/* but show the data on hover */ +table.dataTable td:hover { + overflow: visible; + text-overflow: visible; +} + +/* when ellipsis_length === 0, wrap to column name width */ +table.dataTable td.wrapcell { + white-space: normal !important; + word-break: normal !important; + text-overflow: clip !important; +} + + +/* make _id column darker ala Excel */ +div.DTFC_LeftBodyWrapper { + filter: brightness(0.85); +} + + +/* for dynamic resizing of datatable, needed for scrollresize */ +#resize_wrapper { + position: absolute; + top: 0.1em; + left: 0.1em; + right: 0.1em; + bottom: 0.1em; + max-width: 100vw; + max-height: 100vh; +} + +/* fine-tune positioning of various info elements for Bootstrap */ +.dataTables_length{ + display:inline; +} + +#dtprv_filter { + text-align: right; + display: inline-flex; + float: right; + margin-top: -1.4em; +} + +#dtprv_filter label::after { + content: ''; +} + +#filterinfoicon { + margin-top: 0.75em; +} + +div.resourceinfo { + display: inline; + font-weight: bold; +} + +div.sortinfo { + display: block; +} + +#dtprv_paginate { + margin-top: -2.75em; +} + +@media screen and (max-width: 767px) { + #dtprv_filter { + margin-top: 1px; + } + #dtprv_paginate { + margin-top: 1px; + } +} + +/* for webkit, blink browsers, use input type search cancel button */ +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + cursor:pointer; + height: 12px; + width: 12px; + background-image: url('/vendor/FontAwesome/images/times-circle-solid.svg'); +} + +/* right align datatable buttons */ +div.dt-buttons { + position: relative; + float: right; +} + +/* tighten it up */ +button.dt-button { + margin-right: 0em; +} + +div.dt-button-collection.fixed.four-column { + margin-left: 0px; +} + +div.dt-button-background { + background: rgba(0, 0, 0, 0.7); + /* Fallback */ + background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* IE10 Consumer Preview */ + background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Firefox */ + background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Opera */ + background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); + /* Webkit (Safari/Chrome 10) */ + background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Webkit (Chrome 11+) */ + background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* W3C Markup, IE10 Release Preview */ +} + +div.dt-button-collection.fixed { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-65%, -20%); +} + +.table-striped > tbody > tr.selected > td, +.table-striped > tbody > tr.selected > th { + background-color: #0275d8; + color: #ffffff; +} + +.table-striped > tbody > tr.selected > td > a { + color: #ffffff; + text-decoration: underline; +} + +/* for zooming thumbnails on hover */ +.zoomthumb { +-webkit-transition: all 0.3s ease-in-out; +-moz-transition: all 0.3s ease-in-out; +transition: all 0.3s ease-in-out; +cursor: -webkit-zoom-in; +cursor: -moz-zoom-in; +cursor: zoom-in; +} + +.zoomthumb:hover, +.zoomthumb:active, +.zoomthumb:focus { +/**adjust scale to desired size, +add browser prefixes**/ +-ms-transform: scale(2.5); +-moz-transform: scale(2.5); +-webkit-transform: scale(2.5); +-o-transform: scale(2.5); +transform: scale(2.5); +position:relative; +z-index:100; +} + +.zoomthumb img { + display: block; + width: 100%; + height: auto; +} + +/* Animation CSS */ +#target { + box-sizing: border-box; + width: 40px; + height: 40px; + border-radius: 4px; + position: absolute; + top: calc(50% - 20px); + left: calc(50% - 20px); + background: #7d0; + box-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, .2), inset -1px -1px 0 0 rgba(0, 0, 0, .05); +} + +[class^="animated-"], +[class*=" animated-"] { + animation-fill-mode: both; +} + +@keyframes shake { + 0%, 100% {transform: translateX(0);} + 20%, 60% {transform: translateX(-6px);} + 40%, 80% {transform: translateX(6px);} +} + +.animated-shake { + animation: shake .4s; +} + +.dt-button-collection .dropdown-menu > a { + display: list-item; + padding: 3px 10px; + clear: both; +} + +.dt-button-collection .dropdown-menu > a:hover, +.dt-button-collection .dropdown-menu > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} + +.dt-button-collection .dropdown-menu > a.active { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} + +div.dtfc-right-top-blocker { + display: none !important; +} + +table.dataTable.table-striped > tbody > tr.odd > * { + background-color: #f9f9f9; + box-shadow: none; +} + +.table-striped tbody tr:nth-child(even) td, +.table-striped tbody tr:nth-child(even) th { + background-color: #ffffff; +} + +table.dataTable.table-hover > tbody > tr:hover > * { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.075); +} \ No newline at end of file diff --git a/ckanext/og_datatables_datefilterview/assets/og_datatables_datefilterview.js b/ckanext/og_datatables_datefilterview/assets/og_datatables_datefilterview.js new file mode 100644 index 0000000..8953941 --- /dev/null +++ b/ckanext/og_datatables_datefilterview/assets/og_datatables_datefilterview.js @@ -0,0 +1,948 @@ +/* global $ jQuery gdataDict gresviewId */ + +// global vars used for state saving/deeplinking +let gsavedPage +let gsavedPagelen +let gsavedSelected +// global var for current view mode (table/list) +let gcurrentView = 'table' +// global var for sort info, global so we can show it in copy/print +let gsortInfo = '' +// global vars for filter info labels +let gtableSearchText = '' +let gcolFilterText = '' + +let datatable +const gisFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 +let gsearchMode = '' +let gstartTime = 0 +let gelapsedTime + +// HELPER FUNCTIONS +// helper for filtered downloads +const run_query = function (params, format) { + const form = $('#filtered-datatables-download') + const p = $('') + p.attr('value', JSON.stringify(params)) + form.append(p) + const f = $('') + f.attr('value', format) + form.append(f) + form.submit() + p.remove() + f.remove() +} + +// helper for setting expiring localstorage, ttl in secs +function setWithExpiry (key, value, ttl) { + const now = new Date() + + // `item` is an object which contains the original value + // as well as the time when it's supposed to expire + const item = { + value: value, + expiry: ttl > 0 ? now.getTime() + (ttl * 1000) : 0 + } + window.localStorage.setItem(key, JSON.stringify(item)) +} + +// helper for getting expiring localstorage +function getWithExpiry (key) { + const itemStr = window.localStorage.getItem(key) + // if the item doesn't exist, return null + if (!itemStr) { + return null + } + let item + try { + item = JSON.parse(itemStr) + } catch { + return null + } + const now = new Date() + // compare the expiry time of the item with the current time + if (item.expiry && now.getTime() > item.expiry) { + // If the item is expired, delete the item from storage + // and return null + window.localStorage.removeItem(key) + return null + } + return item.value +} + +// helper for modal print +function printModal (title) { + const contents = document.querySelector('.dtr-details').innerHTML + const prtWindow = window.open('', '_blank') + prtWindow.document.write('

' + title + '

') + prtWindow.document.write(contents) + prtWindow.document.write('
') + prtWindow.print() + prtWindow.close() +} + +// helper for modal clipboard copy +function copyModal (title) { + const el = document.querySelector('.dtr-details') + const body = document.body + let range + let sel + if (document.createRange && window.getSelection) { + range = document.createRange() + sel = window.getSelection() + sel.removeAllRanges() + try { + range.selectNodeContents(el) + sel.addRange(range) + } catch (e) { + range.selectNode(el) + sel.addRange(range) + } + } else if (body.createTextRange) { + range = body.createTextRange() + range.moveToElementText(el) + range.select() + } + document.execCommand('copy') + window.getSelection().removeAllRanges() +} + +// force column auto width adjustment to kick in +// used by "Autofit columns" button +function fitColText () { + const dt = $('#dtprv').DataTable({ retrieve: true }) + if (gcurrentView === 'list') { + dt.responsive.recalc() + } + dt.columns.adjust().draw(false) +} + +// ensure element id is valid +function validateId (id) { + id = id.toLowerCase() + // Make alphanumeric (removes all other characters) + id = id.replace(/[^a-z0-9_\s-]/g, '') + // Convert whitespaces and underscore to # + id = id.replace(/[\s_]/g, '#') + // Convert multiple # to hyphen + id = id.replace(/[#]+/g, '-') + return id +} + +// compile sort & active filters for display in print, clipboard copy & search tooltip +function filterInfo (dt, noHtml = false, justFilterInfo = false, wrapped = false) { + let filtermsg = justFilterInfo ? '' : document.getElementById('dtprv_info').innerText + + const selinfo = document.getElementsByClassName('select-info')[0] + + if (selinfo !== undefined) { + filtermsg = filtermsg.replace(selinfo.innerText, ', ' + selinfo.innerText) + } + + // add active filter info to messageTop + if (gsearchMode === 'table') { + filtermsg = filtermsg + '
' + gtableSearchText + ': ' + dt.search() + } else if (gsearchMode === 'column') { + let colsearchflag = false + let colsearchmsg = '' + dt.columns().every(function () { + const colsearch = this.search() + const colname = this.name() + + if (colsearch) { + colsearchflag = true + colsearchmsg = colsearchmsg + ' ' + colname + ':
' + colsearch + ', ' + } + }) + if (colsearchflag) { + filtermsg = filtermsg + '
' + gcolFilterText + ':
' + colsearchmsg.slice(0, -2) + } + } + filtermsg = justFilterInfo ? filtermsg : filtermsg + '
' + gsortInfo + filtermsg = noHtml ? filtermsg.replace(/(<([^>]+)>)/ig, '') : filtermsg + filtermsg = wrapped ? filtermsg.replace(/,/g, '\n') : filtermsg + return filtermsg.trim() +}; + +// Copy deeplink to clipboard +function copyLink (dt, deeplink, shareText, sharemsgText) { + const hiddenDiv = $('
') + .css({ + height: 1, + width: 1, + overflow: 'hidden', + position: 'fixed', + top: 0, + left: 0 + }) + + const textarea = $('