uwidget.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. !(function ($) {
  2. var UWidget = function (element, options) {
  3. this.init($(element), options);
  4. };
  5. UWidget.prototype = {
  6. options: {
  7. url: null,
  8. handler: null,
  9. template: null,
  10. sort: {
  11. enabled: false,
  12. name: 'sort',
  13. values: ['id', 'date'],
  14. labels: ['Identifier', 'Date']
  15. },
  16. direction: {
  17. enabled: false,
  18. name: 'direction',
  19. values: ['desc', 'asc'],
  20. labels: ['Descending', 'Ascending']
  21. },
  22. filters: {
  23. ebabled: false,
  24. name: 'filters',
  25. values: [],
  26. labels: []
  27. }
  28. },
  29. init: function ($element, options) {
  30. this.$element = $element;
  31. this.options = $.extend(true, {}, this.options, options);
  32. this._xhrCache = {};
  33. if (!this.options.url || !this.options.handler || !this.options.template)
  34. throw new Error('You must define a widget url, an ajax handler and a template');
  35. this
  36. ._initActions()
  37. ._initFromDOM()
  38. .fetch();
  39. },
  40. _initActions: function () {
  41. var i, checked;
  42. this.$actions = $('<span class="uwidget-actions"></span>');
  43. if (this.options.sort.enabled) {
  44. this.$sort = $('<select name="'+ this.options.sort.name +'"></select>')
  45. .on('change', false, $.proxy(this._updateActions, this));
  46. for (i = 0; i < this.options.sort.values.length; i++)
  47. this.$sort.append('<option value="' + this.options.sort.values[i] + '">' + this.options.sort.labels[i] + '</option>');
  48. this.$actions.append(this.$sort);
  49. }
  50. if (this.options.direction.enabled) {
  51. this.$direction = $('<select name="'+ this.options.direction.name +'"></select>')
  52. .on('change', false, $.proxy(this._updateActions, this));
  53. for (i = 0; i < this.options.direction.values.length; i++)
  54. this.$direction.append('<option value="' + this.options.direction.values[i] + '">' + this.options.direction.labels[i] + '</option>');
  55. this.$actions.append(this.$direction);
  56. }
  57. if (this.options.filters.enabled) {
  58. this.$filters = $('<span class="filters"></span>')
  59. .on('change', false, $.proxy(this._updateActions, this));
  60. for (i = 0; i < this.options.filters.values.length; i++) {
  61. checked = this.$element.data('filters') && new RegExp(this.options.filters.labels[i], 'i').test(this.$element.data('filters'));
  62. this.$filters.append(this.options.filters.labels[i] + ' <input type="checkbox" name="filters[]" value="' + this.options.filters.values[i] + '" ' + (checked ? 'checked' : '') + '/>');
  63. }
  64. this.$actions.append(this.$filters);
  65. }
  66. this.$container = $('<ul class="uwidget-container"></ul>');
  67. this.$info = $('<span class="uwidget-info"><a href="#" target="_blank">UWidget</a></span>');
  68. this.$element
  69. .append(this.$actions)
  70. .append(this.$container)
  71. .append(this.$info);
  72. this._updateActions();
  73. return this;
  74. },
  75. _initFromDOM: function () {
  76. if (this.$element.data('width'))
  77. this.$element.css('width', this.$element.data('width'));
  78. if (this.$element.data('height')) {
  79. this.$element.css('height', this.$element.data('height'));
  80. this.$container.css('height', this.$element.height() - this.$actions.height() - this.$info.height());
  81. }
  82. return this;
  83. },
  84. _updateActions: function () {
  85. if (this.options.sort.enabled)
  86. this.$element.data('sort', this.$sort.val());
  87. if (this.options.direction.enabled)
  88. this.$element.data('direction', this.$direction.val());
  89. if (this.options.filters.enabled) {
  90. var val = [];
  91. this.$actions.find('input[type=checkbox]:checked').each(function () {
  92. val.push($(this).val());
  93. });
  94. this.$element.data('filters', val.join(', '));
  95. }
  96. this.fetch();
  97. },
  98. getUrl: function () {
  99. var url = ('function' === typeof this.options.url ? this.options.url(this.options) : this.options.url),
  100. options = ['sort', 'direction', 'filters'],
  101. value = '';
  102. url += -1 !== url.indexOf('?') ? '&uwidget' : '?uwidget';
  103. for (var i = 0; i < options.length; i++) {
  104. value = this.$element.data([options[i]] + '');
  105. if (this.options[options[i]].enabled && value.length)
  106. url += '&' + this.options[options[i]].name + '=' + value;
  107. }
  108. return url;
  109. },
  110. fetch: function () {
  111. var that = this,
  112. url = that.getUrl();
  113. this.$element
  114. .removeClass('error')
  115. .removeClass('fetched')
  116. .addClass('fetching');
  117. if ('undefined' !== typeof this._xhrCache[url])
  118. return this._updateCollection.apply(this, this._xhrCache[url]);
  119. $.ajax($.extend(true, {}, {
  120. url: url
  121. }, that.$element.data('remoteOptions')))
  122. .done(function () {
  123. that._updateCollection.apply(that, arguments);
  124. that._xhrCache[url] = arguments;
  125. })
  126. .fail(function () {
  127. that.$container.addClass('error');
  128. })
  129. .always(function () {
  130. that.$container.removeClass('fetching');
  131. });
  132. },
  133. _updateCollection: function (collection) {
  134. this.$container.html('').addClass('fetched');
  135. collection = this.options.handler.apply(this, arguments);
  136. for (var i = 0; i < collection.length; i++)
  137. this.$container.append(tmpl(this.options.template, collection[i]));
  138. }
  139. };
  140. $.fn.UWidget = function (options) {
  141. return new UWidget(this, options);
  142. };
  143. // Simple JavaScript Templating
  144. // John Resig - http://ejohn.org/ - MIT Licensed
  145. (function(){
  146. var cache = {};
  147. this.tmpl = function tmpl(str, data){
  148. // Figure out if we're getting a template, or if we need to
  149. // load the template - and be sure to cache the result.
  150. var fn = !/\W/.test(str) ?
  151. cache[str] = cache[str] ||
  152. tmpl(document.getElementById(str).innerHTML) :
  153. // Generate a reusable function that will serve as a template
  154. // generator (and which will be cached).
  155. new Function("obj",
  156. "var p=[],print=function(){p.push.apply(p,arguments);};" +
  157. // Introduce the data as local variables using with(){}
  158. "with(obj){p.push('" +
  159. // Convert the template into pure JavaScript
  160. str
  161. .replace(/[\r\t\n]/g, " ")
  162. .split("<%").join("\t")
  163. .replace(/((^|%>)[^\t]*)'/g, "$1\r")
  164. .replace(/\t=(.*?)%>/g, "',$1,'")
  165. .split("\t").join("');")
  166. .split("%>").join("p.push('")
  167. .split("\r").join("\\'")
  168. + "');}return p.join('');");
  169. // Provide some basic currying to the user
  170. return data ? fn(data) : fn;
  171. };
  172. })();
  173. })(window.jQuery);