http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.js ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.js new file mode 100644 index 0000000..e65e8cd --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.js @@ -0,0 +1,1045 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(require("_"), require("jQuery"), require("Backbone"), require("Backgrid")); + else if(typeof define === 'function' && define.amd) + define(["underscore", "jquery", "backbone", "backgrid"], factory); + else if(typeof exports === 'object') + exports["Backgrid.Extension.ColumnManager"] = factory(require("_"), require("jQuery"), require("Backbone"), require("Backgrid")); + else + root["Backgrid.Extension.ColumnManager"] = factory(root["_"], root["jQuery"], root["Backbone"], root["Backgrid"]); +})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__, __WEBPACK_EXTERNAL_MODULE_4__) { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + /** + * A column manager for backgrid + * + * @module Backgrid.ColumnManager + */ + // Dependencies + var _ = __webpack_require__(1); + var $ = __webpack_require__(2); + var Backbone = __webpack_require__(3); + var Backgrid = __webpack_require__(4); + + /** + * Manages visibility of columns. + * + * @class Backgrid.Extension.ColumnManager ColumnManager + * @constructor + * @param {Backgrid.Columns} columns + * @param {Object} [options] + * @param {number} [options.initialColumnCount] Initial amount of columns to show. Default is null (All visible). + * @param {boolean} [options.trackSize] + * @param {boolean} [options.trackOrder] + * @param {boolean} [options.trackVisibility] + * @param {string} [options.stateChecking] can be "strict" or "loose". + * @param {boolean} [options.saveState] + * @param {string} [options.saveStateKey] Storage key. Must be unique for location. Can be left out if this plugin is only used in one place. + * @param {string} [options.saveStateLocation] Can be "localStorage" (default) or "sessionStorage" (be aware, session stored values are lost when window is closed) + * @param {boolean} [options.loadStateOnInit] + * @param {Array} [state] + */ + Backgrid.Extension.ColumnManager = function (columns, options, state) { + // Bind backbone events + _.extend(this, Backbone.Events); + + // Save options and merge with defaults + var defaults = { + initialColumnsVisible: null, + + // State options + trackSize: true, + trackOrder: true, + trackVisibility: true, + stateChecking: "strict", + saveState: false, + saveStateKey: "", + saveStateLocation: "localStorage", + loadStateOnInit: false + }; + this.options = _.extend({}, defaults, options); + this.state = []; + + // Check if columns is instance of Backgrid.Columns + if (columns instanceof Backgrid.Columns) { + // Save columns + this.columns = columns; + + // Add columnManager to columns (instance) + columns.columnManager = this; + this.addManagerToColumns(); + + // Set state if provided + var storedState = (this.options.loadStateOnInit) ? this.loadState() : false; + if (state && this.checkStateValidity(state)) { + this.setState(state, true); + } + else if (storedState) { + this.setState(storedState, true); + } + else { + // If no initial state is provided, adhere to initial column visibility settings + this.setInitialColumnVisibility(); + + // Set current state + this.setState(this.getStateFromColumns()); + } + + // Listen to column events + if (this.options.trackVisibility || this.options.trackSize || this.options.trackOrder) { + //this.stateUpdateHandler = _.bind(this.stateUpdateHandler, this); + var events = "" + + ((this.options.trackVisibility) ? "change:renderable " : "") + + ((this.options.trackSize) ? "resize " : "") + + ((this.options.trackOrder) ? "ordered" : ""); + this.columns.on(events, _.bind(this.stateUpdateHandler, this)); + } + } + else { + // Issue warning + console.error("Backgrid.ColumnManager: options.columns is not an instance of Backgrid.Columns"); + } + }; + + /** + * Loops over all columns and sets the visibility according to provided options. + * + * @method setInitialColumnVisibility + */ + Backgrid.Extension.ColumnManager.prototype.setInitialColumnVisibility = function () { + var self = this; + + // Loop columns and set renderable property according to settings + var initialColumnsVisible = self.options.initialColumnsVisible; + + if (initialColumnsVisible) { + self.columns.each(function (col, index) { + col.set("renderable", (col.get("alwaysVisible")) ? true : index < initialColumnsVisible); + }); + } + }; + + /** + * Loops over all columns and adds the columnManager instance to VisibilityHeaderCell columns. + * + * @method addManagerToColumns + */ + Backgrid.Extension.ColumnManager.prototype.addManagerToColumns = function () { + var self = this; + + self.columns.each(function (col) { + // Look for header cell + if (col.get("headerCell") === Backgrid.Extension.ColumnManager.ColumnVisibilityHeaderCell) { + col.set("headerCell", col.get("headerCell").extend({ + columnManager: self + })); + } + + if (col.get("headerCell") instanceof Backgrid.Extension.ColumnManager.ColumnVisibilityHeaderCell) { + col.get("headerCell").columnManager = self; + } + }); + }; + + /** + * Convenience function to retrieve a column either directly or by its id. + * Returns false if no column is found. + * + * @method getColumn + * @param {string|number|Backgrid.Column} col + * @return {Backgrid.Column|boolean} + */ + Backgrid.Extension.ColumnManager.prototype.getColumn = function (col) { + // If column is a string or number, try to find a column which has that ID + if (_.isNumber(col) || _.isString(col)) { + col = this.columns.get(col); + } + return (col instanceof Backgrid.Column) ? col : false; + }; + + /** + * Hides a column + * + * @method hidecolumn + * @param {string|number|Backgrid.Column} col + */ + Backgrid.Extension.ColumnManager.prototype.hideColumn = function (col) { + // If column is a valid backgrid column, set the renderable property to false + var column = this.getColumn(col); + if (column) { + column.set("renderable", false); + } + }; + + /** + * Shows a column + * + * @method showColumn + * @param {string|number|Backgrid.Column} col + */ + Backgrid.Extension.ColumnManager.prototype.showColumn = function (col) { + // If column is a valid backgrid column, set the renderable property to true + var column = this.getColumn(col); + if (column) { + column.set("renderable", true); + } + }; + + /** + * Toggles a columns' visibility + * + * @method toggleColumnVisibility + * @param {string|number|Backgrid.Column} col + */ + Backgrid.Extension.ColumnManager.prototype.toggleColumnVisibility = function (col) { + // If column is a valid backgrid column, set the renderable property to true + var column = this.getColumn(col); + if (column) { + if (column.get("renderable")) { + this.hideColumn(column); + } + else { + this.showColumn(column); + } + } + }; + + /** + * Returns the managed column collection + * + * @method getColumnCollection + * @return {Backgrid.Columns} + */ + Backgrid.Extension.ColumnManager.prototype.getColumnCollection = function () { + return this.columns; + }; + + /** + * + * @method setState + * @param {Array} state + * @param {boolean} applyState + * @return {boolean} + */ + Backgrid.Extension.ColumnManager.prototype.setState = function (state, applyState) { + var self = this; + + // Filter state + _.filter(state, function(columnState) { + if (!_.has(columnState, "name")) { + return false; + } + + var column = self.columns.findWhere({ + name: state.name + }); + + return typeof column !== "undefined"; + }); + + // Check if state is valid + if (self.checkStateValidity(state) && state !== self.state) { + // Apply and save state + self.state = state; + self.trigger("state-changed", state); + + if (applyState) { + return self.applyStateToColumns(); + } + else { + return self.saveState(); + } + } + return false; + }; + + /** + * @method getState + * @return {Array} + */ + Backgrid.Extension.ColumnManager.prototype.getState = function () { + return this.state; + }; + + /** + * + * @method checkStateValidity + * @return {boolean} + */ + Backgrid.Extension.ColumnManager.prototype.checkStateValidity = function (state) { + // Has to be array + if (!_.isArray(state) && _.isEmpty(state)) { + return false; + } + + function checkValidityColumnState() { + return _.every(state, function(column) { + var valid = true; + + // We require a name key + if (!_.has(column, "name")) { + valid = false; + } + + // If renderable is set, should be boolean + if (_.has(column, "renderable")) { + if (!_.isBoolean(column.renderable)) { + valid = false; + } + } + + // If displayOrder is set, should be a number + if (_.has(column, "displayOrder")) { + if (!_.isNumber(column.displayOrder)) { + valid = false; + } + } + + // If width is set, should be a number or a string + if (_.has(column, "width")) { + if (!_.isNumber(column.width) && !_.isString(column.width)) { + valid = false; + } + } + + return valid; + }); + } + + // Check if state is valid + if (this.options.stateChecking === "loose") { + // At least we require 'name' keys in every objec + return checkValidityColumnState(); + } + else { + // Strict check + // Requires same length and valid name keys. + if (state.length !== this.columns.length && !checkValidityColumnState()) { + return false; + } + + var columnNameKeys = this.columns.map(function (column) { + return column.get("name"); + }); + + var newStateNameKeys = _.map(state, function (column) { + return column.name; + }); + + return columnNameKeys.sort().toString() === newStateNameKeys.sort().toString(); + } + }; + + + /** + * + * @method loadState + * @return {boolean} + */ + Backgrid.Extension.ColumnManager.prototype.loadState = function () { + // Get state from storage + var state = JSON.parse(this.getStorage().getItem(this.getStorageKey())); + if (this.checkStateValidity(state)) { + return state; + } + return false; + }; + + /** + * + * @method saveState + * @param {boolean} [force] Override save settings. + * @return {boolean} + */ + Backgrid.Extension.ColumnManager.prototype.saveState = function (force) { + if (this.options.saveState || force) { + this.getStorage().setItem(this.getStorageKey(), JSON.stringify(this.state)); + this.trigger("state-saved"); + return true; + } + return false; + }; + + /** + * @method getStorage + * @return {boolean|Storage} + * @private + */ + Backgrid.Extension.ColumnManager.prototype.getStorage = function () { + // Check if storage functionality is available + if (typeof Storage !== "undefined") { + return (this.options.saveStateLocation === "sessionStorage") ? sessionStorage : localStorage; + } + else { + console.error("ColMrg: No storage support detected. State won't be saved."); + return false; + } + }; + + /** + * @method getStorageKey + * @return {string} + * @private + */ + Backgrid.Extension.ColumnManager.prototype.getStorageKey = function () { + return (this.options.saveStateKey) ? "backgrid-colmgr-" + this.options.saveStateKey : "backgrid-colmgr"; + }; + + /** + * @method stateUpdateHandler + * @return {boolean} + * @private + */ + Backgrid.Extension.ColumnManager.prototype.stateUpdateHandler = function () { + var state = this.getStateFromColumns(); + return this.setState(state); + }; + + /** + * @method getStateFromColumn + * @return {Array} + */ + Backgrid.Extension.ColumnManager.prototype.getStateFromColumns = function() { + var self = this; + + // Map state from columns + return this.columns.map(function(column) { + var columnState = { + name: column.get("name") + }; + + if (self.options.trackVisibility) { + columnState.renderable = column.get("renderable"); + } + if (self.options.trackOrder) { + columnState.displayOrder = column.get("displayOrder"); + } + if (self.options.trackSize) { + columnState.width = column.get("width"); + } + return columnState; + }); + }; + + /** + * @method applyStateToColumns + * @private + */ + Backgrid.Extension.ColumnManager.prototype.applyStateToColumns = function () { + var self = this; + + // Loop state + var ordered = false; + _.each(this.state, function(columnState) { + // Find column + var column = self.columns.findWhere({ + name: columnState.name + }); + + if (_.has(columnState, "renderable")) { + column.set("renderable", columnState.renderable); + } + if (_.has(columnState, "width")) { + var oldWidth = column.get("width"); + column.set("width", columnState.width, {silent: true}); + if (oldWidth !== columnState.width) { + column.trigger("resize", column, columnState.width, oldWidth); + } + } + + if (_.has(columnState, "displayOrder")) { + if (columnState.displayOrder !== column.get("displayOrder")) { + ordered = true; + } + column.set("displayOrder", columnState.displayOrder, {silent: true}); + } + }); + + if (ordered) { + self.columns.sort(); + self.columns.trigger("ordered"); + } + }; + + ////////////////////////////////////////////// + /////////////// UI Controls ////////////////// + ////////////////////////////////////////////// + + /** + * A dropdown item view + * + * @class DropDownItemView + * @extends Backbone.View + */ + var DropDownItemView = Backbone.View.extend({ + className: "columnmanager-dropdown-item", + tagName: "li", + + /** + * @method initialize + * @param {object} opts + * @param {Backgrid.Extension.ColumnManager} opts.columnManager ColumnManager instance. + * @param {Backgrid.Column} opts.column A backgrid column. + */ + initialize: function (opts) { + this.columnManager = opts.columnManager; + this.column = opts.column; + this.template = opts.template; + + _.bindAll(this, "render", "toggleVisibility"); + this.column.on("change:renderable", this.render, this); + this.el.addEventListener("click", this.toggleVisibility, true); + }, + + /** + * @method render + * @return {DropDownItemView} + */ + render: function () { + this.$el.empty(); + + this.$el.append(this.template({ + label: this.column.get("label") + })); + + if (this.column.get("renderable")) { + this.$el.addClass((this.column.get("renderable")) ? "visible" : null); + } + else { + this.$el.removeClass("visible"); + } + + return this; + }, + + /** + * Toggles visibility of column. + * + * @method toggleVisibility + * @param {object} e + */ + toggleVisibility: function (e) { + if (e) { + this.stopPropagation(e); + } + this.columnManager.toggleColumnVisibility(this.column); + }, + + /** + * Convenience function to stop event propagation. + * + * @method stopPropagation + * @param {object} e + * @private + */ + stopPropagation: function (e) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + } + }); + + + /** + * Dropdown view container. + * + * @class DropDownView + * @extends Backbone.view + */ + var DropDownView = Backbone.View.extend({ + /** + * @property className + * @type String + * @default "columnmanager-dropdown-container" + */ + className: "columnmanager-dropdown-container", + + /** + * @method initialize + * @param {object} opts + * @param {Backgrid.Extension.ColumnManager} opts.columnManager ColumnManager instance. + * @param {Backbone.View} opts.DropdownItemView View to be used for the items. + * @param {Function} opts.dropdownItemTemplate + */ + initialize: function (opts) { + this.options = opts; + this.columnManager = opts.columnManager; + this.ItemView = (opts.DropdownItemView instanceof Backbone.View) ? opts.DropdownItemView : DropDownItemView; + this.$dropdownButton = opts.$dropdownButton; + + this.on("dropdown:opened", this.open, this); + this.on("dropdown:closed", this.close, this); + this.columnManager.columns.on("add remove", this.render, this); + }, + + /** + * @method render + * @return {DropDownView} + */ + render: function () { + var view = this; + view.$el.empty(); + + // List all columns + this.columnManager.columns.each(function (col) { + if (!col.get("alwaysVisible")) { + view.$el.append(new view.ItemView({ + column: col, + columnManager: view.columnManager, + template: view.options.dropdownItemTemplate + }).render().el); + } + }); + + return this; + }, + + /** + * Opens the dropdown. + * + * @method open + */ + open: function () { + this.$el.addClass("open"); + + // Get button + var $button = this.$dropdownButton; + + // Align + var align; + if (this.options.align === "auto") { + // Determine what alignment fits + var viewPortWidth = document.body.clientWidth || document.body.clientWidth; + align = (($button.offset().left + this.$el.outerWidth()) > viewPortWidth) ? "left" : "right"; + } + else { + align = (this.options.align === "left" || this.options.align === "right") ? + (this.options.align === "right" ? "right" : "left") : "right"; + } + + var offset; + if (align === "left") { + // Align right by default + offset = $button.offset().left + $button.outerWidth() - this.$el.outerWidth(); + this.$el.css("left", offset + "px"); + } + else { + offset = $button.offset().left; + this.$el.css("left", offset + "px"); + } + + // Height position + var offsetHeight = $button.offset().top + $button.outerHeight(); + this.$el.css("top", offsetHeight + "px"); + }, + + /** + * Closes the dropdown. + * + * @method close + */ + close: function () { + this.$el.removeClass("open"); + } + }); + + /** + * UI control which manages visibility of columns. + * Inspired by: https://github.com/kjantzer/backbonejs-dropdown-view. + * + * @class Backgrid.Extension.ColumnManagerVisibilityControl + * @extends Backbone.View + */ + Backgrid.Extension.ColumnManagerVisibilityControl = Backbone.View.extend({ + /** + * @property tagName + * @type String + * @default "div" + */ + tagName: "div", + + /** + * @property className + * @type String + * @default "columnmanager-visibilitycontrol" + */ + className: "columnmanager-visibilitycontrol", + + /** + * @property defaultEvents + * @type Object + */ + defaultEvents: { + "click": "stopPropagation" + }, + + /** + * @property defaultOpts + * @type Object + */ + defaultOpts: { + width: null, + closeOnEsc: true, + closeOnClick: true, + openOnInit: false, + columnManager: null, + + // Button + buttonTemplate: _.template("<button class='dropdown-button'>...</button>"), + + // Container + DropdownView: DropDownView, + dropdownAlign: "auto", + + // Item view + DropdownItemView: DropDownItemView, + dropdownItemTemplate: _.template("<span class='indicator'></span><span class='column-label'><%= label %></span>") + }, + + /** + * @method initialize + * @param {Object} opts + * @param {Backgrid.Extension.ColumnManager} opts.columnManager ColumnManager instance + */ + initialize: function (opts) { + this.options = _.extend({}, this.defaultOpts, opts); + this.events = _.extend({}, this.defaultEvents, this.events || {}); + this.columnManager = opts.columnManager; + + // Option checking + if (!this.columnManager instanceof Backgrid.Extension.ColumnManager) { + console.error("Backgrid.ColumnManager: options.columns is not an instance of Backgrid.Columns"); + } + + // Bind scope to events + _.bindAll(this, "deferClose", "stopDeferClose", "closeOnEsc", "toggle", "render"); + + // UI events + document.body.addEventListener("click", this.deferClose, true); + this.el.addEventListener("click", this.stopDeferClose, true); + if (this.options.closeOnEsc) { + document.body.addEventListener("keyup", this.closeOnEsc, false); + } + this.el.addEventListener("click", this.toggle, false); + + // Create elements + this.setup(); + + // Listen for dropdown view events indicating to open and/or close + this.view.on("dropdown:close", this.close, this); + this.view.on("dropdown:open", this.open, this); + }, + + /** + * @method delayStart + * @private + */ + delayStart: function () { + clearTimeout(this.closeTimeout); + this.delayTimeout = setTimeout(this.open.bind(this), this.options.delay); + }, + + /** + * @method delayEnd + * @private + */ + delayEnd: function () { + clearTimeout(this.delayTimeout); + this.closeTimeout = setTimeout(this.close.bind(this), 300); + }, + + /** + * @method setup + * @private + */ + setup: function () { + // Override element width + if (this.options.width) { + this.$el.width(this.options.width + "px"); + } + + // Create button element + this.$dropdownButton = $(this.options.buttonTemplate()); + + var viewOptions = { + columnManager: this.columnManager, + DropdownItemView: this.options.DropdownItemView, + dropdownItemTemplate: this.options.dropdownItemTemplate, + align: this.options.dropdownAlign, + $dropdownButton: this.$dropdownButton + }; + + // Check if a different childView has been provided, if not, use default dropdown view + this.view = (this.options.DropdownView instanceof Backbone.View) ? + new this.options.DropdownView(viewOptions) : + new DropDownView(viewOptions); + }, + + /** + * @method setup + */ + render: function () { + this.$el.empty(); + + // Render button + this.$el.append(this.$dropdownButton); + + // Render inner view + this.view.render(); // tell the inner view to render itself + $(document.body).append(this.view.el); + return this; + }, + + /** + * Convenience function to stop event propagation + * + * @method stopPropagation + * @param {object} e + * @private + */ + stopPropagation: function (e) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + }, + + /** + * Toggle the dropdown visibility + * + * @method toggle + * @param {object} [e] + */ + toggle: function (e) { + if (this.isOpen !== true) { + this.open(e); + } + else { + this.close(e); + } + }, + + /** + * Open the dropdown + * + * @method open + * @param {object} [e] + */ + open: function (e) { + clearTimeout(this.closeTimeout); + clearTimeout(this.deferCloseTimeout); + + if (e) { + if (e.stopPropagation) { + e.stopPropagation(); + } + if (e.preventDefault) { + e.preventDefault(); + } + e.cancelBubble = true; + } + + // Don't do anything if already open + if (this.isOpen) { + return; + } + + this.isOpen = true; + this.$el.addClass("open"); + this.trigger("dropdown:opened"); + + // Notify child view + this.view.trigger("dropdown:opened"); + }, + + /** + * Close the dropdown + * + * @method close + * @param {object} [e] + */ + close: function (e) { + // Don't do anything if already closed + if (!this.isOpen) { + return; + } + + this.isOpen = false; + this.$el.removeClass("open"); + this.trigger("dropdown:closed"); + + // Notify child view + this.view.trigger("dropdown:closed"); + }, + + /** + * Close the dropdown on esc + * + * @method closeOnEsc + * @param {object} e + * @private + */ + closeOnEsc: function (e) { + if (e.which === 27) { + this.deferClose(); + } + }, + + /** + * @method deferClose + * @private + */ + deferClose: function () { + this.deferCloseTimeout = setTimeout(this.close.bind(this), 0); + }, + + /** + * @method stopDeferClose + * @private + */ + stopDeferClose: function (e) { + clearTimeout(this.deferCloseTimeout); + }, + + /** + * Clean up this control + * + * @method remove + * @chainable + */ + remove: function () { + // Remove event listeners + document.body.removeEventListener("click", this.deferClose); + this.el.removeEventListener("click", this.stopDeferClose); + if (this.options.closeOnEsc) { + document.body.removeEventListener("keyup", this.closeOnEsc); + } + this.el.removeEventListener("click", this.toggle); + + // Remove DOM element + $(this.view.el).remove(); + + // Invoke original backbone methods + return Backbone.View.prototype.remove.apply(this, arguments); + } + }); + + /** + * Backgrid HeaderCell containing ColumnManagerVisibilityControl + * + * @class Backgrid.Extension.ColumnVisibilityHeaderCell + * @extends Backgrid.HeaderCell + */ + + Backgrid.Extension.ColumnManager.ColumnVisibilityHeaderCell = Backgrid.HeaderCell.extend({ + initialize: function (options) { + Backgrid.HeaderCell.prototype.initialize.apply(this, arguments); + + // Add class + this.$el.addClass(this.column.get("name")); + }, + render: function () { + this.$el.empty(); + + // Add control + var colVisibilityControl = this.colVisibilityControl = new Backgrid.Extension.ColumnManagerVisibilityControl({ + columnManager: this.columnManager + }); + + // Add to header + this.$el.html(colVisibilityControl.render().el); + + this.delegateEvents(); + return this; + }, + + /** + * Clean up this cell. + * + * @method remove + * @chainable + */ + remove: function () { + // Remove UI control + this.colVisibilityControl.remove(); + + // Invoke super + /*eslint no-underscore-dangle:0*/ + return Backgrid.HeaderCell.__super__.remove.apply(this, arguments); + } + }); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __WEBPACK_EXTERNAL_MODULE_1__; + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __WEBPACK_EXTERNAL_MODULE_2__; + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __WEBPACK_EXTERNAL_MODULE_3__; + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __WEBPACK_EXTERNAL_MODULE_4__; + +/***/ } +/******/ ]) +}); +; \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.min.js ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.min.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.min.js new file mode 100644 index 0000000..3312282 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-columnmanager/Backgrid.ColumnManager.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("Backbone"),require("Backgrid"),require("_"),require("jQuery")):"function"==typeof define&&define.amd?define(["Backbone","Backgrid","_","jQuery"],t):"object"==typeof exports?exports["Backgrid.Extension.ColumnManager"]=t(require("Backbone"),require("Backgrid"),require("_"),require("jQuery")):e["Backgrid.Extension.ColumnManager"]=t(e.Backbone,e.Backgrid,e._,e.jQuery)}(this,function(e,t,n,i){return function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={exports:{},id:i,loaded:!1};return e[i].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";var i=n(3),o=n(4),s=n(1),r=n(2);r.Extension.ColumnManager=function(e,t,n){i.extend(this,s.Events);var o={initialColumnsVisible:null,trackSize:!0,trackOrder:!0,trackVisibility:!0,stateChecking:"strict",saveState:!1,saveStateKey:"",saveStateLocation:"localStorage",loadStateOnInit:! 1};if(this.options=i.extend({},o,t),this.state=[],e instanceof r.Columns){this.columns=e,e.columnManager=this,this.addManagerToColumns();var a=this.options.loadStateOnInit?this.loadState():!1;if(n&&this.checkStateValidity(n)?this.setState(n,!0):a?this.setState(a,!0):(this.setInitialColumnVisibility(),this.setState(this.getStateFromColumns())),this.options.trackVisibility||this.options.trackSize||this.options.trackOrder){var l=""+(this.options.trackVisibility?"change:renderable ":"")+(this.options.trackSize?"resize ":"")+(this.options.trackOrder?"ordered":"");this.columns.on(l,i.bind(this.stateUpdateHandler,this))}}else console.error("Backgrid.ColumnManager: options.columns is not an instance of Backgrid.Columns")},r.Extension.ColumnManager.prototype.setInitialColumnVisibility=function(){var e=this,t=e.options.initialColumnsVisible;t&&e.columns.each(function(e,n){e.set("renderable",e.get("alwaysVisible")?!0:t>n)})},r.Extension.ColumnManager.prototype.addManagerToColumns=function(){va r e=this;e.columns.each(function(t){t.get("headerCell")===r.Extension.ColumnManager.ColumnVisibilityHeaderCell&&t.set("headerCell",t.get("headerCell").extend({columnManager:e})),t.get("headerCell")instanceof r.Extension.ColumnManager.ColumnVisibilityHeaderCell&&(t.get("headerCell").columnManager=e)})},r.Extension.ColumnManager.prototype.getColumn=function(e){return(i.isNumber(e)||i.isString(e))&&(e=this.columns.get(e)),e instanceof r.Column?e:!1},r.Extension.ColumnManager.prototype.hideColumn=function(e){var t=this.getColumn(e);t&&t.set("renderable",!1)},r.Extension.ColumnManager.prototype.showColumn=function(e){var t=this.getColumn(e);t&&t.set("renderable",!0)},r.Extension.ColumnManager.prototype.toggleColumnVisibility=function(e){var t=this.getColumn(e);t&&(t.get("renderable")?this.hideColumn(t):this.showColumn(t))},r.Extension.ColumnManager.prototype.getColumnCollection=function(){return this.columns},r.Extension.ColumnManager.prototype.setState=function(e,t){var n=this;return i. filter(e,function(t){if(!i.has(t,"name"))return!1;var o=n.columns.findWhere({name:e.name});return"undefined"!=typeof o}),n.checkStateValidity(e)&&e!==n.state?(n.state=e,n.trigger("state-changed",e),t?n.applyStateToColumns():n.saveState()):!1},r.Extension.ColumnManager.prototype.getState=function(){return this.state},r.Extension.ColumnManager.prototype.checkStateValidity=function(e){function t(){return i.every(e,function(e){var t=!0;return i.has(e,"name")||(t=!1),i.has(e,"renderable")&&(i.isBoolean(e.renderable)||(t=!1)),i.has(e,"displayOrder")&&(i.isNumber(e.displayOrder)||(t=!1)),i.has(e,"width")&&(i.isNumber(e.width)||i.isString(e.width)||(t=!1)),t})}if(!i.isArray(e)&&i.isEmpty(e))return!1;if("loose"===this.options.stateChecking)return t();if(e.length!==this.columns.length&&!t())return!1;var n=this.columns.map(function(e){return e.get("name")}),o=i.map(e,function(e){return e.name});return n.sort().toString()===o.sort().toString()},r.Extension.ColumnManager.prototype.loadState=func tion(){var e=JSON.parse(this.getStorage().getItem(this.getStorageKey()));return this.checkStateValidity(e)?e:!1},r.Extension.ColumnManager.prototype.saveState=function(e){return this.options.saveState||e?(this.getStorage().setItem(this.getStorageKey(),JSON.stringify(this.state)),this.trigger("state-saved"),!0):!1},r.Extension.ColumnManager.prototype.getStorage=function(){return"undefined"!=typeof Storage?"sessionStorage"===this.options.saveStateLocation?sessionStorage:localStorage:(console.error("ColMrg: No storage support detected. State won't be saved."),!1)},r.Extension.ColumnManager.prototype.getStorageKey=function(){return this.options.saveStateKey?"backgrid-colmgr-"+this.options.saveStateKey:"backgrid-colmgr"},r.Extension.ColumnManager.prototype.stateUpdateHandler=function(){var e=this.getStateFromColumns();return this.setState(e)},r.Extension.ColumnManager.prototype.getStateFromColumns=function(){var e=this;return this.columns.map(function(t){var n={name:t.get("name")};return e.options.trackVisibility&&(n.renderable=t.get("renderable")),e.options.trackOrder&&(n.displayOrder=t.get("displayOrder")),e.options.trackSize&&(n.width=t.get("width")),n})},r.Extension.ColumnManager.prototype.applyStateToColumns=function(){var e=this,t=!1;i.each(this.state,function(n){var o=e.columns.findWhere({name:n.name});if(i.has(n,"renderable")&&o.set("renderable",n.renderable),i.has(n,"width")){var s=o.get("width");o.set("width",n.width,{silent:!0}),s!==n.width&&o.trigger("resize",o,n.width,s)}i.has(n,"displayOrder")&&(n.displayOrder!==o.get("displayOrder")&&(t=!0),o.set("displayOrder",n.displayOrder,{silent:!0}))}),t&&(e.columns.sort(),e.columns.trigger("ordered"))};var a=s.View.extend({className:"columnmanager-dropdown-item",tagName:"li",initialize:function(e){this.columnManager=e.columnManager,this.column=e.column,this.template=e.template,i.bindAll(this,"render","toggleVisibility"),this.column.on("change:renderable",this.render,this),this.el.addEventListener("click",this. toggleVisibility,!0)},render:function(){return this.$el.empty(),this.$el.append(this.template({label:this.column.get("label")})),this.column.get("renderable")?this.$el.addClass(this.column.get("renderable")?"visible":null):this.$el.removeClass("visible"),this},toggleVisibility:function(e){e&&this.stopPropagation(e),this.columnManager.toggleColumnVisibility(this.column)},stopPropagation:function(e){e.stopPropagation(),e.stopImmediatePropagation(),e.preventDefault()}}),l=s.View.extend({className:"columnmanager-dropdown-container",initialize:function(e){this.options=e,this.columnManager=e.columnManager,this.ItemView=e.DropdownItemView instanceof s.View?e.DropdownItemView:a,this.$dropdownButton=e.$dropdownButton,this.on("dropdown:opened",this.open,this),this.on("dropdown:closed",this.close,this),this.columnManager.columns.on("add remove",this.render,this)},render:function(){var e=this;return e.$el.empty(),this.columnManager.columns.each(function(t){t.get("alwaysVisible")||e.$el.append(n ew e.ItemView({column:t,columnManager:e.columnManager,template:e.options.dropdownItemTemplate}).render().el)}),this},open:function(){this.$el.addClass("open");var e,t=this.$dropdownButton;if("auto"===this.options.align){var n=document.body.clientWidth||document.body.clientWidth;e=t.offset().left+this.$el.outerWidth()>n?"left":"right"}else e="left"===this.options.align||"right"===this.options.align?"right"===this.options.align?"right":"left":"right";var i;"left"===e?(i=t.offset().left+t.outerWidth()-this.$el.outerWidth(),this.$el.css("left",i+"px")):(i=t.offset().left,this.$el.css("left",i+"px"));var o=t.offset().top+t.outerHeight();this.$el.css("top",o+"px")},close:function(){this.$el.removeClass("open")}});r.Extension.ColumnManagerVisibilityControl=s.View.extend({tagName:"div",className:"columnmanager-visibilitycontrol",defaultEvents:{click:"stopPropagation"},defaultOpts:{width:null,closeOnEsc:!0,closeOnClick:!0,openOnInit:!1,columnManager:null,buttonTemplate:i.template("<button cl ass='dropdown-button'>...</button>"),DropdownView:l,dropdownAlign:"auto",DropdownItemView:a,dropdownItemTemplate:i.template("<span class='indicator'></span><span class='column-label'><%= label %></span>")},initialize:function(e){this.options=i.extend({},this.defaultOpts,e),this.events=i.extend({},this.defaultEvents,this.events||{}),this.columnManager=e.columnManager,!this.columnManager instanceof r.Extension.ColumnManager&&console.error("Backgrid.ColumnManager: options.columns is not an instance of Backgrid.Columns"),i.bindAll(this,"deferClose","stopDeferClose","closeOnEsc","toggle","render"),document.body.addEventListener("click",this.deferClose,!0),this.el.addEventListener("click",this.stopDeferClose,!0),this.options.closeOnEsc&&document.body.addEventListener("keyup",this.closeOnEsc,!1),this.el.addEventListener("click",this.toggle,!1),this.setup(),this.view.on("dropdown:close",this.close,this),this.view.on("dropdown:open",this.open,this)},delayStart:function(){clearTimeout(this.cl oseTimeout),this.delayTimeout=setTimeout(this.open.bind(this),this.options.delay)},delayEnd:function(){clearTimeout(this.delayTimeout),this.closeTimeout=setTimeout(this.close.bind(this),300)},setup:function(){this.options.width&&this.$el.width(this.options.width+"px"),this.$dropdownButton=o(this.options.buttonTemplate());var e={columnManager:this.columnManager,DropdownItemView:this.options.DropdownItemView,dropdownItemTemplate:this.options.dropdownItemTemplate,align:this.options.dropdownAlign,$dropdownButton:this.$dropdownButton};this.view=this.options.DropdownView instanceof s.View?new this.options.DropdownView(e):new l(e)},render:function(){return this.$el.empty(),this.$el.append(this.$dropdownButton),this.view.render(),o(document.body).append(this.view.el),this},stopPropagation:function(e){e.stopPropagation(),e.stopImmediatePropagation(),e.preventDefault()},toggle:function(e){this.isOpen!==!0?this.open(e):this.close(e)},open:function(e){clearTimeout(this.closeTimeout),clearTimeou t(this.deferCloseTimeout),e&&(e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault(),e.cancelBubble=!0),this.isOpen||(this.isOpen=!0,this.$el.addClass("open"),this.trigger("dropdown:opened"),this.view.trigger("dropdown:opened"))},close:function(e){this.isOpen&&(this.isOpen=!1,this.$el.removeClass("open"),this.trigger("dropdown:closed"),this.view.trigger("dropdown:closed"))},closeOnEsc:function(e){27===e.which&&this.deferClose()},deferClose:function(){this.deferCloseTimeout=setTimeout(this.close.bind(this),0)},stopDeferClose:function(e){clearTimeout(this.deferCloseTimeout)},remove:function(){return document.body.removeEventListener("click",this.deferClose),this.el.removeEventListener("click",this.stopDeferClose),this.options.closeOnEsc&&document.body.removeEventListener("keyup",this.closeOnEsc),this.el.removeEventListener("click",this.toggle),o(this.view.el).remove(),s.View.prototype.remove.apply(this,arguments)}}),r.Extension.ColumnManager.ColumnVisibilityHeader Cell=r.HeaderCell.extend({initialize:function(e){r.HeaderCell.prototype.initialize.apply(this,arguments),this.$el.addClass(this.column.get("name"))},render:function(){this.$el.empty();var e=this.colVisibilityControl=new r.Extension.ColumnManagerVisibilityControl({columnManager:this.columnManager});return this.$el.html(e.render().el),this.delegateEvents(),this},remove:function(){return this.colVisibilityControl.remove(),r.HeaderCell.__super__.remove.apply(this,arguments)}})},function(t,n,i){t.exports=e},function(e,n,i){e.exports=t},function(e,t,i){e.exports=n},function(e,t,n){e.exports=i}])}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/css/backgrid-filter.css ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/css/backgrid-filter.css b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/css/backgrid-filter.css new file mode 100644 index 0000000..4c689d0 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/css/backgrid-filter.css @@ -0,0 +1,193 @@ +/* + backgrid-filter + http://github.com/wyuenho/backgrid + + Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors + Licensed under the MIT @license. +*/ + +/* + Search Icon CSS derived from: + + PURE CSS GUI ICONS + by Nicolas Gallagher + - http://nicolasgallagher.com/pure-css-gui-icons/ + + http://nicolasgallagher.com + http://twitter.com/necolas + + Created: 29 July 2010 + Version: 1.0.1 + + Dual licensed under MIT and GNU GPLv2 (c) Nicolas Gallagher +*/ + +.backgrid-filter.form-search { + position: relative; + width: 248px; + height: 30px; + margin: 20px; +} + +/* + Search Icon +*/ + +.backgrid-filter .search { + position: absolute; + top: 50%; + left: 6px; + z-index: 1000; + width: 10px; + height: 20px; + margin-top: -10px; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.backgrid-filter .search:before { + position: absolute; + top: 50%; + left: 0; + width: 6px; + height: 6px; + margin-top: -6px; + background: transparent; + border: 3px solid gray; + -webkit-border-radius: 12px; + -moz-border-radius: 12px; + border-radius: 12px; + content: ""; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.backgrid-filter .search:after { + position: absolute; + top: 50%; + left: 10px; + width: 3px; + height: 7px; + margin-top: 2px; + background-color: gray; + content: ""; + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +/* + Clear button + */ + +.backgrid-filter .clear { + position: absolute; + top: 50%; + right: 8px; + z-index: 1000; + width: 10px; + height: 20px; + margin-top: -10px; + font-family: sans-serif; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: gray; + text-decoration: none; +} + +.backgrid-filter input[type="search"] { + position: absolute; + display: inline-block; + width: 206px; + height: 20px; + padding: 4px 6px; + font-weight: normal; + color: #555; + vertical-align: middle; + background-color: #fff; + border: 1px solid #ccc; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +/* + Normalize the search input box, with code borrowed from normalize.css. + + https://github.com/necolas/normalize.css/ + + Copyright (c) Nicolas Gallagher and Jonathan Neal, MIT @license. + */ + +/* + * 1. Correct font family not being inherited in all browsers. + * 2. Correct font size not being inherited in all browsers. + * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. + * 4. Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. + */ + +.backgrid-filter input { + margin: 0; + font-family: inherit; + font-size: 100%; + line-height: normal; +} + +/* + * Re-set default cursor for disabled elements. + */ + +.backgrid-filter input[disabled] { + cursor: default; +} + +/* + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +.backgrid-filter input[type="search"] { + outline: none; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: none; +} + +/* + * Remove the default clear button on IE + */ + +.backgrid-filter input[type="search"]::-ms-clear { + display: none; +} + +/* + * Remove inner padding and border in Firefox 4+. + */ + +.backgrid-filter input::-moz-focus-inner { + padding: 0; + border: 0; +} + +.backgrid-filter input[type="search"] { + padding-right: 18px; + padding-left: 22px; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.js ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.js new file mode 100644 index 0000000..66b1c21 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.js @@ -0,0 +1,487 @@ +/* + backgrid-filter + http://github.com/wyuenho/backgrid + + Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors + Licensed under the MIT @license. +*/ +(function (root, factory) { + + // CommonJS + if (typeof exports == "object") { + (function () { + var lunr; + try { lunr = require("lunr"); } catch (e) {} + module.exports = factory(require("underscore"), + require("backbone"), + require("backgrid"), + lunr); + }()); + } + // Browser + else { + factory(root._, root.Backbone, root.Backgrid, root.lunr); + } + +}(this, function (_, Backbone, Backgrid, lunr) { + + "use strict"; + + /** + ServerSideFilter is a search form widget that submits a query to the server + for filtering the current collection. + + @class Backgrid.Extension.ServerSideFilter + */ + var ServerSideFilter = Backgrid.Extension.ServerSideFilter = Backbone.View.extend({ + + /** @property */ + tagName: "form", + + /** @property */ + className: "backgrid-filter form-search", + + /** @property {function(Object, ?Object=): string} template */ + template: _.template('<span class="search"> </span><input type="search" <% if (placeholder) { %> placeholder="<%- placeholder %>" <% } %> name="<%- name %>" /><a class="clear" data-backgrid-action="clear" href="#">×</a>', null, {variable: null}), + + /** @property */ + events: { + "keyup input[type=search]": "showClearButtonMaybe", + "click a[data-backgrid-action=clear]": "clear", + "submit": "search" + }, + + /** @property {string} [name='q'] Query key */ + name: "q", + + /** + @property {string} [placeholder] The HTML5 placeholder to appear beneath + the search box. + */ + placeholder: null, + + /** + @param {Object} options + @param {Backbone.Collection} options.collection + @param {string} [options.name] + @param {string} [options.placeholder] + @param {function(Object): string} [options.template] + */ + initialize: function (options) { + ServerSideFilter.__super__.initialize.apply(this, arguments); + this.name = options.name || this.name; + this.placeholder = options.placeholder || this.placeholder; + this.template = options.template || this.template; + + // Persist the query on pagination + var collection = this.collection, self = this; + if (Backbone.PageableCollection && + collection instanceof Backbone.PageableCollection && + collection.mode == "server") { + collection.queryParams[this.name] = function () { + return self.searchBox().val() || null; + }; + } + }, + + /** + Event handler. Show the clear button when the search box has text, hide + it otherwise. + */ + showClearButtonMaybe: function () { + var $clearButton = this.clearButton(); + var searchTerms = this.searchBox().val(); + if (searchTerms) $clearButton.show(); + else $clearButton.hide(); + }, + + /** + Returns the search input box. + */ + searchBox: function () { + return this.$el.find("input[type=search]"); + }, + + /** + Returns the clear button. + */ + clearButton: function () { + return this.$el.find("a[data-backgrid-action=clear]"); + }, + + /** + Upon search form submission, this event handler constructs a query + parameter object and pass it to Collection#fetch for server-side + filtering. + + If the collection is a PageableCollection, searching will go back to the + first page. + */ + search: function (e) { + if (e) e.preventDefault(); + + var data = {}; + var query = this.searchBox().val(); + if (query) data[this.name] = query; + + var collection = this.collection; + + // go back to the first page on search + if (Backbone.PageableCollection && + collection instanceof Backbone.PageableCollection) { + collection.getFirstPage({data: data, reset: true, fetch: true}); + } + else collection.fetch({data: data, reset: true}); + }, + + /** + Event handler for the clear button. Clears the search box and refetch the + collection. + + If the collection is a PageableCollection, clearing will go back to the + first page. + */ + clear: function (e) { + if (e) e.preventDefault(); + this.searchBox().val(null); + this.showClearButtonMaybe(); + + var collection = this.collection; + + // go back to the first page on clear + if (Backbone.PageableCollection && + collection instanceof Backbone.PageableCollection) { + collection.getFirstPage({reset: true, fetch: true}); + } + else collection.fetch({reset: true}); + }, + + /** + Renders a search form with a text box, optionally with a placeholder and + a preset value if supplied during initialization. + */ + render: function () { + this.$el.empty().append(this.template({ + name: this.name, + placeholder: this.placeholder, + value: this.value + })); + this.showClearButtonMaybe(); + this.delegateEvents(); + return this; + } + + }); + + /** + ClientSideFilter is a search form widget that searches a collection for + model matches against a query on the client side. The exact matching + algorithm can be overriden by subclasses. + + @class Backgrid.Extension.ClientSideFilter + @extends Backgrid.Extension.ServerSideFilter + */ + var ClientSideFilter = Backgrid.Extension.ClientSideFilter = ServerSideFilter.extend({ + + /** @property */ + events: _.extend({}, ServerSideFilter.prototype.events, { + "click a[data-backgrid-action=clear]": function (e) { + e.preventDefault(); + this.clear(); + }, + "keydown input[type=search]": "search", + "submit": function (e) { + e.preventDefault(); + this.search(); + } + }), + + /** + @property {?Array.<string>} [fields] A list of model field names to + search for matches. If null, all of the fields will be searched. + */ + fields: null, + + /** + @property [wait=149] The time in milliseconds to wait since the last + change to the search box's value before searching. This value can be + adjusted depending on how often the search box is used and how large the + search index is. + */ + wait: 149, + + /** + Debounces the #search and #clear methods and makes a copy of the given + collection for searching. + + @param {Object} options + @param {Backbone.Collection} options.collection + @param {string} [options.placeholder] + @param {string} [options.fields] + @param {string} [options.wait=149] + */ + initialize: function (options) { + ClientSideFilter.__super__.initialize.apply(this, arguments); + + this.fields = options.fields || this.fields; + this.wait = options.wait || this.wait; + + this._debounceMethods(["search", "clear"]); + + var collection = this.collection = this.collection.fullCollection || this.collection; + var shadowCollection = this.shadowCollection = collection.clone(); + + this.listenTo(collection, "add", function (model, collection, options) { + shadowCollection.add(model, options); + }); + this.listenTo(collection, "remove", function (model, collection, options) { + shadowCollection.remove(model, options); + }); + this.listenTo(collection, "sort", function (col) { + if (!this.searchBox().val()) shadowCollection.reset(col.models); + }); + this.listenTo(collection, "reset", function (col, options) { + options = _.extend({reindex: true}, options || {}); + if (options.reindex && options.from == null && options.to == null) { + shadowCollection.reset(col.models); + } + }); + }, + + _debounceMethods: function (methodNames) { + if (_.isString(methodNames)) methodNames = [methodNames]; + + this.undelegateEvents(); + + for (var i = 0, l = methodNames.length; i < l; i++) { + var methodName = methodNames[i]; + var method = this[methodName]; + this[methodName] = _.debounce(method, this.wait); + } + + this.delegateEvents(); + }, + + /** + Constructs a Javascript regular expression object for #makeMatcher. + + This default implementation takes a query string and returns a Javascript + RegExp object that matches any of the words contained in the query string + case-insensitively. Override this method to return a different regular + expression matcher if this behavior is not desired. + + @param {string} query The search query in the search box. + @return {RegExp} A RegExp object to match against model #fields. + */ + makeRegExp: function (query) { + return new RegExp(query.trim().split(/\s+/).join("|"), "i"); + }, + + /** + This default implementation takes a query string and returns a matcher + function that looks for matches in the model's #fields or all of its + fields if #fields is null, for any of the words in the query + case-insensitively using the regular expression object returned from + #makeRegExp. + + Most of time, you'd want to override the regular expression used for + matching. If so, please refer to the #makeRegExp documentation, + otherwise, you can override this method to return a custom matching + function. + + Subclasses overriding this method must take care to conform to the + signature of the matcher function. The matcher function is a function + that takes a model as paramter and returns true if the model matches a + search, or false otherwise. + + In addition, when the matcher function is called, its context will be + bound to this ClientSideFilter object so it has access to the filter's + attributes and methods. + + @param {string} query The search query in the search box. + @return {function(Backbone.Model):boolean} A matching function. + */ + makeMatcher: function (query) { + var regexp = this.makeRegExp(query); + return function (model) { + var keys = this.fields || model.keys(); + for (var i = 0, l = keys.length; i < l; i++) { + if (regexp.test(model.get(keys[i]) + "")) return true; + } + return false; + }; + }, + + /** + Takes the query from the search box, constructs a matcher with it and + loops through collection looking for matches. Reset the given collection + when all the matches have been found. + + If the collection is a PageableCollection, searching will go back to the + first page. + */ + search: function () { + var matcher = _.bind(this.makeMatcher(this.searchBox().val()), this); + var col = this.collection; + if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true}); + col.reset(this.shadowCollection.filter(matcher), {reindex: false}); + }, + + /** + Clears the search box and reset the collection to its original. + + If the collection is a PageableCollection, clearing will go back to the + first page. + */ + clear: function () { + this.searchBox().val(null); + this.showClearButtonMaybe(); + var col = this.collection; + if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true}); + col.reset(this.shadowCollection.models, {reindex: false}); + } + + }); + + /** + LunrFilter is a ClientSideFilter that uses [lunrjs](http://lunrjs.com/) to + index the text fields of each model for a collection, and performs + full-text searching. + + @class Backgrid.Extension.LunrFilter + @extends Backgrid.Extension.ClientSideFilter + */ + var LunrFilter = Backgrid.Extension.LunrFilter = ClientSideFilter.extend({ + + /** + @property {string} [ref="id"]ï½lunrjs` document reference attribute name. + */ + ref: "id", + + /** + @property {Object} fields A hash of `lunrjs` index field names and boost + value. Unlike ClientSideFilter#fields, LunrFilter#fields is _required_ to + initialize the index. + */ + fields: null, + + /** + Indexes the underlying collection on construction. The index will refresh + when the underlying collection is reset. If any model is added, removed + or if any indexed fields of any models has changed, the index will be + updated. + + @param {Object} options + @param {Backbone.Collection} options.collection + @param {string} [options.placeholder] + @param {string} [options.ref] ï½lunrjs` document reference attribute name. + @param {Object} [options.fields] A hash of `lunrjs` index field names and + boost value. + @param {number} [options.wait] + */ + initialize: function (options) { + LunrFilter.__super__.initialize.apply(this, arguments); + + this.ref = options.ref || this.ref; + + var collection = this.collection = this.collection.fullCollection || this.collection; + this.listenTo(collection, "add", this.addToIndex); + this.listenTo(collection, "remove", this.removeFromIndex); + this.listenTo(collection, "reset", this.resetIndex); + this.listenTo(collection, "change", this.updateIndex); + + this.resetIndex(collection); + }, + + /** + Reindex the collection. If `options.reindex` is `false`, this method is a + no-op. + + @param {Backbone.Collection} collection + @param {Object} [options] + @param {boolean} [options.reindex=true] + */ + resetIndex: function (collection, options) { + options = _.extend({reindex: true}, options || {}); + + if (options.reindex) { + var self = this; + this.index = lunr(function () { + _.each(self.fields, function (boost, fieldName) { + this.field(fieldName, boost); + this.ref(self.ref); + }, this); + }); + + collection.each(function (model) { + this.addToIndex(model); + }, this); + } + }, + + /** + Adds the given model to the index. + + @param {Backbone.Model} model + */ + addToIndex: function (model) { + var index = this.index; + var doc = model.toJSON(); + if (index.documentStore.has(doc[this.ref])) index.update(doc); + else index.add(doc); + }, + + /** + Removes the given model from the index. + + @param {Backbone.Model} model + */ + removeFromIndex: function (model) { + var index = this.index; + var doc = model.toJSON(); + if (index.documentStore.has(doc[this.ref])) index.remove(doc); + }, + + /** + Updates the index for the given model. + + @param {Backbone.Model} model + */ + updateIndex: function (model) { + var changed = model.changedAttributes(); + if (changed && !_.isEmpty(_.intersection(_.keys(this.fields), + _.keys(changed)))) { + this.index.update(model.toJSON()); + } + }, + + /** + Takes the query from the search box and performs a full-text search on + the client-side. The search result is returned by resetting the + underlying collection to the models after interrogating the index for the + query answer. + + If the collection is a PageableCollection, searching will go back to the + first page. + */ + search: function () { + var col = this.collection; + if (!this.searchBox().val()) { + col.reset(this.shadowCollection.models, {reindex: false}); + return; + } + + var searchResults = this.index.search(this.searchBox().val()); + var models = []; + for (var i = 0; i < searchResults.length; i++) { + var result = searchResults[i]; + models.push(this.shadowCollection.get(result.ref)); + } + + if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true}); + col.reset(models, {reindex: false}); + } + + }); + +})); http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.min.js ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.min.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.min.js new file mode 100644 index 0000000..3d8edfb --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-filter/js/backgrid-filter.min.js @@ -0,0 +1,8 @@ +/* + backgrid-filter + http://github.com/wyuenho/backgrid + + Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors + Licensed under the MIT @license. +*/ +!function(a,b){"object"==typeof exports?!function(){var a;try{a=require("lunr")}catch(c){}module.exports=b(require("underscore"),require("backbone"),require("backgrid"),a)}():b(a._,a.Backbone,a.Backgrid,a.lunr)}(this,function(a,b,c,d){"use strict";var e=c.Extension.ServerSideFilter=b.View.extend({tagName:"form",className:"backgrid-filter form-search",template:a.template('<span class="search"> </span><input type="search" <% if (placeholder) { %> placeholder="<%- placeholder %>" <% } %> name="<%- name %>" /><a class="clear" data-backgrid-action="clear" href="#">×</a>',null,{variable:null}),events:{"keyup input[type=search]":"showClearButtonMaybe","click a[data-backgrid-action=clear]":"clear",submit:"search"},name:"q",placeholder:null,initialize:function(a){e.__super__.initialize.apply(this,arguments),this.name=a.name||this.name,this.placeholder=a.placeholder||this.placeholder,this.template=a.template||this.template;var c=this.collection,d=this;b.PageableCollection&&c instan ceof b.PageableCollection&&"server"==c.mode&&(c.queryParams[this.name]=function(){return d.searchBox().val()||null})},showClearButtonMaybe:function(){var a=this.clearButton(),b=this.searchBox().val();b?a.show():a.hide()},searchBox:function(){return this.$el.find("input[type=search]")},clearButton:function(){return this.$el.find("a[data-backgrid-action=clear]")},search:function(a){a&&a.preventDefault();var c={},d=this.searchBox().val();d&&(c[this.name]=d);var e=this.collection;b.PageableCollection&&e instanceof b.PageableCollection?e.getFirstPage({data:c,reset:!0,fetch:!0}):e.fetch({data:c,reset:!0})},clear:function(a){a&&a.preventDefault(),this.searchBox().val(null),this.showClearButtonMaybe();var c=this.collection;b.PageableCollection&&c instanceof b.PageableCollection?c.getFirstPage({reset:!0,fetch:!0}):c.fetch({reset:!0})},render:function(){return this.$el.empty().append(this.template({name:this.name,placeholder:this.placeholder,value:this.value})),this.showClearButtonMaybe(),thi s.delegateEvents(),this}}),f=c.Extension.ClientSideFilter=e.extend({events:a.extend({},e.prototype.events,{"click a[data-backgrid-action=clear]":function(a){a.preventDefault(),this.clear()},"keydown input[type=search]":"search",submit:function(a){a.preventDefault(),this.search()}}),fields:null,wait:149,initialize:function(b){f.__super__.initialize.apply(this,arguments),this.fields=b.fields||this.fields,this.wait=b.wait||this.wait,this._debounceMethods(["search","clear"]);var c=this.collection=this.collection.fullCollection||this.collection,d=this.shadowCollection=c.clone();this.listenTo(c,"add",function(a,b,c){d.add(a,c)}),this.listenTo(c,"remove",function(a,b,c){d.remove(a,c)}),this.listenTo(c,"sort",function(a){this.searchBox().val()||d.reset(a.models)}),this.listenTo(c,"reset",function(b,c){c=a.extend({reindex:!0},c||{}),c.reindex&&null==c.from&&null==c.to&&d.reset(b.models)})},_debounceMethods:function(b){a.isString(b)&&(b=[b]),this.undelegateEvents();for(var c=0,d=b.length;d>c; c++){var e=b[c],f=this[e];this[e]=a.debounce(f,this.wait)}this.delegateEvents()},makeRegExp:function(a){return new RegExp(a.trim().split(/\s+/).join("|"),"i")},makeMatcher:function(a){var b=this.makeRegExp(a);return function(a){for(var c=this.fields||a.keys(),d=0,e=c.length;e>d;d++)if(b.test(a.get(c[d])+""))return!0;return!1}},search:function(){var b=a.bind(this.makeMatcher(this.searchBox().val()),this),c=this.collection;c.pageableCollection&&c.pageableCollection.getFirstPage({silent:!0}),c.reset(this.shadowCollection.filter(b),{reindex:!1})},clear:function(){this.searchBox().val(null),this.showClearButtonMaybe();var a=this.collection;a.pageableCollection&&a.pageableCollection.getFirstPage({silent:!0}),a.reset(this.shadowCollection.models,{reindex:!1})}}),g=c.Extension.LunrFilter=f.extend({ref:"id",fields:null,initialize:function(a){g.__super__.initialize.apply(this,arguments),this.ref=a.ref||this.ref;var b=this.collection=this.collection.fullCollection||this.collection;this.listenT o(b,"add",this.addToIndex),this.listenTo(b,"remove",this.removeFromIndex),this.listenTo(b,"reset",this.resetIndex),this.listenTo(b,"change",this.updateIndex),this.resetIndex(b)},resetIndex:function(b,c){if(c=a.extend({reindex:!0},c||{}),c.reindex){var e=this;this.index=d(function(){a.each(e.fields,function(a,b){this.field(b,a),this.ref(e.ref)},this)}),b.each(function(a){this.addToIndex(a)},this)}},addToIndex:function(a){var b=this.index,c=a.toJSON();b.documentStore.has(c[this.ref])?b.update(c):b.add(c)},removeFromIndex:function(a){var b=this.index,c=a.toJSON();b.documentStore.has(c[this.ref])&&b.remove(c)},updateIndex:function(b){var c=b.changedAttributes();c&&!a.isEmpty(a.intersection(a.keys(this.fields),a.keys(c)))&&this.index.update(b.toJSON())},search:function(){var a=this.collection;if(!this.searchBox().val())return void a.reset(this.shadowCollection.models,{reindex:!1});for(var b=this.index.search(this.searchBox().val()),c=[],d=0;d<b.length;d++){var e=b[d];c.push(this.shadowCo llection.get(e.ref))}a.pageableCollection&&a.pageableCollection.getFirstPage({silent:!0}),a.reset(c,{reindex:!1})}})}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/css/backgrid-paginator.css ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/css/backgrid-paginator.css b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/css/backgrid-paginator.css new file mode 100644 index 0000000..20ebbd2 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/css/backgrid-paginator.css @@ -0,0 +1,58 @@ +/* + backgrid-paginator + http://github.com/wyuenho/backgrid + + Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors + Licensed under the MIT license. +*/ + +.backgrid-paginator { + text-align: center; + border-top: none; + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.backgrid-paginator ul { + display: inline-block; + *display: inline; + margin: 5px 0; + *zoom: 1; +} + +.backgrid-paginator ul > li { + display: inline; +} + +.backgrid-paginator ul > li > a, +.backgrid-paginator ul > li > span { + float: left; + width: 30px; + height: 30px; + padding: 0; + line-height: 30px; + text-decoration: none; +} + +.backgrid-paginator ul > li > a:hover, +.backgrid-paginator ul > .active > a, +.backgrid-paginator ul > .active > span { + background-color: #f5f5f5; +} + +.backgrid-paginator ul > .active > a, +.backgrid-paginator ul > .active > span { + color: #999999; + cursor: default; +} + +.backgrid-paginator ul > .disabled > span, +.backgrid-paginator ul > .disabled > a, +.backgrid-paginator ul > .disabled > a:hover { + color: #999999; + cursor: default; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/39c85bb8/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/js/backgrid-paginator.js ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/js/backgrid-paginator.js b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/js/backgrid-paginator.js new file mode 100644 index 0000000..a0b3af9 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-portal/src/main/webapp/libs/bower/backgrid-paginator/js/backgrid-paginator.js @@ -0,0 +1,427 @@ +/* + backgrid-paginator + http://github.com/wyuenho/backgrid + + Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors + Licensed under the MIT @license. +*/ +(function (root, factory) { + + // CommonJS + if (typeof exports == "object") { + module.exports = factory(require("underscore"), + require("backbone"), + require("backgrid"), + require("backbone-pageable")); + } + // Browser + else { + factory(root._, root.Backbone, root.Backgrid); + } + +}(this, function (_, Backbone, Backgrid) { + + "use strict"; + + /** + PageHandle is a class that renders the actual page handles and reacts to + click events for pagination. + + This class acts in two modes - control or discrete page handle modes. If + one of the `is*` flags is `true`, an instance of this class is under + control page handle mode. Setting a `pageIndex` to an instance of this + class under control mode has no effect and the correct page index will + always be inferred from the `is*` flag. Only one of the `is*` flags should + be set to `true` at a time. For example, an instance of this class cannot + simultaneously be a rewind control and a fast forward control. A `label` + and a `title` template or a string are required to be passed to the + constuctor under this mode. If a `title` template is provided, it __MUST__ + accept a parameter `label`. When the `label` is provided to the `title` + template function, its result will be used to render the generated anchor's + title attribute. + + If all of the `is*` flags is set to `false`, which is the default, an + instance of this class will be in discrete page handle mode. An instance + under this mode requires the `pageIndex` to be passed from the constructor + as an option and it __MUST__ be a 0-based index of the list of page numbers + to render. The constuctor will normalize the base to the same base the + underlying PageableCollection collection instance uses. A `label` is not + required under this mode, which will default to the equivalent 1-based page + index calculated from `pageIndex` and the underlying PageableCollection + instance. A provided `label` will still be honored however. The `title` + parameter is also not required under this mode, in which case the default + `title` template will be used. You are encouraged to provide your own + `title` template however if you wish to localize the title strings. + + If this page handle represents the current page, an `active` class will be + placed on the root list element. + + If this page handle is at the border of the list of pages, a `disabled` + class will be placed on the root list element. + + Only page handles that are neither `active` nor `disabled` will respond to + click events and triggers pagination. + + @class Backgrid.Extension.PageHandle + */ + var PageHandle = Backgrid.Extension.PageHandle = Backbone.View.extend({ + + /** @property */ + tagName: "li", + + /** @property */ + events: { + "click a": "changePage" + }, + + /** + @property {string|function(Object.<string, string>): string} title + The title to use for the `title` attribute of the generated page handle + anchor elements. It can be a string or an Underscore template function + that takes a mandatory `label` parameter. + */ + title: _.template('Page <%- label %>', null, {variable: null}), + + /** + @property {boolean} isRewind Whether this handle represents a rewind + control + */ + isRewind: false, + + /** + @property {boolean} isBack Whether this handle represents a back + control + */ + isBack: false, + + /** + @property {boolean} isForward Whether this handle represents a forward + control + */ + isForward: false, + + /** + @property {boolean} isFastForward Whether this handle represents a fast + forward control + */ + isFastForward: false, + + /** + Initializer. + + @param {Object} options + @param {Backbone.Collection} options.collection + @param {number} pageIndex 0-based index of the page number this handle + handles. This parameter will be normalized to the base the underlying + PageableCollection uses. + @param {string} [options.label] If provided it is used to render the + anchor text, otherwise the normalized pageIndex will be used + instead. Required if any of the `is*` flags is set to `true`. + @param {string} [options.title] + @param {boolean} [options.isRewind=false] + @param {boolean} [options.isBack=false] + @param {boolean} [options.isForward=false] + @param {boolean} [options.isFastForward=false] + */ + initialize: function (options) { + var collection = this.collection; + var state = collection.state; + var currentPage = state.currentPage; + var firstPage = state.firstPage; + var lastPage = state.lastPage; + + _.extend(this, _.pick(options, + ["isRewind", "isBack", "isForward", "isFastForward"])); + + var pageIndex; + if (this.isRewind) pageIndex = firstPage; + else if (this.isBack) pageIndex = Math.max(firstPage, currentPage - 1); + else if (this.isForward) pageIndex = Math.min(lastPage, currentPage + 1); + else if (this.isFastForward) pageIndex = lastPage; + else { + pageIndex = +options.pageIndex; + pageIndex = (firstPage ? pageIndex + 1 : pageIndex); + } + this.pageIndex = pageIndex; + + this.label = (options.label || (firstPage ? pageIndex : pageIndex + 1)) + ''; + var title = options.title || this.title; + this.title = _.isFunction(title) ? title({label: this.label}) : title; + }, + + /** + Renders a clickable anchor element under a list item. + */ + render: function () { + this.$el.empty(); + var anchor = document.createElement("a"); + anchor.href = '#'; + if (this.title) anchor.title = this.title; + anchor.innerHTML = this.label; + this.el.appendChild(anchor); + + var collection = this.collection; + var state = collection.state; + var currentPage = state.currentPage; + var pageIndex = this.pageIndex; + + if (this.isRewind && currentPage == state.firstPage || + this.isBack && !collection.hasPrevious() || + this.isForward && !collection.hasNext() || + this.isFastForward && (currentPage == state.lastPage || state.totalPages < 1)) { + this.$el.addClass("disabled"); + } + else if (!(this.isRewind || + this.isBack || + this.isForward || + this.isFastForward) && + state.currentPage == pageIndex) { + this.$el.addClass("active"); + } + + this.delegateEvents(); + return this; + }, + + /** + jQuery click event handler. Goes to the page this PageHandle instance + represents. No-op if this page handle is currently active or disabled. + */ + changePage: function (e) { + e.preventDefault(); + var $el = this.$el, col = this.collection; + if (!$el.hasClass("active") && !$el.hasClass("disabled")) { + if (this.isRewind) col.getFirstPage(); + else if (this.isBack) col.getPreviousPage(); + else if (this.isForward) col.getNextPage(); + else if (this.isFastForward) col.getLastPage(); + else col.getPage(this.pageIndex, {reset: true}); + } + return this; + } + + }); + + /** + Paginator is a Backgrid extension that renders a series of configurable + pagination handles. This extension is best used for splitting a large data + set across multiple pages. If the number of pages is larger then a + threshold, which is set to 10 by default, the page handles are rendered + within a sliding window, plus the rewind, back, forward and fast forward + control handles. The individual control handles can be turned off. + + @class Backgrid.Extension.Paginator + */ + var Paginator = Backgrid.Extension.Paginator = Backbone.View.extend({ + + /** @property */ + className: "backgrid-paginator", + + /** @property */ + windowSize: 10, + + /** + @property {number} slideScale the number used by #slideHowMuch to scale + `windowSize` to yield the number of pages to slide. For example, the + default windowSize(10) * slideScale(0.5) yields 5, which means the window + will slide forward 5 pages as soon as you've reached page 6. The smaller + the scale factor the less pages to slide, and vice versa. + + Also See: + + - #slideMaybe + - #slideHowMuch + */ + slideScale: 0.5, + + /** + @property {Object.<string, Object.<string, string>>} controls You can + disable specific control handles by setting the keys in question to + null. The defaults will be merged with your controls object, with your + changes taking precedent. + */ + controls: { + rewind: { + label: "ã", + title: "First" + }, + back: { + label: "ã", + title: "Previous" + }, + forward: { + label: "ã", + title: "Next" + }, + fastForward: { + label: "ã", + title: "Last" + } + }, + + /** @property */ + renderIndexedPageHandles: true, + + /** + @property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle + class to use for rendering individual handles + */ + pageHandle: PageHandle, + + /** @property */ + goBackFirstOnSort: true, + + /** + Initializer. + + @param {Object} options + @param {Backbone.Collection} options.collection + @param {boolean} [options.controls] + @param {boolean} [options.pageHandle=Backgrid.Extension.PageHandle] + @param {boolean} [options.goBackFirstOnSort=true] + */ + initialize: function (options) { + var self = this; + self.controls = _.defaults(options.controls || {}, self.controls, + Paginator.prototype.controls); + + _.extend(self, _.pick(options || {}, "windowSize", "pageHandle", + "slideScale", "goBackFirstOnSort", + "renderIndexedPageHandles")); + + var col = self.collection; + self.listenTo(col, "add", self.render); + self.listenTo(col, "remove", self.render); + self.listenTo(col, "reset", self.render); + self.listenTo(col, "backgrid:sorted", function () { + if (self.goBackFirstOnSort) col.getFirstPage({reset: true}); + }); + }, + + /** + Decides whether the window should slide. This method should return 1 if + sliding should occur and 0 otherwise. The default is sliding should occur + if half of the pages in a window has been reached. + + __Note__: All the parameters have been normalized to be 0-based. + + @param {number} firstPage + @param {number} lastPage + @param {number} currentPage + @param {number} windowSize + @param {number} slideScale + + @return {0|1} + */ + slideMaybe: function (firstPage, lastPage, currentPage, windowSize, slideScale) { + return Math.round(currentPage % windowSize / windowSize); + }, + + /** + Decides how many pages to slide when sliding should occur. The default + simply scales the `windowSize` to arrive at a fraction of the `windowSize` + to increment. + + __Note__: All the parameters have been normalized to be 0-based. + + @param {number} firstPage + @param {number} lastPage + @param {number} currentPage + @param {number} windowSize + @param {number} slideScale + + @return {number} + */ + slideThisMuch: function (firstPage, lastPage, currentPage, windowSize, slideScale) { + return ~~(windowSize * slideScale); + }, + + _calculateWindow: function () { + var collection = this.collection; + var state = collection.state; + + // convert all indices to 0-based here + var firstPage = state.firstPage; + var lastPage = +state.lastPage; + lastPage = Math.max(0, firstPage ? lastPage - 1 : lastPage); + var currentPage = Math.max(state.currentPage, state.firstPage); + currentPage = firstPage ? currentPage - 1 : currentPage; + var windowSize = this.windowSize; + var slideScale = this.slideScale; + var windowStart = Math.floor(currentPage / windowSize) * windowSize; + if (currentPage <= lastPage - this.slideThisMuch()) { + windowStart += (this.slideMaybe(firstPage, lastPage, currentPage, windowSize, slideScale) * + this.slideThisMuch(firstPage, lastPage, currentPage, windowSize, slideScale)); + } + var windowEnd = Math.min(lastPage + 1, windowStart + windowSize); + return [windowStart, windowEnd]; + }, + + /** + Creates a list of page handle objects for rendering. + + @return {Array.<Object>} an array of page handle objects hashes + */ + makeHandles: function () { + + var handles = []; + var collection = this.collection; + + var window = this._calculateWindow(); + var winStart = window[0], winEnd = window[1]; + + if (this.renderIndexedPageHandles) { + for (var i = winStart; i < winEnd; i++) { + handles.push(new this.pageHandle({ + collection: collection, + pageIndex: i + })); + } + } + + var controls = this.controls; + _.each(["back", "rewind", "forward", "fastForward"], function (key) { + var value = controls[key]; + if (value) { + var handleCtorOpts = { + collection: collection, + title: value.title, + label: value.label + }; + handleCtorOpts["is" + key.slice(0, 1).toUpperCase() + key.slice(1)] = true; + var handle = new this.pageHandle(handleCtorOpts); + if (key == "rewind" || key == "back") handles.unshift(handle); + else handles.push(handle); + } + }, this); + + return handles; + }, + + /** + Render the paginator handles inside an unordered list. + */ + render: function () { + this.$el.empty(); + + if (this.handles) { + for (var i = 0, l = this.handles.length; i < l; i++) { + this.handles[i].remove(); + } + } + + var handles = this.handles = this.makeHandles(); + + var ul = document.createElement("ul"); + for (var i = 0; i < handles.length; i++) { + ul.appendChild(handles[i].render().el); + } + + this.el.appendChild(ul); + + return this; + } + + }); + +}));
