PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/gallery-autocomplete/test/ac-plugin.js

https://github.com/nonano/yui3-gallery
JavaScript | 194 lines | 136 code | 20 blank | 38 comment | 17 complexity | 67370831702218388ab2821ff4a45602 MD5 | raw file
  1. YUI.add('ac-plugin', function(Y) {
  2. function ACPlugin () { ACPlugin.superclass.constructor.apply(this, arguments) };
  3. Y.extend(
  4. (Y.Plugin.ACPlugin = Y.augment(ACPlugin, Y.EventTarget)),
  5. Y.Plugin.Base,
  6. { // prototype
  7. initializer : function () {
  8. var self = this,
  9. host = self.get("host");
  10. self.handles = attachHandles(self, host);
  11. // publish events:
  12. // keep it simple
  13. // "query" for when value changes.
  14. // "load" for when data returns.
  15. // "show" for when it's time to show something
  16. // "hide" for when it's time to hide
  17. var defaults = eventDefaultBehavior;
  18. Y.Array.each([
  19. "query",
  20. "load",
  21. "show",
  22. "hide",
  23. "next",
  24. "previous"
  25. ], function (ev) { self.publish("ac:"+ev, {
  26. broadcast : 1,
  27. bubbles : 1,
  28. context : self,
  29. preventable : true,
  30. defaultFn : defaults[ev] || null,
  31. prefix : "ac"
  32. }) }, self);
  33. // manage the browser's autocomplete, since that'll interefere,
  34. // but we need to make sure that we don't prevent pre-filling
  35. // when the user navs back to the page, unless the developer has
  36. // specifically disabled that feature in the markup.
  37. manageBrowserAC(host);
  38. },
  39. destructor : function () {
  40. Y.Array.each(this.handles, function (h) { h.detach() });
  41. },
  42. open : function () { this.fire("ac:show") },
  43. next : function (e) { e.preventDefault(); this.fire("ac:next") },
  44. previous : function (e) { e.preventDefault(); this.fire("ac:previous") },
  45. close : function () { this.fire("ac:hide") }
  46. },
  47. { // statics
  48. NAME : "ACPlugin",
  49. NS : "ac",
  50. ATTRS : {
  51. queryValue : {
  52. // override these in the other AC modules as necessary.
  53. // for instance, the delimited getter could get the cursor location,
  54. // split on the delimiter, and then return the selected one.
  55. // the inline-replacing setter could set-and-select the rest of the word.
  56. getter : function () {
  57. return this.get("host").get("value");
  58. },
  59. setter : function (q) {
  60. this.get("host").set("value", q);
  61. // keep track of what it has been explicitly set to, so that we don't
  62. // try to make a query repeatedly when the user hasn't done anything.
  63. return (this._cachedValue = q);
  64. }
  65. },
  66. // data source object
  67. dataSource : {
  68. validator : function (ds) {
  69. // quack.
  70. return ds && Y.Lang.isFunction(ds.sendRequest);
  71. }
  72. },
  73. // minimum number of chars before we'll query
  74. minQueryLength : {
  75. value : 3,
  76. validator : Y.Lang.isNumber
  77. },
  78. // convert a value into a request for the DS
  79. // Can be either a string containg "{query}" somewhere,
  80. // or a function that takes and returns a string.
  81. queryTemplate : {
  82. value : encodeURIComponent,
  83. setter : function (q) {
  84. return (
  85. Y.Lang.isFunction(q) ? q
  86. : function (query) {
  87. // exchange {query} with the query,
  88. // but turn \{query} into {query}, if for some reason that
  89. // string needs to appear in the URL.
  90. return q
  91. .replace(
  92. /(^|[^\\])((\\{2})*)\{query\}/,
  93. '$1$2'+encodeURIComponent(query)
  94. ).replace(
  95. /(^|[^\\])((\\{2})*)\\(\{query\})/,
  96. '$1$2$4'
  97. );
  98. }
  99. );
  100. }
  101. }
  102. } // end attrs
  103. } // end statics
  104. );
  105. // helpers below
  106. function attachHandles (self, host) {
  107. return [
  108. // query on valueChange
  109. Y.on("valueChange", valueChangeHandler, host, self),
  110. // next/open on down
  111. Y.on("key", self.next, host, "down:40", self),
  112. // previous on up
  113. Y.on("key", self.previous, host, "down:38", self),
  114. // close on escape
  115. Y.on("key", self.close, host, "down:27", self)
  116. ];
  117. };
  118. function valueChangeHandler (e) {
  119. var value = e.value;
  120. if (!value) return this.close();
  121. if (value === this._cachedValue || value.length < this.get("minQueryLength")) return;
  122. this._cachedValue = value;
  123. this.fire( "ac:query", { value : e.value });
  124. };
  125. function browserACFixer (domnode) { return function () {
  126. if (domnode) domnode.setAttribute(autocomplete, "on");
  127. domnode = null;
  128. }};
  129. function manageBrowserAC (host) {
  130. // turn off the browser's autocomplete, but take note of it to turn
  131. // it back on later.
  132. var domnode = Y.Node.getDOMNode(host),
  133. autocomplete = "autocomplete",
  134. bac = domnode.getAttribute(autocomplete);
  135. // turn the autocomplete back on so back button works, but only
  136. // if the user hasn't disabled it in the first place.
  137. if ((bac && bac !== "off") || bac === null || bac === undefined) {
  138. var bacf = browserACFixer(domnode);
  139. // hook onto both. Concession to browser craziness.
  140. Y.on("beforeunload", bacf, window);
  141. Y.on("unload", bacf, window);
  142. }
  143. // turn off the browser's autocomplete feature, since that'll interfere.
  144. domnode.setAttribute(autocomplete, "off");
  145. };
  146. function handleQueryResponse (e) {
  147. var res = (e && e.response && e.response.results) ? e.response.results : e;
  148. // if there is a result, and it's not an empty array
  149. if (res && !(res && ("length" in res) && res.length === 0)) this.fire("ac:load", {
  150. results : res,
  151. query : this.get("queryValue")
  152. });
  153. };
  154. var eventDefaultBehavior = {
  155. query : function (e) {
  156. var self = this,
  157. ds = self.get("dataSource"),
  158. query = e.value,
  159. handler = Y.bind(handleQueryResponse, self);
  160. var request = {
  161. request : self.get("queryTemplate")(query),
  162. callback : {
  163. success : handler,
  164. failure : handler
  165. }
  166. };
  167. // if we have a datasource, then make the request.
  168. if (ds) ds.sendRequest(request);
  169. }
  170. };
  171. }, '@VERSION@', {
  172. optional:["event-custom"],
  173. requires:['node', 'plugin', 'value-change', 'event-key']
  174. });