Posted By

damientailhades on 09/20/11


Tagged

js textmate jquery hijax


Versions (?)

jquery_hijax_js_1.0.0


 / Published in: Other
 

  1. /*
  2.  
  3. JQuery Hijax plugin:
  4. ~~~~~~~~~~~~~~~~~~~~
  5. @product JQuery Hijax plugin
  6. @version 1.0.0
  7. @copyright Copyright (c) 2010 Yaron Tadmor
  8. @site http://www.yarontadmor.co.cc/hijax
  9. @license GPL license (http://www.gnu.org/licenses/gpl.html)
  10. @requires jquery.history.js
  11.  
  12.  
  13. Revision History:
  14. ~~~~~~~~~~~~~~~~
  15. 0.9.5 - Fixed bug on IE (extra comma in default option list).
  16. 0.9.6 - Added support for changing title on Ajax
  17. - Added support for proper JS handling in Hijaxed links via _hijax_ready.
  18. - Minor bug fixes for end-cases of IE.
  19. 0.9.7 - Changed title handling so title is in "title" tag (instead of a div)
  20. - Use the content of the source element instead of the element itself
  21. 0.9.8 - Slight modification to hash saving code. Solves bug of going back and forward
  22. to the last page
  23. - Fixed a bug of .live() for [hijax*=] on FireFox
  24. 0.9.9 - Changed _hijax_ready to be an array and not a function.
  25. - Changed regex script, to a more efficient version, due to problems
  26. with FF4 and Chrom 10
  27. - Replace hash delimiter from "/" to ":" to allow paths in URL
  28. 1.0.0 - Replaced dontAjaxInitialPage parameter of init() with initialState.
  29. - Hande sync of multiple hijaxing (clicking a link before ajax operation ended)
  30. NOTE: start/end callbacks must take into account that start might be called before end has ended.
  31. - adde "fail" parameter to endCB.
  32. - Removed formHref. Forms url must distinguish form submit and history load via form params.
  33. - Added support for real hash tags which cause scrolling
  34.  
  35. TODO: - take care of HTML validity issues.
  36. - handle google #!
  37. - Fix real hash support scrolling (scroll only after loaded when history changes)
  38.  
  39. License and Disclaimer:
  40. ~~~~~~~~~~~~~~~~~~~~~~
  41. This software is licensed under the GPL open source license. You may distribute it freely, and
  42. use it in your own software.
  43. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPLICIT OR IMPLIED, INCLUDING BUT
  44. NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  45. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  46. DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
  47. OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  48.  
  49.  
  50.  
  51. * - internal use only
  52.  
  53. trg : {[startCB: <start load function>, ]
  54. [endCB: <end load callback>, ]
  55. url: <url to load>, -
  56. src: <source ID in returned html>,
  57. *[force: <force data reload even if url/src didn't change>,]
  58.  
  59. *[formData: <if form a serialized version of form's data>]
  60.  
  61. }
  62.  
  63.  
  64. targets: { <array of trg objects> }
  65.  
  66. options: { trgs: { <array of trg objects> },
  67. defaults: { src: <default source>, (used for targets with no source reference)
  68. url: <default url>, (used for targets with no URL)
  69. startCB: <default start load function>,
  70. endCB: <default end load function>,
  71. },
  72. defaultTrg: <default target>, (used for links/forms with no target reference at all)
  73. selectionClass: <class to add for selected objects> }
  74.  
  75. */
  76.  
  77.  
  78.  
  79. (function ($) {
  80. // global hijax params
  81. var _init = false;
  82. var _curHash = "*"; // This causes empty hash (initial page) to load default state (since ""!="*")
  83. var _numPenddingAjaxLoads = 0, _pendingRequest = null, _pendingScroll = null;
  84.  
  85.  
  86. // Add hijaxing programmatically to a certain link or form. (can be done by using the hijax attr)
  87. $.fn.hijax = function (targets) {
  88. if (!targets)
  89. targets = {};
  90.  
  91. return this.each (function() {
  92. var $this = $(this);
  93.  
  94. // save the targets passed to us
  95. var locTargets = $.extend (true, {}, targets);
  96. this.hijaxTargets = locTargets;
  97.  
  98. // register the proper event handler.
  99. if (!$this.hasClass ("hijax")) {
  100. if ($this.is ("a"))
  101. $this.click ($.fn.hijax.event);
  102. else if ($this.is ("form"))
  103. $this.submit ($.fn.hijax.event)
  104. }
  105. });
  106. }
  107.  
  108. // handles click on hijaxed link or submit
  109. $.fn.hijax.event = function (e) {
  110. if (!_init)
  111. return;
  112.  
  113. e.preventDefault();
  114.  
  115. // get targets of object, and override current state with object targets
  116. var curStateTargets = $.fn.hijax.parseHash (_curHash, true);
  117. var targets = $.fn.hijax.buildTargets.call (this);
  118. targets = $.extend (true, curStateTargets, targets);
  119.  
  120. _pendingScroll = "";
  121.  
  122. // calc resulting hash
  123. var newHash = $.fn.hijax.buildHash (targets);
  124.  
  125. // save new hash in history, and do ajax calls
  126. // NOTE: Since targets might contain form data which is not saved in hashs, we disable
  127. // plugin for the history load and call do ajax explicitly
  128. _init = false;
  129. $.history.load (newHash);
  130. _init = true;
  131. $.fn.hijax.doAjax (targets, newHash);
  132.  
  133.  
  134. }
  135.  
  136. // handles click on links containing hash only (href="#<element>")
  137. $.fn.hijax.hashEvent = function (e) {
  138. if (!_init)
  139. return;
  140.  
  141. e.preventDefault();
  142.  
  143. // build full hash of link with current state
  144. var $this = $(this);
  145.  
  146. var newHash = _curHash.split ("::");
  147. _pendingScroll = $this.attr('href').substring(1);
  148. newHash[0] = _pendingScroll;
  149. newHash = newHash.join ("::");
  150.  
  151.  
  152. // save new hash in history
  153. if (newHash == _curHash)
  154. // browser won't refresh the history so simulate it.
  155. $.fn.hijax.historyCB (newHash)
  156. else
  157. // (this will trigger doAjax())
  158. $.history.load (newHash);
  159. //$.fn.hijax.doAjax (targets, hash);
  160. }
  161.  
  162.  
  163. $.fn.hijax.buildTargets = function () {
  164. $this = $(this);
  165.  
  166. var targets = this.hijaxTargets;
  167. if (!targets)
  168. targets = {};
  169.  
  170. // get target=source pairs and update (or add) them
  171. var connections = $this.attr("hijax");
  172. if (connections != undefined && connections.length == 0)
  173. connections = undefined;
  174. if (connections != undefined)
  175. connections = connections.split ("&");
  176. for (connection in connections) {
  177. connection = connections[connection];
  178. var split = connection.split ("=");
  179. var trg = split[0];
  180. var src = split[1];
  181. var newTarget = {};
  182. newTarget[trg] = { src: src}
  183. $.extend (true, targets, newTarget);
  184. }
  185.  
  186. // if no targets, use default target
  187. var noTargets = true;
  188. for (t in targets) { noTargets = false; break; }
  189. if (noTargets && $.fn.hijax.options.defaultTrg)
  190. targets[$.fn.hijax.options.defaultTrg] = {};
  191.  
  192. // Call tag specific code to handle targets
  193. if ($this.is ("a"))
  194. targets = $.fn.hijax.buildTargets.a.call (this, targets);
  195. else if ($this.is ("form"))
  196. targets = $.fn.hijax.buildTargets.form.call (this, targets);
  197.  
  198. // add defaults:
  199. for (t in targets)
  200. targets[t] = $.extend (true, {}, $.fn.hijax.options.trgs[t], targets[t]);
  201.  
  202.  
  203. return (targets);
  204. }
  205.  
  206.  
  207. $.fn.hijax.buildTargets.a = function (targets) {
  208. $this = $(this);
  209.  
  210. // get url and set it to all requested targets
  211. var url = $this.attr("href");
  212. for (trg in targets)
  213. $.extend (true, targets[trg], { url: url });
  214. // targets[trg].url = url;
  215.  
  216. return (targets);
  217. }
  218.  
  219.  
  220. $.fn.hijax.buildTargets.form = function (targets) {
  221. $this = $(this);
  222.  
  223. // url, alternative href and form data and set to targets
  224. var url = $this.attr("action");
  225. var formData = $this.serialize();
  226. for (trg in targets)
  227. $.extend (true, targets[trg], { url: url, formData: formData });
  228.  
  229. return (targets);
  230. }
  231.  
  232.  
  233. $.fn.hijax.buildHash = function (targets) {
  234. var hash = "";
  235. for (trg in targets) {
  236. var trgData = targets[trg];
  237.  
  238. // if no sourc element or url available, we can't process the target
  239. if (!trgData.src || !trgData.url)
  240. continue;
  241.  
  242. hash += "::" + trg;
  243. hash += ":" + trgData.src;
  244. hash += ":" + trgData.url;
  245. }
  246.  
  247. return (hash);
  248. }
  249.  
  250. $.fn.hijax.parseHash = function (hash, withDefaults) {
  251. // load the default state
  252. var targets = {};
  253. if (withDefaults)
  254. targets = $.extend (true, {}, $.fn.hijax.options.trgs);
  255.  
  256. // parse pars of hash
  257. parts = hash.split ("::");
  258. parts.shift(); // will always be empty, or not interesting
  259. for (part in parts) {
  260. part = parts[part];
  261. subParts = part.split (":", 3);
  262. var trg = subParts[0];
  263. var src = subParts[1];
  264. var url = subParts[2];
  265.  
  266. var newTarget = {};
  267. newTarget[trg] = {url: url, src: src};
  268. $.extend (true, targets, newTarget);
  269. }
  270.  
  271. return (targets);
  272. }
  273.  
  274.  
  275. $.fn.hijax.doAjax = function (targets, hash) {
  276. // if there are still pending requests, wait until they end.
  277. if (_numPenddingAjaxLoads > 0) {
  278. _pendingRequest = { targets: targets, hash: hash };
  279. return;
  280. }
  281.  
  282.  
  283. // reset pending ajax
  284. _numPenddingAjaxLoads = 0;
  285.  
  286. // get current state
  287. var curTargets = $.fn.hijax.parseHash (_curHash, false);
  288.  
  289. // loop and update all targets
  290. for (trg in targets) {
  291. var trgData = targets[trg];
  292.  
  293. // if target hasn't changed and was not forced, ignore it
  294. var curTrgData = curTargets[trg];
  295. if (curTrgData &&
  296. trgData.force != true &&
  297. trgData.src == curTrgData.src &&
  298. trgData.url == curTrgData.url &&
  299. !trgData.formData)
  300. continue;
  301.  
  302. if (!trgData.src || !trgData.url)
  303. continue;
  304.  
  305.  
  306. // add "ajax" var to URL
  307. var url = trgData.url;
  308. if (url.search (/?/) == -1)
  309. url += "?ajax";
  310. else
  311. url += "&ajax";
  312.  
  313. $.fn.hijax.doAjaxHelper (url, trg, trgData);
  314. }
  315.  
  316. if (_numPenddingAjaxLoads == 0)
  317. $.fn.hijax.allAjaxLoaded();
  318.  
  319. // change state
  320. _curHash = hash;
  321. $.fn.hijax.doSelection(targets);
  322.  
  323. return;
  324. }
  325.  
  326. $.fn.hijax.doAjaxHelper = function (url, trg, trgData) {
  327. _numPenddingAjaxLoads++;
  328.  
  329. // setup local data copies for internal functions
  330. var trgDataLoc = $.extend (true, {}, trgData);
  331. var trgLocal = trg;
  332. var $trgLocal = $("#"+trgLocal);
  333.  
  334. trgDataLoc.startCB.call ($trgLocal, function () {
  335. var ajaxLoader = $.post(url, trgDataLoc.formData)
  336. .success (function (data, status, res) {
  337. var regex_script = /<scriptb[^<]*(?:(?!</script>)<[^<]*)*</script>/gi;
  338. var regex_head = /<headb[^<]*(?:(?!</head>)<[^<]*)*</head>/gi;
  339. var regex_title = /<titleb[^<]*(?:(?!</title>)<[^<]*)*</title>/gi;
  340.  
  341. // extract title data, and remove head
  342. var titleData = data.match (regex_title);
  343. data = data.replace (regex_head, "");
  344.  
  345. // append result to DOM.
  346. if (trgDataLoc.src && trgDataLoc.src.length) {
  347. var $tmpDiv = $("<div />").append(data);
  348. $trgLocal.html ($tmpDiv.find("#"+trgDataLoc.src).contents());
  349. }
  350. else
  351. $trgLocal.html (data);
  352.  
  353.  
  354. // copy title data
  355. var trgTitleAttr = "hijax"+trgLocal;
  356. if (titleData) {
  357. titleData = titleData[0];
  358. $titleData = $(titleData);
  359. var titleText = $titleData.attr("hijaxTitle");
  360. if (titleText)
  361. $("title").attr ("hijaxTitle", titleText);
  362. var trgTitleText = $titleData.attr (trgTitleAttr);
  363. if (trgTitleText)
  364. $("title").attr (trgTitleAttr, trgTitleText);
  365. }
  366.  
  367. // trigger the loaded event for the target
  368. var $trg = $("#"+trgLocal);
  369. $trg.trigger ('hijaxReadyEvent');
  370.  
  371. // setup title
  372. $.fn.hijax.doTitle();
  373.  
  374. // end callback
  375. trgDataLoc.endCB.call($trgLocal, true);
  376. })
  377. .error (function() {
  378. // end callback
  379. trgDataLoc.endCB.call($trgLocal, false);
  380. })
  381. .complete (function() {
  382. _numPenddingAjaxLoads--;
  383. if (_numPenddingAjaxLoads == 0)
  384. $.fn.hijax.allAjaxLoaded();
  385. });
  386. });
  387. }
  388.  
  389. $.fn.hijax.allAjaxLoaded = function ()
  390. {
  391. // if there's a pending request, load it.
  392. if (_pendingRequest) {
  393. var tmp = _pendingRequest;
  394. _pendingRequest = null;
  395. $.fn.hijax.doAjax (tmp.targets, tmp.hash);
  396. }
  397.  
  398. // handle scrolling
  399. if (_pendingScroll) {
  400. if (typeof (_pendingScroll) === typeof ("")) {
  401. if (_pendingScroll.length > 0) {
  402. var $targetElem = $("#"+_pendingScroll);
  403. if ($targetElem[0]) {
  404. var offset = $targetElem.offset();
  405. $('html, body').scrollTop (offset.top);
  406. }
  407. }
  408. }
  409. else {
  410. $('html,body').scrollTop(_pendingScroll)
  411. }
  412.  
  413. _pendingScroll = null;
  414. }
  415.  
  416. }
  417.  
  418.  
  419. // handles adding selection attribute by loaded page
  420. $.fn.hijax.doSelection = function (curTargets) {
  421. $("[hijax*=], [hijaxTargets*=]").each (function() {
  422. var objTargets = $.fn.hijax.buildTargets.call (this);
  423. var match = true;
  424. var hasTargets = false;
  425. for (trg in objTargets) {
  426. hasTargets = true;
  427. if (objTargets[trg].src != curTargets[trg].src ||
  428. objTargets[trg].url != curTargets[trg].url ||
  429. objTargets[trg].formData) {
  430. match = false;
  431. break;
  432. }
  433. }
  434.  
  435. // get object to change
  436. $obj = $(this);
  437. var selectionTarget = $obj.attr("hijaxSelectionTarget");
  438. if (selectionTarget)
  439. var $obj = $obj.closest(selectionTarget);
  440.  
  441. // change selection
  442. if (match && hasTargets)
  443. $obj.addClass ($.fn.hijax.options.selectionClass);
  444. else
  445. $obj.removeClass ($.fn.hijax.options.selectionClass);
  446. });
  447.  
  448. }
  449.  
  450.  
  451. // handles setting the title
  452. $.fn.hijax.doTitle = function () {
  453. var $title = $("title");
  454.  
  455. // If we have hijax title on, parse all targets for the text.
  456. var titleText = $title.attr ("hijaxTitle");
  457. if (titleText) {
  458. for (trg in $.fn.hijax.options.trgs) {
  459. // get text for target
  460. var trgTitleAtr = "hijax"+trg;
  461. var trgTitleTxt = $title.attr (trgTitleAtr);
  462. if (!trgTitleTxt)
  463. continue;
  464.  
  465. // put target's text in title
  466. titleText = titleText.replace ("#"+trg, trgTitleTxt);
  467. }
  468.  
  469. // if we have text for ALL titles, we can use it.
  470. if (titleText.search ("#") == -1)
  471. document.title = titleText;
  472. }
  473.  
  474. }
  475.  
  476.  
  477.  
  478. $.fn.hijax.historyCB = function (hash) {
  479. if (!_init)
  480. return;
  481.  
  482. // save current scroll position
  483. var curScroll = $('body').scrollTop()
  484.  
  485. // get targets by hash
  486. var targets = $.fn.hijax.parseHash (hash, true);
  487. // load the state
  488. $.fn.hijax.doAjax (targets, hash);
  489.  
  490. // remember scroll position, so we can reset it back after loading
  491.  
  492. // restore current scroll position until all ajax is done
  493. if (_numPenddingAjaxLoads > 0) {
  494. _pendingScroll = $('body').scrollTop();
  495. $('html,body').scrollTop(curScroll)
  496. }
  497.  
  498.  
  499. }
  500.  
  501.  
  502. $.hijax = function (options, initialState) {
  503. if (_init)
  504. return;
  505.  
  506. // update options
  507. $.extend (true, $.fn.hijax.options, options || {});
  508.  
  509. // if we have a default target, make sure it's in the trgs.
  510. if ($.fn.hijax.options.defaultTrg) {
  511. var newTarget = {};
  512. newTarget[$.fn.hijax.options.defaultTrg] = { }
  513. $.extend (true, $.fn.hijax.options.trgs, newTarget);
  514. }
  515.  
  516. // init targets with default values if needed
  517. for (trg in $.fn.hijax.options.trgs) {
  518. $.fn.hijax.options.trgs[trg] = $.extend (true, {}, $.fn.hijax.options.defaults, $.fn.hijax.options.trgs[trg])
  519. }
  520.  
  521. // init the history module
  522. if (initialState === true)
  523. initialState = $.extend (true, {}, $.fn.hijax.options.trgs);
  524. if (initialState) {
  525. _curHash = $.fn.hijax.buildHash (initialState);
  526. $.fn.hijax.doSelection (initialState);
  527. }
  528.  
  529.  
  530. _init = true;
  531.  
  532. $.history.init($.fn.hijax.historyCB, { unescape: true });
  533. }
  534.  
  535. // BW compat
  536. $.fn.hijax.init = function (options, initialState) {
  537. alert ("HIJAX ERROR:nCode is written for an older version of the Hijax plugin.n"+
  538. "You must change your site code accordingly:n"+
  539. "- Init function changed from $.fn.hijax.init to $.hijaxn" +
  540. "- formHref and href attribute on form is no longer supported,n" +
  541. " You should make sure your form url knows when form was submittedn" +
  542. " and when it was loaded via history according to form parameters.n" +
  543. "nnPlease see docs for more information.nn");
  544. }
  545.  
  546. $.fn.hijax.defaultStartCB = function (cb) {
  547. cb();
  548. }
  549.  
  550. $.fn.hijax.defaultEndCB = function () {
  551. }
  552.  
  553.  
  554. // basic options so we have call backs.
  555. $.fn.hijax.options = {trgs: {},
  556. defaults: { src: null,
  557. url: null,
  558. startCB: $.fn.hijax.defaultStartCB,
  559. endCB: $.fn.hijax.defaultEndCB },
  560. defaultTrg: undefined,
  561. selectionClass: "selected" };
  562.  
  563.  
  564.  
  565. // Install handler to catch all hijax HTML elements (the *= is to allow empty attribute)
  566. if ($.browser.msie) {
  567. $("a[hijax*=]").live ("click", $.fn.hijax.event);
  568. $("form[hijax*=]").live ("submit", $.fn.hijax.event);
  569. }
  570. else {
  571. $("a[hijax]").live ("click", $.fn.hijax.event);
  572. $("form[hijax]").live ("submit", $.fn.hijax.event);
  573. }
  574.  
  575. // install handlers to catch hash only links
  576. $("a[href^=#]").live ("click", $.fn.hijax.hashEvent);
  577.  
  578.  
  579. }) (jQuery);
  580.  

Report this snippet  

You need to login to post a comment.