datatable-sort.js revision a3b15d60042c81a524cebb94370e5a234a19d04b
220N/A/**
1472N/A * Plugs DataTable with sorting functionality.
220N/A *
220N/A * @module datatable
220N/A * @submodule datatable-sort
220N/A */
220N/A
220N/A/**
220N/A * Adds column sorting to DataTable.
220N/A * @class DataTableSort
220N/A * @extends Plugin.Base
220N/A */
220N/Avar YgetClassName = Y.ClassNameManager.getClassName,
220N/A
220N/A DATATABLE = "datatable",
220N/A COLUMN = "column",
220N/A ASC = "asc",
220N/A DESC = "desc",
1472N/A
1472N/A //TODO: Don't use hrefs - use tab/arrow/enter
1472N/A TEMPLATE = '<a class="{link_class}" title="{link_title}" href="{link_href}">{value}</a>';
220N/A
220N/A
220N/Afunction DataTableSort() {
220N/A DataTableSort.superclass.constructor.apply(this, arguments);
220N/A}
220N/A
220N/A/////////////////////////////////////////////////////////////////////////////
220N/A//
220N/A// STATIC PROPERTIES
220N/A//
220N/A/////////////////////////////////////////////////////////////////////////////
220N/AY.mix(DataTableSort, {
220N/A /**
220N/A * The namespace for the plugin. This will be the property on the host which
220N/A * references the plugin instance.
220N/A *
220N/A * @property NS
220N/A * @type String
220N/A * @static
220N/A * @final
220N/A * @value "sort"
220N/A */
220N/A NS: "sort",
220N/A
220N/A /**
220N/A * Class name.
220N/A *
220N/A * @property NAME
220N/A * @type String
220N/A * @static
220N/A * @final
220N/A * @value "dataTableSort"
220N/A */
220N/A NAME: "dataTableSort",
220N/A
220N/A/////////////////////////////////////////////////////////////////////////////
220N/A//
220N/A// ATTRIBUTES
220N/A//
220N/A/////////////////////////////////////////////////////////////////////////////
220N/A ATTRS: {
220N/A /**
220N/A * @attribute trigger
220N/A * @description Defines the trigger that causes a column to be sorted:
220N/A * {event, selector}, where "event" is an event type and "selector" is
220N/A * is a node query selector.
220N/A * @type Object
220N/A * @default {event:"click", selector:"th"}
220N/A * @writeOnce "initOnly"
220N/A */
220N/A trigger: {
220N/A value: {event:"click", selector:"th"},
220N/A writeOnce: "initOnly"
220N/A },
220N/A
220N/A /**
220N/A * @attribute lastSortedBy
220N/A * @description Describes last known sort state: {key,dir}, where
220N/A * "key" is column key and "dir" is either "asc" or "desc".
220N/A * @type Object
220N/A */
220N/A lastSortedBy: {
220N/A setter: "_setLastSortedBy",
220N/A lazyAdd: false
220N/A },
220N/A
220N/A /**
220N/A * @attribute template
220N/A * @description Tokenized markup template for TH sort element.
220N/A * @type String
220N/A * @default '<a class="{link_class}" title="{link_title}" href="{link_href}">{value}</a>'
220N/A */
220N/A template: {
220N/A value: TEMPLATE
220N/A },
220N/A
220N/A /**
220N/A * Strings used in the UI elements.
220N/A *
220N/A * The strings used are defaulted from the datatable-sort language pack
220N/A * for the language identified in the YUI "lang" configuration (which
220N/A * defaults to "en").
220N/A *
220N/A * Configurable strings are "sortBy" and "reverseSortBy", which are
220N/A * assigned to the sort link's title attribute.
220N/A *
220N/A * @attribute strings
220N/A * @type {Object}
220N/A */
220N/A strings: {
220N/A valueFn: function () { return Y.Intl.get('datatable-sort'); }
220N/A }
220N/A }
220N/A});
220N/A
220N/A/////////////////////////////////////////////////////////////////////////////
220N/A//
220N/A// PROTOTYPE
220N/A//
220N/A/////////////////////////////////////////////////////////////////////////////
220N/AY.extend(DataTableSort, Y.Plugin.Base, {
220N/A
220N/A /////////////////////////////////////////////////////////////////////////////
220N/A //
220N/A // METHODS
220N/A //
220N/A /////////////////////////////////////////////////////////////////////////////
220N/A /**
220N/A * Initializer.
220N/A *
220N/A * @method initializer
220N/A * @param config {Object} Config object.
220N/A * @private
220N/A */
220N/A initializer: function(config) {
220N/A var dt = this.get("host"),
220N/A trigger = this.get("trigger");
220N/A
220N/A dt.get("recordset").plug(Y.Plugin.RecordsetSort, {dt: dt});
220N/A dt.get("recordset").sort.addTarget(dt);
220N/A
220N/A // Wrap link around TH value
220N/A this.doBefore("_createTheadThNode", this._beforeCreateTheadThNode);
220N/A
220N/A // Add class
220N/A this.doBefore("_attachTheadThNode", this._beforeAttachTheadThNode);
220N/A this.doBefore("_attachTbodyTdNode", this._beforeAttachTbodyTdNode);
220N/A
220N/A // Attach trigger handlers
220N/A dt.delegate(trigger.event, Y.bind(this._onEventSortColumn,this), trigger.selector);
220N/A
220N/A // Attach UI hooks
220N/A dt.after("recordsetSort:sort", function() {
220N/A this._uiSetRecordset(this.get("recordset"));
220N/A });
220N/A this.on("lastSortedByChange", function(e) {
220N/A this._uiSetLastSortedBy(e.prevVal, e.newVal, dt);
220N/A });
220N/A
220N/A //TODO
220N/A //dt.after("recordset:mutation", function() {//reset lastSortedBy});
220N/A
220N/A //TODO
220N/A //add Column sortFn ATTR
220N/A
220N/A // Update UI after the fact (render-then-plug case)
220N/A if(dt.get("rendered")) {
220N/A dt._uiSetColumnset(dt.get("columnset"));
220N/A this._uiSetLastSortedBy(null, this.get("lastSortedBy"), dt);
220N/A }
220N/A },
220N/A
220N/A /**
220N/A * @method _setLastSortedBy
220N/A * @description Normalizes lastSortedBy
220N/A * @param val {String | Object} {key, dir} or "key"
220N/A * @return {key, dir, notdir}
220N/A * @private
220N/A */
220N/A _setLastSortedBy: function(val) {
220N/A if (Y.Lang.isString(val)) {
220N/A val = { key: val, dir: "desc" };
220N/A }
220N/A
220N/A if (val) {
220N/A return (val.dir === "desc") ?
220N/A { key: val.key, dir: "desc", notdir: "asc" } :
220N/A { key: val.key, dir: "asc", notdir:"desc" };
220N/A } else {
220N/A return null;
220N/A }
220N/A },
220N/A
220N/A /**
220N/A * Updates sort UI.
220N/A *
220N/A * @method _uiSetLastSortedBy
220N/A * @param val {Object} New lastSortedBy object {key,dir}.
220N/A * @param dt {Y.DataTable.Base} Host.
220N/A * @protected
220N/A */
220N/A _uiSetLastSortedBy: function(prevVal, newVal, dt) {
220N/A var strings = this.get('strings'),
220N/A columnset = dt.get("columnset"),
220N/A prevKey = prevVal && prevVal.key,
220N/A newKey = newVal && newVal.key,
220N/A prevClass = prevVal && dt.getClassName(prevVal.dir),
220N/A newClass = newVal && dt.getClassName(newVal.dir),
220N/A prevColumn = columnset.keyHash[prevKey],
220N/A newColumn = columnset.keyHash[newKey],
220N/A tbodyNode = dt._tbodyNode,
220N/A fromTemplate = Y.Lang.sub,
220N/A th, sortArrow, sortLabel;
220N/A
220N/A // Clear previous UI
220N/A if (prevColumn && prevClass) {
220N/A th = prevColumn.thNode;
220N/A sortArrow = th.one('a');
220N/A
220N/A if (sortArrow) {
220N/A sortArrow.set('title', fromTemplate(strings.sortBy, {
220N/A column: prevColumn.get('label')
220N/A }));
220N/A }
220N/A
220N/A th.removeClass(prevClass);
220N/A tbodyNode.all("." + YgetClassName(COLUMN, prevColumn.get("id")))
220N/A .removeClass(prevClass);
220N/A }
220N/A
220N/A // Add new sort UI
220N/A if (newColumn && newClass) {
220N/A th = newColumn.thNode;
220N/A sortArrow = th.one('a');
220N/A
220N/A if (sortArrow) {
220N/A sortLabel = (newVal.dir === ASC) ? "reverseSortBy" : "sortBy";
220N/A
220N/A sortArrow.set('title', fromTemplate(strings[sortLabel], {
220N/A column: newColumn.get('label')
220N/A }));
220N/A }
220N/A
220N/A th.addClass(newClass);
220N/A
220N/A tbodyNode.all("." + YgetClassName(COLUMN, newColumn.get("id")))
220N/A .addClass(newClass);
220N/A }
220N/A },
220N/A
220N/A /**
220N/A * Before header cell element is created, inserts link markup around {value}.
220N/A *
220N/A * @method _beforeCreateTheadThNode
220N/A * @param o {Object} {value, column, tr}.
220N/A * @protected
220N/A */
220N/A _beforeCreateTheadThNode: function(o) {
220N/A var sortedBy, sortLabel;
220N/A
220N/A if (o.column.get("sortable")) {
220N/A sortedBy = this.get('lastSortedBy');
220N/A
220N/A sortLabel = (sortedBy && sortedBy.dir === ASC &&
220N/A sortedBy.key === o.column.get('key')) ?
220N/A "reverseSortBy" : "sortBy";
220N/A
220N/A o.value = Y.Lang.sub(this.get("template"), {
220N/A link_class: o.link_class || "",
220N/A link_title: Y.Lang.sub(this.get('strings.' + sortLabel), {
220N/A column: o.column.get('label')
220N/A }),
220N/A link_href: "#",
220N/A value: o.value
220N/A });
220N/A }
220N/A },
220N/A
220N/A /**
220N/A * Before header cell element is attached, sets applicable class names.
220N/A *
220N/A * @method _beforeAttachTheadThNode
220N/A * @param o {Object} {value, column, tr}.
220N/A * @protected
220N/A */
220N/A _beforeAttachTheadThNode: function(o) {
220N/A var lastSortedBy = this.get("lastSortedBy"),
220N/A key = lastSortedBy && lastSortedBy.key,
220N/A dir = lastSortedBy && lastSortedBy.dir,
220N/A notdir = lastSortedBy && lastSortedBy.notdir;
220N/A
220N/A // This Column is sortable
220N/A if(o.column.get("sortable")) {
220N/A o.th.addClass(YgetClassName(DATATABLE, "sortable"));
220N/A }
220N/A // This Column is currently sorted
220N/A if(key && (key === o.column.get("key"))) {
220N/A o.th.replaceClass(YgetClassName(DATATABLE, notdir), YgetClassName(DATATABLE, dir));
220N/A }
220N/A },
220N/A
220N/A /**
220N/A * Before header cell element is attached, sets applicable class names.
220N/A *
220N/A * @method _beforeAttachTbodyTdNode
220N/A * @param o {Object} {record, column, tr, headers, classnames, value}.
220N/A * @protected
220N/A */
220N/A _beforeAttachTbodyTdNode: function(o) {
220N/A var lastSortedBy = this.get("lastSortedBy"),
220N/A key = lastSortedBy && lastSortedBy.key,
220N/A dir = lastSortedBy && lastSortedBy.dir,
220N/A notdir = lastSortedBy && lastSortedBy.notdir;
220N/A
220N/A // This Column is sortable
220N/A if(o.column.get("sortable")) {
220N/A o.td.addClass(YgetClassName(DATATABLE, "sortable"));
220N/A }
220N/A // This Column is currently sorted
220N/A if(key && (key === o.column.get("key"))) {
220N/A o.td.replaceClass(YgetClassName(DATATABLE, notdir), YgetClassName(DATATABLE, dir));
220N/A }
220N/A },
220N/A /**
220N/A * In response to the "trigger" event, sorts the underlying Recordset and
220N/A * updates the lastSortedBy attribute.
220N/A *
220N/A * @method _onEventSortColumn
220N/A * @param o {Object} {value, column, tr}.
220N/A * @protected
220N/A */
220N/A _onEventSortColumn: function(e) {
220N/A e.halt();
220N/A //TODO: normalize e.currentTarget to TH
220N/A var table = this.get("host"),
220N/A column = table.get("columnset").idHash[e.currentTarget.get("id")],
220N/A key, field, lastSort, desc, sorter;
220N/A
220N/A if (column.get("sortable")) {
220N/A key = column.get("key");
220N/A field = column.get("field");
220N/A lastSort = this.get("lastSortedBy") || {};
220N/A desc = (lastSort.key === key && lastSort.dir === ASC);
220N/A sorter = column.get("sortFn");
220N/A
220N/A table.get("recordset").sort.sort(field, desc, sorter);
220N/A
220N/A this.set("lastSortedBy", {
220N/A key: key,
220N/A dir: (desc) ? DESC : ASC
220N/A });
220N/A }
220N/A }
220N/A});
220N/A
220N/AY.namespace("Plugin").DataTableSort = DataTableSort;
220N/A
220N/A
220N/A
220N/A