(function ($) { //Erm... weird out of scope variables...? //var newSearch = false; var searchesPending = 0; $(function () { var _ql = $(".senior_quicklist"); _ql.snrQuickList(); }); $.fn.snrQuickList = function (options) { if (this.length > 1) { this.each(function () { $(this).snrQuickList(options) }); return this; } var localStorage; try { localStorage = window.localStorage; } catch (e) { // Access denied :-( } var ql = this; var searching = null; var qlDefaults = { newSearch: false, pageSize: parseInt(ql.attr("data-pageSize")), sortColIndex: parseInt(ql.attr("data-sortColIndex")), sortDirection: ql.attr("data-sortDirection"), linkColTitle: ql.attr("data-linkColTitle"), linkColTemplate: ql.attr("data-linkColTemplate"), showDelete: ql.attr("data-showDelete") == "true" ? true : false, deleteText: ql.attr("data-deleteText"), showCheckCol: ql.attr("data-showCheckCol") == "true" ? true : false, provider: ql.attr("data-provider"), pathToDataService: ql.attr("data-pathToDataService"), infiniteScrolling: ql.attr("data-infScroll") == "true" ? true : false, enablePaging: ql.attr("data-paging") == "false" ? false : true, showResultsInfo: ql.attr("data-info") == "false" ? false : true, scrollHeight: ql.attr("data-sScrollY"), providerData: ql.attr("data-providerData"), enablePipelining: ql.attr("data-enablePipelining") == "true" ? true : false, pipeSize: parseInt(ql.attr("data-pipeSize")), serverSide: ql.attr("data-serverSide") == "false" ? false : true, enableInstantResults: ql.attr("data-enableInstantResults") == "false" ? false : true, enableTableTools: ql.attr("data-enableTableTools") == "false" ? false : true, tableToolsSwfPath: ql.attr("data-tableToolsSwfPath") }; var opts = $.extend({}, $.fn.snrQuickList.defaults, qlDefaults, options); //The object that represents each search field. var SearchField = function (fieldElement) { this.dataFieldName = fieldElement.attr("data-datafieldname"); this.dataType = fieldElement.attr("data-dataType"); this.compType = parseInt(fieldElement.attr("data-compType")); this.pre = fieldElement.attr("data-prefilter") == "true"; this.values = []; //Returns a list of values associated with the field. There is usually only one value. But for some fields, like a date range, there are more. this.getValues = function () { //if its a checkbox if (this.dataType == 'System.Web.UI.WebControls.CheckBoxList') { this.values = $("input:checked", fieldElement).map(function () { return $(this).is(".watermark") ? "" : $(this).next().text(); }).get(); return this.values; } else { this.values = $(".formfield_input", fieldElement).map(function () { return $(this).is(".watermark") ? "" : $(this).val(); }).get(); return this.values; } } }; var resultsCont = $('.resultsCont', ql); var searchAllElement = $('.searchAll', ql); var searchAllField = new SearchField(searchAllElement); var searchButton = $('.search_button', ql); var removeSelectedButton = $('.remove_selected_button', ql); var dataArgs = {}; var totalRecords = 0; var fieldOffset = 0; var hdnStore = $('input.ql_hdnChecked', ql); var oDataTable; //For pipelining var oCache = { iCacheLower: -1 }; this.settings = opts; this.getDataXHR = null; this.getData = function (addArgs, callback) { var self = this; //Get ajax package of field values //Fix to make sure providerdata is extended rather than overwirtten var filterData = getFilterData(); if (addArgs && addArgs.hasOwnProperty("providerData")) { var curProviderData = JSON.parse(filterData.providerData) var newProviderData = typeof addArgs.providerData === "string" ? JSON.parse(addArgs.providerData) : addArgs.providerData; addArgs.providerData = JSON.stringify($.extend(true, curProviderData, newProviderData)); } dataArgs = $.extend(true, filterData, addArgs); //Initial settings var aoDataDict = $.extend(getQLSettings(), { sEcho: 1, iSortCol_0: opts.sortColIndex, sSortDir_0: opts.sortDirection, iSortingCols: 1, iDisplayStart: 0, iDisplayLength: (opts.serverSide) ? ( opts.pageSize > 0 ? opts.pageSize : 99999) : -1 }); //Get settings from local storage var dataTable = resultsCont.closest('table'); if (dataTable.length > 0) { if (localStorage) { var storedArgs = JSON.parse(localStorage.getItem('DataTables_' + dataTable.attr('id') + '_' + window.location.pathname + window.location.search)); if (storedArgs) { opts.pageSize = storedArgs.iLength; dataArgs.aoData.iDisplayLength = opts.pageSize; if (!opts.newSearch) { dataArgs.aoData.iDisplayStart = storedArgs.iStart; } else { storedArgs.iStart = 0; localStorage.setItem('DataTables_' + dataTable.attr('id') + '_' + window.location.pathname + window.location.search, JSON.stringify(storedArgs)); } } } } if (opts.enablePipelining & !opts.serverSide) { oCache.iCacheLower = 0; oCache.iCacheUpper = (opts.pageSize * opts.pipeSize); oCache.iDisplayLength = opts.pageSize; oCache.iDisplayStart = 0; oCache.newFilter = true; aoDataDict.iDisplayLength = opts.pageSize * opts.pipeSize; } //Accept args passed in with addArgs dataArgs.aoData = $.extend(aoDataDict, opts.aoData, dataArgs.aoData); oCache.aoDataDict = dataArgs.aoData; //Send the JSON field data to the server //Get the toolbox items //Abort existing request if there is one ongoing. if (self.getDataXHR && self.getDataXHR.readyState != 4 && !(dataArgs.aoData.iDisplayStart > 0)) { self.getDataXHR.abort(); } self.getDataXHR = $.ajax({ "type": "POST", "url": opts.pathToDataService, "data": JSON.stringify(dataArgs), "cache": false, "contentType": "application/json; charset=utf-8", "dataType": "json", "success": function (data, textStatus, jqXHR) { //Clear selected items (as we have new data) hdnStore.val(""); data = data.hasOwnProperty('d') ? data.d : data; //Send the data to any plugins that might exist. The default results display can be cancelled here by this method returning false. var dataObj = JSON.parse(data); if (opts.onResultsFetched.call(this, dataObj)) { populateSearchResults(data, textStatus, jqXHR); reInitQS(ql); } if (callback) { callback.call(data); } if (opts.onSearchComplete) { opts.onSearchComplete.call(dataObj) } }, "error": function (XMLHttpRequest, textStatus, errorThrown) { if (XMLHttpRequest.status != 0) { if (window.console && window.console.log) { console.log("Error - " + JSON.stringify(XMLHttpRequest) + " textStatus - " + textStatus + " errorThrown - " + errorThrown); } } } }); }; // get ids of selected rows this.getSelectedRowIds = function () { return hdnStore.val() == "" ? [] : JSON.parse(hdnStore.val()); }; this.getSearchFields = function() { var qlJSON = {}; qlJSON.SearchAll = searchAllField.getValues()[0]; qlJSON.fields = []; //if ((qlJSON.SearchAll == '' || typeof qlJSON.SearchAll == "undefined") && $(ql).find('.ql_searchField.sf_searchall input:first').length > 0) { // qlJSON.SearchAll = $(ql).find('.ql_searchField.sf_searchall input:first').val(); // Needed for listings //} var fields = $(ql).find('.ql_searchField', ql); fields.each(function (i) { var sfInfo = new SearchField($(this)); sfInfo.getValues(); qlJSON.fields.push(sfInfo); }); return qlJSON; } // get ids of all rows this.getContainedRowIds = function () { return oDataTable.$('tr').map(function () { return $(this).attr('id'); }).get(); }; this.getDataTable = function () { return (oDataTable) ? oDataTable : resultsCont.dataTable({ "bRetrieve": true }); }; this.reinitialise = function () { reInitQS(this); } // add selected rows to another quicklist - this must have the same field spec and // ServerSide set to false this.addSelectedRowsToQuickList = function (targetQuicklist) { var targetDataTable = targetQuicklist.getDataTable(); var currentRowData = targetQuicklist.getContainedRowIds(); var selectedRowData = []; oDataTable.$('tr:visible').each(function () { var row = this; if ($(row).find('.ql_sel').is(':checked')) { var rowData = oDataTable.fnGetData(row); // check if we already have this row if ($.inArray(rowData.DT_RowId, currentRowData) == -1) { selectedRowData.push(rowData); } } }); targetDataTable.fnAddData(selectedRowData); // uncheck the selected options in this quicklist as we can't remove them oDataTable.$('.ql_sel:checked, .ql_sel_all:checked').removeAttr('checked'); targetQuicklist.reinitialise(); }; this.removeRowFromQuicklist = function (dtRowId) { var row = oDataTable.$('tr[id="' + dtRowId + '"]'); if (row) { oDataTable.fnDeleteRow(row[0], null, true); } } var searchFields = $('.ql_searchField', ql); searchAllElement.add(searchFields).find('input[type="text"]').keyup(function (ev) { //var input = $('input[type="text"]', searchAllElement); //don't search on tab out if (ev.which == 9) { return; } if (opts.enableInstantResults) { opts.newSearch = true; clearTimeout(searching); searching = setTimeout(function () { ql.getData(); }, 250); } if (ev.which == 13) { ql.getData(); } ev.preventDefault(); }); searchFields.on("change chosen:updated liszt:updated datepicker-select", function (ev) { if (opts.enableInstantResults && !$(ev.target).is(":text")) { opts.newSearch = true; ql.getData(); } ev.preventDefault(); }); searchButton.click(function (ev) { opts.newSearch = true; searchesPending += 1; ql.getData(); return false; }); $('.ql_searchField', ql).keypress(function (ev) { if (ev.which == 13) { ev.preventDefault();//don't want this to bubble searchButton.trigger("click"); } }); removeSelectedButton.click(function (ev) { var dataTable = ql.getDataTable(); dataTable.$('tr:visible').each(function () { var row = this; if ($(row).find('.ql_sel').is(':checked')) { dataTable.fnDeleteRow(row, null, true); } }); return false; }); var populateSearchResults = function (data, textStatus, jqXHR) { //alert(data); var resultsData = JSON.parse(data); oCache.lastJson = jQuery.extend(true, {}, resultsData); if (opts.enablePipelining) { if (oCache.iCacheLower != oCache.iDisplayStart) { resultsData.aaData.splice(0, oCache.iDisplayStart - oCache.iCacheLower); } resultsData.aaData.splice(oCache.iDisplayLength, resultsData.aaData.length); } totalRecords = resultsData.iTotalDisplayRecords; fieldOffset = resultsData.iFieldOffset; var localStorage; try { localStorage = window.localStorage; } catch (e) { // Access denied :-( } var dataTablesSettings = { "bFilter": false, "bDestroy": true, "aaData": resultsData.aaData, "aoColumnDefs": resultsData.aoColumnDefs, "bProcessing": true, "bServerSide": opts.serverSide, "sAjaxDataSource": opts.pathToDataService, "iDisplayLength": opts.pageSize > 0 ? opts.pageSize : totalRecords, "aLengthMenu": [[10, 25, 50, 100, totalRecords], [10, 25, 50, 100, 'All']], "bAutoWidth": false, "oLanguage": { "sInfoFiltered": "" }, "fnServerData": function (sSource, aoData, fnCallback) { updateResults(ql, totalRecords, fieldOffset, sSource, aoData, fnCallback); }, "fnCreatedRow": function (nRow, aData, iDataIndex) { if (aData.DT_Meta) { $(nRow).attr("data-rowMeta", aData.DT_Meta); } $.each(aData, function (key, value) { if (key.indexOf("DT_") == 0 && key !== "DT_Meta") { $(nRow).attr("data-" + key.substr(3), value); } }); }, "bScrollInfinite": opts.infiniteScrolling, "bPaginate": opts.enablePaging ? true : false, "bInfo": opts.showResultsInfo, "bScrollCollapse": opts.infiniteScrolling ? true : false, "sScrollY": opts.scrollHeight, "aaSorting": [[opts.sortColIndex, opts.sortDirection]], "fnInfoCallback": function (oSettings, iStart, iEnd, iMax, iTotal, sPre) { if (opts.infiniteScrolling) { return "Showing " + iTotal + " entries"; } else { return sPre; } }, "bStateSave": true, "fnStateSave": function (oSettings, oData) { if (localStorage) localStorage.setItem('DataTables_' + this.fnSettings().sTableId + '_' + window.location.pathname + window.location.search, JSON.stringify(oData)); }, "fnStateLoad": function (oSettings) { if (localStorage) return JSON.parse(localStorage.getItem('DataTables_' + this.fnSettings().sTableId + '_' + window.location.pathname + window.location.search)); } } if (opts.enableTableTools) { dataTablesSettings["sDom"] = 'T<"clear">lfrtip'; dataTablesSettings["oTableTools"] = { "sSwfPath": opts.tableToolsSwfPath, "aButtons": ["csv", "pdf", "print", "copy"] } } //Set up the data table object here oDataTable = resultsCont.dataTable(dataTablesSettings); }; //Datatables is built to work with an array of objects, whereas asp.net webservices works better with dictioanry objects. So here we convert from a dictionary to and array. //There should never be a huge amount of data here so performance hit on the client is small. function objToArray(oDictionary) { var ary = []; $.each(oDictionary, function (n, v) { ary.push({ name: n, value: v }); }); return ary; } function arrayToObj(array) { var obj = {}; for (var i = 0; i < array.length; i++) { obj[array[i].name] = array[i].value; } return obj; } function getServerData(dataArgs, aoData, fnCallback) { var self = this; var sData = dataArgs; var aoDataDict = arrayToObj(aoData); aoDataDict = $.extend(aoDataDict, getQLSettings()); //convert the object array given by datatables into a more usable dictionary. Do this on the client to save server burden. for (var i = 0; i < aoData.length; i++) { aoDataDict[aoData[i].name] = aoData[i].value; } //Let the server know that this is either paging or sorting. the filter is the same so we don't need to get total number of field etc. aoDataDict.bFilterChanged = false; aoDataDict.iTotalDisplayRecords = totalRecords; aoDataDict.iFieldOffset = fieldOffset; sData.aoData = aoDataDict; //Abort existing request if there is one ongoing. if (self.getDataXHR && self.getDataXHR.readyState != 4) { self.getDataXHR.abort(); } self.getDataXHR = $.ajax({ "type": "POST", "url": opts.pathToDataService, "data": JSON.stringify(sData), "contentType": "application/json; charset=utf-8", "dataType": 'json', "success": function (data) { data = data.hasOwnProperty('d') ? data.d : data; //alert(data); var resultsData = JSON.parse(data) oCache.lastJson = jQuery.extend(true, {}, resultsData); if (opts.enablePipelining) { if (oCache.iCacheLower != oCache.iDisplayStart) { resultsData.aaData.splice(0, oCache.iDisplayStart - oCache.iCacheLower); } resultsData.aaData.splice(oCache.iDisplayLength, resultsData.aaData.length); } fnCallback.call(this, resultsData); reInitQS(ql); } }); } function updateResults(ql, totalRecords, fieldOffset, sSource, aoData, fnCallback) { var echo = aoData[0].value; if (echo > 1) { if (opts.enablePipelining) { //////////////// PIPELINING ///////////////////// var iPipe = opts.pipeSize; /* Ajust the pipe size */ var bNeedServer = false; var iRequestStart = fnGetKey(aoData, "iDisplayStart"); var iRequestLength = fnGetKey(aoData, "iDisplayLength"); var iRequestEnd = iRequestStart + iRequestLength; oCache.iDisplayStart = iRequestStart; /* outside pipeline? */ if (oCache.iCacheLower < 0 || iRequestStart < oCache.iCacheLower || iRequestEnd > oCache.iCacheUpper) { bNeedServer = true; } /* sorting etc changed? */ //Before the first post to the server, there is some info that we don't know, like number of records or column values. However, piping needs to check //the previous request. So here we update the cached request with only the missing data. if (oCache.newFilter) { oCache.lastRequest = []; for (var i = 0, iLen = aoData.length; i < iLen; i++) { if (oCache.aoDataDict.hasOwnProperty(aoData[i].name)) { oCache.lastRequest.push({ name: aoData[i].name, value: oCache.aoDataDict[aoData[i].name] }); } else { oCache.lastRequest.push({ name: aoData[i].name, value: aoData[i].value }); } } oCache.newFilter = false; } if (oCache.lastRequest && !bNeedServer) { for (var i = 0, iLen = aoData.length; i < iLen; i++) { if (aoData[i].name != "iDisplayStart" && aoData[i].name != "iDisplayLength" && aoData[i].name != "sEcho") { if (aoData[i].value != oCache.lastRequest[i].value) { //alert("Setting need server to true because of " + aoData[i].name + ": " + aoData[i].value + " - " + oCache.lastRequest[i].value); bNeedServer = true; break; } } } } //alert("need server? - " + bNeedServer); /* Store the request for checking next time around */ oCache.lastRequest = aoData.slice(); if (bNeedServer) { if (iRequestStart < oCache.iCacheLower) { iRequestStart = iRequestStart - (iRequestLength * (iPipe - 1)); if (iRequestStart < 0) { iRequestStart = 0; } } oCache.iCacheLower = iRequestStart; oCache.iCacheUpper = iRequestStart + (iRequestLength * iPipe); oCache.iDisplayLength = fnGetKey(aoData, "iDisplayLength"); fnSetKey(aoData, "iDisplayStart", iRequestStart); fnSetKey(aoData, "iDisplayLength", iRequestLength * iPipe); getServerData(dataArgs, aoData, fnCallback); } else { refreshFromCache(echo, iRequestStart, iRequestLength, fnCallback); return; } //////////////// PIPELINING ///////////////////// } else { getServerData(dataArgs, aoData, fnCallback); } } else { refreshFromCache(echo, 0, opts.pageSize, fnCallback); } } function refreshFromCache(echo, iRequestStart, iRequestLength, fnCallback) { var json = jQuery.extend(true, {}, oCache.lastJson); if (opts.enablePipelining) { json.sEcho = echo; /* Update the echo for each response */ json.aaData.splice(0, iRequestStart - oCache.iCacheLower); json.aaData.splice(iRequestLength, json.aaData.length); } fnCallback.call(this, json); reInitQS(ql); } function fnSetKey(aoData, sKey, mValue) { for (var i = 0, iLen = aoData.length; i < iLen; i++) { if (aoData[i].name == sKey) { aoData[i].value = mValue; } } } function fnGetKey(aoData, sKey) { for (var i = 0, iLen = aoData.length; i < iLen; i++) { if (aoData[i].name == sKey) { return aoData[i].value; } } return null; } function getQLSettings() { return { bFilterChanged: true, sLinkColTitle: opts.linkColTitle, sLinkColTemplate: opts.linkColTemplate, bShowDeleteCol: opts.showDelete, sDeleteText: opts.deleteText, bShowCheckCol: opts.showCheckCol, sProvider: opts.provider } } function reInitQS(ql) { opts.initDeleteLinks(ql, opts.deleteComfirmText); //Initialize checkboxes var chkAll = $('input:checkbox.ql_sel_all', ql); //This line stops jQuery doing some weird caching thing. chkAll.unbind('click'); var checkBoxes = $('input:checkbox.ql_sel', ql); checkBoxes.unbind('click'); hdnStore = $('input.ql_hdnChecked', ql); //Check boxes if there are any selected on this page var checkedItems = hdnStore.val() == "" ? [] : JSON.parse(hdnStore.val()); if (checkedItems.length > 0) { checkBoxes.each(function (i) { var rowId = $(this).closest('tr').attr("id"); if ($.inArray(rowId, checkedItems) > -1) { this.checked = true; } else { this.checked = false; } }); } chkAll.click(function () { var checkedItems = hdnStore.val() === "" ? [] : JSON.parse(hdnStore.val()); var checked = this.checked; checkBoxes.each(function (i) { this.checked = checked; var rowId = $(this).closest('tr').attr("id"); selectItem(rowId, checkedItems, checked); if (opts.onCheckItem) { opts.onCheckItem.call(this, rowId, checked); } }); hdnStore.val(JSON.stringify(checkedItems)); if (opts.onCheckAll) { //opts.onCheckAll.call(this, rowId, this.checked); opts.onCheckAll.call(this.checked, checkedItems, hdnStore); } }); checkBoxes.click(function () { var checkedItems = hdnStore.val() === "" ? [] : JSON.parse(hdnStore.val()); var rowId = $(this).closest('tr').attr("id"); selectItem(rowId, checkedItems, this.checked); hdnStore.val(JSON.stringify(checkedItems)); if (opts.onCheckItem) { opts.onCheckItem.call(this, rowId, this.checked); } }); } function selectItem(rowId, checkedItems, checked) { var iIndex = $.inArray(rowId, checkedItems) if (iIndex > -1) { if (!checked) { checkedItems.splice(iIndex, 1); } } else { if (checked) { checkedItems.push(rowId); } } } function getFilterData() { var qlJSON = {}; qlJSON.SearchAll = searchAllField.getValues()[0]; qlJSON.fields = []; if ((qlJSON.SearchAll == '' || typeof qlJSON.SearchAll == "undefined") && $(ql).find('.ql_searchField.sf_searchall input:first').length > 0) { qlJSON.SearchAll = $(ql).find('.ql_searchField.sf_searchall input:first').val(); // Needed for listings } var fields = $(ql).find('.ql_searchField', ql); fields.each(function (i) { var sfInfo = new SearchField($(this)); sfInfo.getValues(); qlJSON.fields.push(sfInfo); }); var data = { filterData: JSON.stringify(qlJSON), deleteIds: [], providerData: opts.providerData, aoData: {} }; if (opts.onGetFilterData !== undefined) { opts.onGetFilterData(data); } ql.trigger("getFilterData", data); return data; } var getFieldInfo = function (field) { return new SearchField(field); }; this.data("QuickList", this); return this; } function get_type(thing) { if (thing === null) return "[object Null]"; // special case return Object.prototype.toString.call(thing); } $(function () { var _ql = $(".senior_quicklist_knockout"); if (_ql.attr("data-koTemplateId")) { _ql.snrQuickList_Knockout(); } }); function initQLPager(vm, pagePrev, pageNext) { var currentPage = vm.currentPage(); //First page if (currentPage <= 1) { pagePrev.removeClass("paginate_enabled_previous"); pagePrev.addClass("paginate_disabled_previous"); } else { pagePrev.removeClass("paginate_disabled_previous"); pagePrev.addClass("paginate_enabled_previous"); } //Last page if (currentPage >= vm.getNumPages()) { pageNext.removeClass("paginate_enabled_next"); pageNext.addClass("paginate_disabled_next"); } else { pageNext.removeClass("paginate_disabled_next"); pageNext.addClass("paginate_enabled_next"); } var pages = []; for (var i = 1; i <= vm.getNumPages(); i++) { pages.push({ num: i }); } vm.pages(pages); } $.fn.snrQuickList_Knockout = function (options) { var that = this; if (options === "viewModel" && this.data("viewModel")) { return this.data("viewModel"); } if (options === "quicklist" && this.data("quicklist")) { return this.data("quicklist"); } if (this.length > 1) { this.each(function () { $(this).snrQuickList_Knockout(opts) }); return this; } if (this.length == 0) { return; } var ql = $(this).snrQuickList({ aoData: { koTemplates: true} }); this.data("quicklist", ql); var qlDefaults = { containerId: ql.attr("data-koTemplateId") }; var opts = $.extend({}, qlDefaults, options); this.viewModel = { vm: this, items: ko.observableArray([]), hasItems: ko.observable(false), currentPage: ko.observable(1), currentSortCol: ql.settings.sortColIndex, totalRecords: ko.observable(0), itemsPerPage: ql.settings.pageSize, updateCallback: '', getNumPages: function () { return Math.ceil(this.totalRecords() / this.itemsPerPage) }, deleteItem: function (ele) { var itemToRemove = ko.dataFor(ele); ql.settings.onDelete.call(this, itemToRemove.DT_RowId, ql.getData, ql.settings.deleteComfirmText); }, changePage: function (pageNum) { if (pageNum < 1 || pageNum > this.getNumPages()) { return; } this.currentPage(pageNum); var startIndex = (pageNum - 1) * this.itemsPerPage; ql.getData({ aoData: { iDisplayStart: startIndex, iSortCol_0: this.currentSortCol} }, this.updateCallback); }, sortByCol: function (colIndex) { this.currentSortCol = parseInt(colIndex); if (isNaN(this.currentSortCol)) { return; } aoData = ql.getData({ aoData: { iDisplayStart: 0, iSortCol_0: this.currentSortCol} }); }, search: function (callback) { this.items([]); this.updateCallback = callback; var startIndex = (this.currentPage() - 1) * this.itemsPerPage; ql.getData({ aoData: { iDisplayStart: startIndex, iSortCol_0: this.currentSortCol} }, callback); }, pages: ko.observableArray([]), sortCols: ko.observableArray([{sfIndex:"0", sfTitle:"Name"}]) }; if (opts.containerId) { ko.applyBindings(this.viewModel, document.getElementById(opts.containerId)); } //Set up generic container events bindings var container = $('#' + opts.containerId); container.on("click", ".ql_item_delete", function () { that.viewModel.deleteItem(this); }); //See if there is a sorter dropdown set up var ddSorter = $('.ql_res_sorter', container); ddSorter.change(function () { that.viewModel.sortByCol($(this).val()); }); var paging = (function () { var pagingType; var vm = that.viewModel; function Paging() { var pagePrev = $(".ql_page_prev", container); var pageNext = $(".ql_page_next", container); pagePrev.click(function () { var curPage = vm.currentPage(); vm.changePage(curPage - 1); }); pageNext.click(function () { var curPage = vm.currentPage(); vm.changePage(curPage + 1); }); container.on("click", ".ql_item_page", function () { vm.changePage(parseInt((this).attr("data-pageNum"))); }); return { update: function (data) { //Create the right kind of object to be displayed in knockout template. that.viewModel.items(data.aaData); that.viewModel.hasItems(data.iTotalDisplayRecords > 0); that.viewModel.totalRecords(data.iTotalDisplayRecords); var currentPage = vm.currentPage(); //First page if (currentPage <= 1) { pagePrev.removeClass("paginate_enabled_previous"); pagePrev.addClass("paginate_disabled_previous"); } else { pagePrev.removeClass("paginate_disabled_previous"); pagePrev.addClass("paginate_enabled_previous"); } //Last page if (currentPage >= vm.getNumPages()) { pageNext.removeClass("paginate_enabled_next"); pageNext.addClass("paginate_disabled_next"); } else { pageNext.removeClass("paginate_disabled_next"); pageNext.addClass("paginate_enabled_next"); } var pages = []; for (var i = 1; i <= vm.getNumPages(); i++) { pages.push({ num: i }); } vm.pages(pages); } }; } function InfiniteScroll() { var didScroll = false, scrollDelay = 750, offset, timeoutId; $('#ql_res_pager').remove(); $(window).scroll(function () { didScroll = true; }); function onScroll() { if (didScroll) { didScroll = false; var containerTop = Math.round(container.offset().top); var containerBase = containerTop + container.height(); if (containerBase >= $(window).scrollTop() && containerBase <= ($(window).scrollTop() + $(window).height())) { $.when(callback()); // if this is an async method the post processing will halt until it is finished. } } setTimeout(onScroll, scrollDelay); } timeoutId = setTimeout(onScroll, scrollDelay); function callback() { var curPage = that.viewModel.currentPage(); return that.viewModel.changePage(curPage + 1); } return { update: function (data) { if (!ql.settings.newSearch) { var currentItems = vm.items(); vm.items.valueWillMutate(); ko.utils.arrayPushAll(currentItems, data.aaData); vm.items.valueHasMutated(); } else { vm.items(data.aaData); vm.currentPage(1); timeoutId = setTimeout(onScroll, scrollDelay); } //Create the right kind of object to be displayed in knockout template. vm.hasItems(data.iTotalDisplayRecords > 0); vm.totalRecords(data.iTotalDisplayRecords); var currentPage = vm.currentPage(); // we need to check the page that we are on and disable if we have hit the last page if (currentPage >= vm.getNumPages()) { clearTimeout(timeoutId); } } }; } if (ql.settings.infiniteScrolling) { pagingType = new InfiniteScroll(); } else { pagingType = new Paging(); } return pagingType; } ()); ql.settings.onResultsFetched = function (data) { paging.update(data); searchesPending -= 1; if (searchesPending <= 0) { ql.settings.newSearch = false; searchesPending = 0; } var sortCols = []; var index = 0; $.each(data.aoColumnDefs, function (i, v) { if (v.bSortable) { sortCols.push({ sfIndex: index, sfTitle: v.sTitle }); index++; } }); that.viewModel.sortCols(sortCols); return false; }; this.data("viewModel", this.viewModel); return this; }; $.fn.snrQuickList.defaults = { //enableInstantResults: false, deleteComfirmText: "Are you sure you want to delete this record?", onDelete: function (rowId, fnGetData, delConfirmText) { if (confirm(delConfirmText)) { var dataArgs = { deleteIds: [rowId] } fnGetData(dataArgs); } }, initDeleteLinks: function (ql, delConfirmText) { var delLinks = $('a.del', ql); delLinks.unbind('click'); delLinks.click(function () { $.fn.snrQuickList.defaults.onDelete.call(this, $(this).closest('tr').attr("id"), ql.getData, delConfirmText); }); }, onCheckItem: function (rowId, checked, ql, checkedItems) { }, onCheckAll: function (checked, checkBoxes, hdnStore) { }, onResultsFetched: function (resultsData) { return true; }, onSearchComplete: function (resultsData) { } }; } (jQuery));