PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/js/Switcher.js

https://github.com/triangle/Switcher
JavaScript | 455 lines | 397 code | 53 blank | 5 comment | 119 complexity | c12b7559ea94329fd76d8f955f05d7eb MD5 | raw file
  1. /*
  2. * Switcher v0.53
  3. *
  4. * Requires jQuery
  5. */
  6. var Switcher = function(options){
  7. return new Switcher.Basic(options);
  8. };
  9. Switcher.Basic = function(options){
  10. if (typeof options.items == 'string') {
  11. options.items = {
  12. selector: options.items
  13. }
  14. }
  15. this.options = $.extend(true, {}, this.defaultOptions, options);
  16. this._initItems();
  17. this._attachCallback();
  18. if (this.options.targets) {
  19. this.targets = new Switcher.Targets(this, this.options.targets)
  20. if (options.action) {
  21. this._action = new Switcher.Action(this, options.action);
  22. }
  23. }
  24. this._processInitValue();
  25. }
  26. Switcher.Basic.prototype = {
  27. _initItems: function(){
  28. var oThis = this;
  29. this.items = [];
  30. this.jItems = $(this.options.items.selector);
  31. this.jItems.each(function(){
  32. var newItem = new Switcher.SwitcherItem({
  33. element: this,
  34. itemsOptions: oThis.options.items,
  35. itemIndex: oThis.items.length
  36. });
  37. oThis.items.push(newItem);
  38. newItem._eventElement[oThis.options.items.event](
  39. $.proxy(function(e){
  40. this._itemCallback(e, newItem);
  41. }, oThis)
  42. );
  43. if (!oThis.selectedItem && newItem.isSelected() && typeof oThis.options.initValue === 'undefined'){
  44. oThis.options.initValue = newItem._value;
  45. oThis._setSelectedItem(newItem);
  46. }
  47. });
  48. },
  49. _itemCallback: function(e, item){
  50. if (typeof this.options.action === 'undefined' || this.options.action.preventDefault != false) {
  51. e.preventDefault();
  52. }
  53. if (this.options.stopPropagation != false) {
  54. e.stopPropagation();
  55. }
  56. this.action(item);
  57. },
  58. _attachCallback: function(){
  59. if (this.options.onSelect && typeof this.options.onSelect === 'function') {
  60. this.onSelect = $.proxy(this.options.onSelect, this);
  61. }
  62. },
  63. _setSelectedItem: function(item){
  64. this._clearSelectedItem();
  65. this.selectedItem = item;
  66. this.selectedValue = item._value;
  67. },
  68. _clearSelectedItem: function(){
  69. if (this.selectedItem) {
  70. this.prevSelectedItem = this.selectedItem;
  71. this.prevSelectedValue = this.selectedItem._value;
  72. }
  73. this.selectedItem = null;
  74. this.selectedValue = null
  75. },
  76. _callback: function(){
  77. if (this.onSelect) {
  78. this.onSelect();
  79. }
  80. },
  81. _lock: function(){
  82. this.isLocked = true;
  83. },
  84. _unlock: function(){
  85. this.isLocked = false;
  86. },
  87. _processInitValue: function(){
  88. if(typeof this.options.initValue !== 'undefined') {
  89. for(var i = 0, len = this.items.length; i < len; i++){
  90. if (this.options.initValue == this.options.initValueTemplate.replace('%', this.items[i]._value)){
  91. this.items[i]._eventElement.eq(0)[this.options.items.event]();
  92. this._initedValue = this.items[i]._value;
  93. if (!this.options.multiselect) break;
  94. } else {
  95. this.items[i].deselect();
  96. }
  97. }
  98. if (typeof this._initedValue !== 'undefined' && this.targets) {
  99. for (value in this.targets.oItems) {
  100. if (this.targets.oItems.hasOwnProperty(value) && value != this._initedValue) {
  101. this._action._reverse(value, true);
  102. }
  103. }
  104. }
  105. }
  106. },
  107. action: function(item){
  108. if (!(this.options.multiselect || item.isSelected() && this.options.multistate)) {
  109. if (this.isLocked || (item.isSelected() && !this.options.multistate)) return;
  110. if (!(this.options.multistate && item.isSelected())){
  111. this.deselectSelectedItem();
  112. item.select();
  113. this._setSelectedItem(item);
  114. if (this._action) {
  115. this._action.execute(this);
  116. }
  117. }
  118. } else if (this.options.multiselect || this.options.multistate) {
  119. if (this.isLocked) return;
  120. if (item.isSelected()) {
  121. item.deselect();
  122. if (this._action) {
  123. this._action._reverse(item._value);
  124. }
  125. } else {
  126. if (!this.options.multistate) {
  127. item.select();
  128. if (this.targets) {
  129. this._action._forward(item._value);
  130. }
  131. }
  132. }
  133. }
  134. this._callback();
  135. },
  136. deselectSelectedItem: function(){
  137. if (this.selectedItem) {
  138. this.selectedItem.deselect();
  139. }
  140. },
  141. deselectAllItems: function(){
  142. for (var i = 0, num = this.items.length; i < num; i++){
  143. this.items[i].deselect();
  144. }
  145. },
  146. defaultOptions: {
  147. items: {
  148. selectedClass: 'selected',
  149. valueSource: 'index',
  150. event: 'click'
  151. },
  152. multiselect: false,
  153. initValueTemplate: '%',
  154. stopPropagation: false
  155. }
  156. }
  157. Switcher.SwitcherItem = function(options){
  158. this._element = $(options.element);
  159. this.options = options.itemsOptions;
  160. this._eventElement = this._element;
  161. if (this.options.eventElement) {
  162. this._eventElement = this._element.find(this.options.eventElement);
  163. }
  164. this._setValue(options.itemIndex);
  165. }
  166. Switcher.SwitcherItem.prototype = {
  167. _setValue: function(itemIndex){
  168. this._index = itemIndex;
  169. switch (true) {
  170. case this.options.valueSource == 'id' || this.options.valueAttribute == 'id':
  171. case this.options.valueSource == 'class' || this.options.valueAttribute == 'class':
  172. case this.options.valueSource == 'attribute' || (this.options.valueAttribute !== undefined && this.options.valueAttribute != ''):
  173. this._value = Switcher.utils.getValueFromAttribute(
  174. this._element,
  175. this.options.valueAttribute || this.options.valueSource,
  176. this.options.valueTemplate || '%'
  177. );
  178. break;
  179. case this.options.valueSource == 'index':
  180. this._value = itemIndex;
  181. break;
  182. }
  183. },
  184. select: function(){
  185. this._element.addClass(this.options.selectedClass);
  186. if(this.options.deselectedClass){
  187. this._element.removeClass(this.options.deselectedClass);
  188. }
  189. },
  190. deselect: function(){
  191. this._element.removeClass(this.options.selectedClass);
  192. if(this.options.deselectedClass){
  193. this._element.addClass(this.options.deselectedClass);
  194. }
  195. },
  196. isSelected: function(){
  197. return this._element.hasClass(this.options.selectedClass);
  198. }
  199. }
  200. Switcher.Targets = function(switcher, options){
  201. if (typeof options == 'string') {
  202. options = {
  203. selector: options
  204. }
  205. }
  206. this.options = options;
  207. this.options.linkTemplate = this.options.linkTemplate || '%';
  208. this.switcher = switcher;
  209. this.options.actionType = (typeof this.switcher.options.action == 'string' ? this.switcher.options.action : this.switcher.options.action.type);
  210. this._findItems(switcher.items);
  211. }
  212. Switcher.Targets.prototype = {
  213. _findItems: function(switcherItems){
  214. this.jItems = $(this.options.selector);
  215. this.oItems = {};
  216. for (var i = 0, len = switcherItems.length; i < len; i++){
  217. this.oItems[switcherItems[i]._value] = this._getItemsByValue(switcherItems[i]._value);
  218. }
  219. },
  220. _getItemsByValue: function(value){
  221. var selector = this.options.linkTemplate.replace(/%/g, value);
  222. switch (true) {
  223. case this.options.linkSource == 'id' || this.options.linkAttribute == 'id':
  224. return this.jItems.filter('#' + selector);
  225. break;
  226. case this.options.linkSource == 'class' || this.options.linkAttribute == 'class':
  227. return this.jItems.filter('.' + selector);
  228. break;
  229. case this.options.linkSource == 'attribute' || (this.options.linkAttribute !== undefined && this.options.linkAttribute != ''):
  230. return this.jItems.filter('[' + this.options.linkAttribute + '~="' + selector + '"]');
  231. break;
  232. case this.options.linkSource == 'index':
  233. default:
  234. return this.jItems.eq(value);
  235. break;
  236. }
  237. }
  238. }
  239. Switcher.Action = function(switcher, options) {
  240. var action;
  241. switch (typeof options == 'string' ? options : options.type) {
  242. case 'toggle':
  243. action = new Switcher.Action.Toggle(options);
  244. break;
  245. case 'toggleClass':
  246. action = new Switcher.Action.ToggleClass(options);
  247. break;
  248. case 'setValueClass':
  249. action = new Switcher.Action.SetValueClass(options);
  250. break;
  251. case 'fade':
  252. action = new Switcher.Action.Fade(options);
  253. break;
  254. case 'slide':
  255. action = new Switcher.Action.Slide(options);
  256. break;
  257. }
  258. $.extend(this, action);
  259. this.switcher = switcher;
  260. }
  261. Switcher.Action.prototype = {
  262. execute: function() {
  263. this.reverse(this.getTargets(this.switcher.prevSelectedValue), this.switcher.prevSelectedValue);
  264. this.forward(this.getTargets(this.switcher.selectedValue), this.switcher.selectedValue);
  265. },
  266. getTargets: function(value) {
  267. return this.switcher.targets.oItems[value];
  268. },
  269. _reverse: function(value, quick) {
  270. this.reverse(this.getTargets(value), value, quick);
  271. },
  272. _forward: function(value) {
  273. this.forward(this.getTargets(value), value);
  274. }
  275. }
  276. Switcher.Action.Toggle = function() {}
  277. Switcher.Action.Toggle.prototype = {
  278. reverse: function(targets) {
  279. targets && targets.hide();
  280. },
  281. forward: function(targets) {
  282. targets && targets.show();
  283. }
  284. }
  285. Switcher.Action.ToggleClass = function(options) {
  286. this.forwardAddClass = options.addClass || options.forwardAddClass;
  287. this.forwardRemoveClass = options.removeClass || options.forwardRemoveClass;
  288. this.reverseAddClass = options.removeClass || options.reverseAddClass;
  289. this.reverseRemoveClass = options.addClass || options.reverseRemoveClass;
  290. }
  291. Switcher.Action.ToggleClass.prototype = {
  292. reverse: function(targets) {
  293. if (targets && this.reverseRemoveClass) targets.removeClass(this.reverseRemoveClass);
  294. if (targets && this.reverseAddClass) targets.addClass(this.reverseAddClass);
  295. },
  296. forward: function(targets, value) {
  297. if (targets && this.forwardRemoveClass) targets.removeClass(this.forwardRemoveClass);
  298. if (targets && this.forwardAddClass) targets.addClass(this.forwardAddClass);
  299. }
  300. }
  301. Switcher.Action.SetValueClass = function(options){
  302. this.classTemplate = options.classTemplate || '%';
  303. }
  304. Switcher.Action.SetValueClass.prototype = {
  305. reverse: function(targets, value) {
  306. targets && targets.removeClass(this.classTemplate.replace(/%/g, value));
  307. },
  308. forward: function(targets, value) {
  309. targets && targets.addClass(this.classTemplate.replace(/%/g, value));
  310. },
  311. getTargets: function() {
  312. return this.switcher.targets.jItems;
  313. }
  314. }
  315. Switcher.Action.Fade = function(options) {
  316. this.easing = options.fadeEasing;
  317. this.duration = options.fadeDuration;
  318. }
  319. Switcher.Action.Fade.prototype = {
  320. execute: function() {
  321. var oThis = this;
  322. this.reverse(this.getTargets(this.switcher.prevSelectedValue), null, false, function(){
  323. oThis.forward(oThis.getTargets(oThis.switcher.selectedValue));
  324. })
  325. },
  326. reverse: function(targets, value, quick, callback) {
  327. if (targets) {
  328. if (!quick) {
  329. this.switcher._lock();
  330. targets.fadeOut(this.duration, this.easing, callback || $.proxy(this.switcher, "_unlock"));
  331. } else {
  332. targets.hide();
  333. }
  334. }
  335. },
  336. forward: function(targets, value) {
  337. if (targets) {
  338. this.switcher._lock();
  339. targets.fadeIn(this.duration, this.easing, $.proxy(this.switcher, "_unlock"));
  340. }
  341. }
  342. }
  343. Switcher.Action.Slide = function(options) {
  344. this.easing = options.fadeEasing;
  345. this.duration = options.fadeDuration;
  346. }
  347. Switcher.Action.Slide.prototype = {
  348. execute: function() {
  349. var oThis = this;
  350. this.reverse(this.getTargets(this.switcher.prevSelectedValue), null, false, function(){
  351. oThis.forward(oThis.getTargets(oThis.switcher.selectedValue));
  352. })
  353. },
  354. reverse: function(targets, value, quick, callback) {
  355. if (targets) {
  356. if (!quick) {
  357. this.switcher._lock();
  358. targets.slideUp(this.duration, this.easing, callback || $.proxy(this.switcher, "_unlock"));
  359. } else {
  360. targets.hide();
  361. }
  362. }
  363. },
  364. forward: function(targets, value) {
  365. if (targets) {
  366. this.switcher._lock();
  367. targets.slideDown(this.duration, this.easing, $.proxy(this.switcher, "_unlock"));
  368. }
  369. }
  370. }
  371. Switcher.utils = {
  372. extend: function extend(subClass, superClass) {
  373. var F = function() {};
  374. F.prototype = superClass.prototype;
  375. subClass.prototype = new F();
  376. subClass.prototype.constructor = subClass;
  377. subClass.superClass = superClass.prototype;
  378. if(superClass.prototype.constructor == Object.prototype.constructor) {
  379. superClass.prototype.constructor = superClass;
  380. }
  381. },
  382. getValueFromAttribute: function(el, sAttrName, valueTemplate) {
  383. var el = $(el);
  384. if(el.length){
  385. var
  386. aAttrValues = [],
  387. rValue = new RegExp(valueTemplate.replace('%', '(.+)'));
  388. if (sAttrName != 'id'){
  389. aAttrValues = el.attr(sAttrName).split(' ')
  390. } else {
  391. aAttrValues.push(el.attr(sAttrName));
  392. }
  393. for (var i = 0, len = aAttrValues.length; i < len; i++) {
  394. var m = rValue.exec(aAttrValues[i]);
  395. if (m){
  396. return m[1];
  397. }
  398. }
  399. }
  400. return false;
  401. }
  402. }