js.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. function popWin(url, win, para) {
  6. var win = window.open(url, win, para);
  7. win.focus();
  8. }
  9. function setLocation(url) {
  10. window.location.href = url;
  11. }
  12. function setPLocation(url, setFocus) {
  13. if (setFocus) {
  14. window.opener.focus();
  15. }
  16. window.opener.location.href = url;
  17. }
  18. function setLanguageCode(code, fromCode) {
  19. //TODO: javascript cookies have different domain and path than php cookies
  20. var href = window.location.href;
  21. var after = '',
  22. dash;
  23. if (dash = href.match(/\#(.*)$/)) {
  24. href = href.replace(/\#(.*)$/, '');
  25. after = dash[0];
  26. }
  27. if (href.match(/[?]/)) {
  28. var re = /([?&]store=)[a-z0-9_]*/;
  29. if (href.match(re)) {
  30. href = href.replace(re, '$1' + code);
  31. } else {
  32. href += '&store=' + code;
  33. }
  34. var re = /([?&]from_store=)[a-z0-9_]*/;
  35. if (href.match(re)) {
  36. href = href.replace(re, '');
  37. }
  38. } else {
  39. href += '?store=' + code;
  40. }
  41. if (typeof fromCode != 'undefined') {
  42. href += '&from_store=' + fromCode;
  43. }
  44. href += after;
  45. setLocation(href);
  46. }
  47. /**
  48. * Add classes to specified elements.
  49. * Supported classes are: 'odd', 'even', 'first', 'last'
  50. *
  51. * @param elements - array of elements to be decorated
  52. * [@param decorateParams] - array of classes to be set. If omitted, all available will be used
  53. */
  54. function decorateGeneric(elements, decorateParams) {
  55. var allSupportedParams = ['odd', 'even', 'first', 'last'];
  56. var _decorateParams = {};
  57. var total = elements.length;
  58. if (total) {
  59. // determine params called
  60. if (typeof decorateParams == 'undefined') {
  61. decorateParams = allSupportedParams;
  62. }
  63. if (!decorateParams.length) {
  64. return;
  65. }
  66. for (var k in allSupportedParams) {
  67. _decorateParams[allSupportedParams[k]] = false;
  68. }
  69. for (var k in decorateParams) {
  70. _decorateParams[decorateParams[k]] = true;
  71. }
  72. // decorate elements
  73. // elements[0].addClassName('first'); // will cause bug in IE (#5587)
  74. if (_decorateParams.first) {
  75. Element.addClassName(elements[0], 'first');
  76. }
  77. if (_decorateParams.last) {
  78. Element.addClassName(elements[total - 1], 'last');
  79. }
  80. for (var i = 0; i < total; i++) {
  81. if ((i + 1) % 2 == 0) {
  82. if (_decorateParams.even) {
  83. Element.addClassName(elements[i], 'even');
  84. }
  85. } else if (_decorateParams.odd) {
  86. Element.addClassName(elements[i], 'odd');
  87. }
  88. }
  89. }
  90. }
  91. /**
  92. * Decorate table rows and cells, tbody etc
  93. * @see decorateGeneric()
  94. */
  95. function decorateTable(table, options) {
  96. var table = $(table);
  97. if (table) {
  98. // set default options
  99. var _options = {
  100. 'tbody': false,
  101. 'tbody tr': ['odd', 'even', 'first', 'last'],
  102. 'thead tr': ['first', 'last'],
  103. 'tfoot tr': ['first', 'last'],
  104. 'tr td': ['last']
  105. };
  106. // overload options
  107. if (typeof options != 'undefined') {
  108. for (var k in options) {
  109. _options[k] = options[k];
  110. }
  111. }
  112. // decorate
  113. if (_options['tbody']) {
  114. decorateGeneric(table.select('tbody'), _options['tbody']);
  115. }
  116. if (_options['tbody tr']) {
  117. decorateGeneric(table.select('tbody tr'), _options['tbody tr']);
  118. }
  119. if (_options['thead tr']) {
  120. decorateGeneric(table.select('thead tr'), _options['thead tr']);
  121. }
  122. if (_options['tfoot tr']) {
  123. decorateGeneric(table.select('tfoot tr'), _options['tfoot tr']);
  124. }
  125. if (_options['tr td']) {
  126. var allRows = table.select('tr');
  127. if (allRows.length) {
  128. for (var i = 0; i < allRows.length; i++) {
  129. decorateGeneric(allRows[i].getElementsByTagName('TD'), _options['tr td']);
  130. }
  131. }
  132. }
  133. }
  134. }
  135. /**
  136. * Set "odd", "even" and "last" CSS classes for list items
  137. * @see decorateGeneric()
  138. */
  139. function decorateList(list, nonRecursive) {
  140. if ($(list)) {
  141. if (typeof nonRecursive == 'undefined') {
  142. var items = $(list).select('li');
  143. } else {
  144. var items = $(list).childElements();
  145. }
  146. decorateGeneric(items, ['odd', 'even', 'last']);
  147. }
  148. }
  149. /**
  150. * Set "odd", "even" and "last" CSS classes for list items
  151. * @see decorateGeneric()
  152. */
  153. function decorateDataList(list) {
  154. list = $(list);
  155. if (list) {
  156. decorateGeneric(list.select('dt'), ['odd', 'even', 'last']);
  157. decorateGeneric(list.select('dd'), ['odd', 'even', 'last']);
  158. }
  159. }
  160. /**
  161. * Parse SID and produces the correct URL
  162. */
  163. function parseSidUrl(baseUrl, urlExt) {
  164. var sidPos = baseUrl.indexOf('/?SID=');
  165. var sid = '';
  166. urlExt = urlExt != undefined ? urlExt : '';
  167. if (sidPos > -1) {
  168. sid = '?' + baseUrl.substring(sidPos + 2);
  169. baseUrl = baseUrl.substring(0, sidPos + 1);
  170. }
  171. return baseUrl + urlExt + sid;
  172. }
  173. /**
  174. * Formats currency using patern
  175. * format - JSON (pattern, decimal, decimalsDelimeter, groupsDelimeter)
  176. * showPlus - true (always show '+'or '-'),
  177. * false (never show '-' even if number is negative)
  178. * null (show '-' if number is negative)
  179. */
  180. function formatCurrency(price, format, showPlus) {
  181. var precision = isNaN(format.precision = Math.abs(format.precision)) ? 2 : format.precision;
  182. var requiredPrecision = isNaN(format.requiredPrecision = Math.abs(format.requiredPrecision)) ? 2 : format.requiredPrecision;
  183. //precision = (precision > requiredPrecision) ? precision : requiredPrecision;
  184. //for now we don't need this difference so precision is requiredPrecision
  185. precision = requiredPrecision;
  186. var integerRequired = isNaN(format.integerRequired = Math.abs(format.integerRequired)) ? 1 : format.integerRequired;
  187. var decimalSymbol = format.decimalSymbol == undefined ? ',' : format.decimalSymbol;
  188. var groupSymbol = format.groupSymbol == undefined ? '.' : format.groupSymbol;
  189. var groupLength = format.groupLength == undefined ? 3 : format.groupLength;
  190. var s = '';
  191. if (showPlus == undefined || showPlus == true) {
  192. s = price < 0 ? '-' : showPlus ? '+' : '';
  193. } else if (showPlus == false) {
  194. s = '';
  195. }
  196. var i = parseInt(price = Math.abs(+price || 0).toFixed(precision)) + '';
  197. var pad = i.length < integerRequired ? integerRequired - i.length : 0;
  198. while (pad) {
  199. i = '0' + i; pad--;
  200. }
  201. j = (j = i.length) > groupLength ? j % groupLength : 0;
  202. re = new RegExp('(\\d{' + groupLength + '})(?=\\d)', 'g');
  203. /**
  204. * replace(/-/, 0) is only for fixing Safari bug which appears
  205. * when Math.abs(0).toFixed() executed on "0" number.
  206. * Result is "0.-0" :(
  207. */
  208. var r = (j ? i.substr(0, j) + groupSymbol : '') + i.substr(j).replace(re, '$1' + groupSymbol) + (precision ? decimalSymbol + Math.abs(price - i).toFixed(precision).replace(/-/, 0).slice(2) : '');
  209. var pattern = '';
  210. if (format.pattern.indexOf('{sign}') == -1) {
  211. pattern = s + format.pattern;
  212. } else {
  213. pattern = format.pattern.replace('{sign}', s);
  214. }
  215. return pattern.replace('%s', r).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  216. }
  217. function expandDetails(el, childClass) {
  218. if (Element.hasClassName(el, 'show-details')) {
  219. $$(childClass).each(function (item) {
  220. item.hide();
  221. });
  222. Element.removeClassName(el, 'show-details');
  223. } else {
  224. $$(childClass).each(function (item) {
  225. item.show();
  226. });
  227. Element.addClassName(el, 'show-details');
  228. }
  229. }
  230. // Version 1.0
  231. var isIE = navigator.appVersion.match(/MSIE/) == 'MSIE';
  232. if (!window.Varien)
  233. var Varien = new Object();
  234. Varien.showLoading = function () {
  235. var loader = $('loading-process');
  236. loader && loader.show();
  237. };
  238. Varien.hideLoading = function () {
  239. var loader = $('loading-process');
  240. loader && loader.hide();
  241. };
  242. Varien.GlobalHandlers = {
  243. onCreate: function () {
  244. Varien.showLoading();
  245. },
  246. onComplete: function () {
  247. if (Ajax.activeRequestCount == 0) {
  248. Varien.hideLoading();
  249. }
  250. }
  251. };
  252. Ajax.Responders.register(Varien.GlobalHandlers);
  253. /**
  254. * Quick Search form client model
  255. */
  256. Varien.searchForm = Class.create();
  257. Varien.searchForm.prototype = {
  258. initialize: function (form, field, emptyText) {
  259. this.form = $(form);
  260. this.field = $(field);
  261. this.emptyText = emptyText;
  262. Event.observe(this.form, 'submit', this.submit.bind(this));
  263. Event.observe(this.field, 'focus', this.focus.bind(this));
  264. Event.observe(this.field, 'blur', this.blur.bind(this));
  265. this.blur();
  266. },
  267. submit: function (event) {
  268. if (this.field.value == this.emptyText || this.field.value == '') {
  269. Event.stop(event);
  270. return false;
  271. }
  272. return true;
  273. },
  274. focus: function (event) {
  275. if (this.field.value == this.emptyText) {
  276. this.field.value = '';
  277. }
  278. },
  279. blur: function (event) {
  280. if (this.field.value == '') {
  281. this.field.value = this.emptyText;
  282. }
  283. }
  284. };
  285. Varien.DateElement = Class.create();
  286. Varien.DateElement.prototype = {
  287. initialize: function (type, content, required, format) {
  288. if (type == 'id') {
  289. // id prefix
  290. this.day = $(content + 'day');
  291. this.month = $(content + 'month');
  292. this.year = $(content + 'year');
  293. this.full = $(content + 'full');
  294. this.advice = $(content + 'date-advice');
  295. } else if (type == 'container') {
  296. // content must be container with data
  297. this.day = content.day;
  298. this.month = content.month;
  299. this.year = content.year;
  300. this.full = content.full;
  301. this.advice = content.advice;
  302. } else {
  303. return;
  304. }
  305. this.required = required;
  306. this.format = format;
  307. this.day.addClassName('validate-custom');
  308. this.day.validate = this.validate.bind(this);
  309. this.month.addClassName('validate-custom');
  310. this.month.validate = this.validate.bind(this);
  311. this.year.addClassName('validate-custom');
  312. this.year.validate = this.validate.bind(this);
  313. this.setDateRange(false, false);
  314. this.year.setAttribute('autocomplete', 'off');
  315. this.advice.hide();
  316. },
  317. validate: function () {
  318. var error = false,
  319. day = parseInt(this.day.value, 10) || 0,
  320. month = parseInt(this.month.value, 10) || 0,
  321. year = parseInt(this.year.value, 10) || 0;
  322. if (this.day.value.strip().empty() &&
  323. this.month.value.strip().empty() &&
  324. this.year.value.strip().empty()
  325. ) {
  326. if (this.required) {
  327. error = 'Please enter a date.';
  328. } else {
  329. this.full.value = '';
  330. }
  331. } else if (!day || !month || !year) {
  332. error = 'Please enter a valid full date.';
  333. } else {
  334. var date = new Date,
  335. countDaysInMonth = 0,
  336. errorType = null;
  337. date.setYear(year); date.setMonth(month - 1); date.setDate(32);
  338. countDaysInMonth = 32 - date.getDate();
  339. if (!countDaysInMonth || countDaysInMonth > 31) countDaysInMonth = 31;
  340. if (day < 1 || day > countDaysInMonth) {
  341. errorType = 'day';
  342. error = 'Please enter a valid day (1-%1).';
  343. } else if (month < 1 || month > 12) {
  344. errorType = 'month';
  345. error = 'Please enter a valid month (1-12).';
  346. } else {
  347. if (day % 10 == day) this.day.value = '0' + day;
  348. if (month % 10 == month) this.month.value = '0' + month;
  349. this.full.value = this.format.replace(/%[mb]/i, this.month.value).replace(/%[de]/i, this.day.value).replace(/%y/i, this.year.value);
  350. var testFull = this.month.value + '/' + this.day.value + '/' + this.year.value;
  351. var test = new Date(testFull);
  352. if (isNaN(test)) {
  353. error = 'Please enter a valid date.';
  354. } else {
  355. this.setFullDate(test);
  356. }
  357. }
  358. var valueError = false;
  359. if (!error && !this.validateData()) {//(year<1900 || year>curyear) {
  360. errorType = this.validateDataErrorType;//'year';
  361. valueError = this.validateDataErrorText;//'Please enter a valid year (1900-%d).';
  362. error = valueError;
  363. }
  364. }
  365. if (error !== false) {
  366. if (jQuery.mage.__) {
  367. error = jQuery.mage.__(error);
  368. }
  369. if (!valueError) {
  370. this.advice.innerHTML = error.replace('%1', countDaysInMonth);
  371. } else {
  372. this.advice.innerHTML = this.errorTextModifier(error);
  373. }
  374. this.advice.show();
  375. return false;
  376. }
  377. // fixing elements class
  378. this.day.removeClassName('validation-failed');
  379. this.month.removeClassName('validation-failed');
  380. this.year.removeClassName('validation-failed');
  381. this.advice.hide();
  382. return true;
  383. },
  384. validateData: function () {
  385. var year = this.fullDate.getFullYear();
  386. var date = new Date;
  387. this.curyear = date.getFullYear();
  388. return year >= 1900 && year <= this.curyear;
  389. },
  390. validateDataErrorType: 'year',
  391. validateDataErrorText: 'Please enter a valid year (1900-%1).',
  392. errorTextModifier: function (text) {
  393. return text.replace('%1', this.curyear);
  394. },
  395. setDateRange: function (minDate, maxDate) {
  396. this.minDate = minDate;
  397. this.maxDate = maxDate;
  398. },
  399. setFullDate: function (date) {
  400. this.fullDate = date;
  401. }
  402. };
  403. Varien.DOB = Class.create();
  404. Varien.DOB.prototype = {
  405. initialize: function (selector, required, format) {
  406. var el = $$(selector)[0];
  407. var container = {};
  408. container.day = Element.select(el, '.dob-day input')[0];
  409. container.month = Element.select(el, '.dob-month input')[0];
  410. container.year = Element.select(el, '.dob-year input')[0];
  411. container.full = Element.select(el, '.dob-full input')[0];
  412. container.advice = Element.select(el, '.validation-advice')[0];
  413. new Varien.DateElement('container', container, required, format);
  414. }
  415. };
  416. Varien.dateRangeDate = Class.create();
  417. Varien.dateRangeDate.prototype = Object.extend(new Varien.DateElement(), {
  418. validateData: function () {
  419. var validate = true;
  420. if (this.minDate || this.maxValue) {
  421. if (this.minDate) {
  422. this.minDate = new Date(this.minDate);
  423. this.minDate.setHours(0);
  424. if (isNaN(this.minDate)) {
  425. this.minDate = new Date('1/1/1900');
  426. }
  427. validate = validate && this.fullDate >= this.minDate;
  428. }
  429. if (this.maxDate) {
  430. this.maxDate = new Date(this.maxDate);
  431. this.minDate.setHours(0);
  432. if (isNaN(this.maxDate)) {
  433. this.maxDate = new Date();
  434. }
  435. validate = validate && this.fullDate <= this.maxDate;
  436. }
  437. if (this.maxDate && this.minDate) {
  438. this.validateDataErrorText = 'Please enter a valid date between %s and %s';
  439. } else if (this.maxDate) {
  440. this.validateDataErrorText = 'Please enter a valid date less than or equal to %s';
  441. } else if (this.minDate) {
  442. this.validateDataErrorText = 'Please enter a valid date equal to or greater than %s';
  443. } else {
  444. this.validateDataErrorText = '';
  445. }
  446. }
  447. return validate;
  448. },
  449. validateDataErrorText: 'Date should be between %s and %s',
  450. errorTextModifier: function (text) {
  451. if (this.minDate) {
  452. text = text.sub('%s', this.dateFormat(this.minDate));
  453. }
  454. if (this.maxDate) {
  455. text = text.sub('%s', this.dateFormat(this.maxDate));
  456. }
  457. return text;
  458. },
  459. dateFormat: function (date) {
  460. return date.getMonth() + 1 + '/' + date.getDate() + '/' + date.getFullYear();
  461. }
  462. });
  463. Varien.FileElement = Class.create();
  464. Varien.FileElement.prototype = {
  465. initialize: function (id) {
  466. this.fileElement = $(id);
  467. this.hiddenElement = $(id + '_value');
  468. this.fileElement.observe('change', this.selectFile.bind(this));
  469. },
  470. selectFile: function (event) {
  471. this.hiddenElement.value = this.fileElement.getValue();
  472. }
  473. };
  474. Validation.addAllThese([
  475. ['validate-custom', ' ', function (v, elm) {
  476. return elm.validate();
  477. }]
  478. ]);
  479. Element.addMethods({
  480. getInnerText: function (element) {
  481. element = $(element);
  482. if (element.innerText && !Prototype.Browser.Opera) {
  483. return element.innerText;
  484. }
  485. return element.innerHTML.stripScripts().unescapeHTML().replace(/[\n\r\s]+/g, ' ').strip();
  486. }
  487. });
  488. /*
  489. if (!("console" in window) || !("firebug" in console))
  490. {
  491. var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
  492. "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
  493. window.console = {};
  494. for (var i = 0; i < names.length; ++i)
  495. window.console[names[i]] = function() {}
  496. }
  497. */
  498. /**
  499. * Executes event handler on the element. Works with event handlers attached by Prototype,
  500. * in a browser-agnostic fashion.
  501. * @param element The element object
  502. * @param event Event name, like 'change'
  503. *
  504. * @example fireEvent($('my-input', 'click'));
  505. */
  506. function fireEvent(element, event) {
  507. // dispatch event
  508. var evt = document.createEvent('HTMLEvents');
  509. evt.initEvent(event, true, true); // event type, bubbling, cancelable
  510. return element.dispatchEvent(evt);
  511. }
  512. /**
  513. * Returns more accurate results of floating-point modulo division
  514. * E.g.:
  515. * 0.6 % 0.2 = 0.19999999999999996
  516. * modulo(0.6, 0.2) = 0
  517. *
  518. * @param dividend
  519. * @param divisor
  520. */
  521. function modulo(dividend, divisor) {
  522. var epsilon = divisor / 10000;
  523. var remainder = dividend % divisor;
  524. if (Math.abs(remainder - divisor) < epsilon || Math.abs(remainder) < epsilon) {
  525. remainder = 0;
  526. }
  527. return remainder;
  528. }
  529. /**
  530. * createContextualFragment is not supported in IE9. Adding its support.
  531. */
  532. if (typeof Range != 'undefined' && !Range.prototype.createContextualFragment) {
  533. Range.prototype.createContextualFragment = function (html) {
  534. var frag = document.createDocumentFragment(),
  535. div = document.createElement('div');
  536. frag.appendChild(div);
  537. div.outerHTML = html;
  538. return frag;
  539. };
  540. }
  541. /**
  542. * Convert byte count to float KB/MB format
  543. *
  544. * @param int $bytes
  545. * @return string
  546. */
  547. var byteConvert = function (bytes) {
  548. if (isNaN(bytes)) {
  549. return '';
  550. }
  551. var symbols = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  552. var exp = Math.floor(Math.log(bytes) / Math.log(2));
  553. if (exp < 1) {
  554. exp = 0;
  555. }
  556. var i = Math.floor(exp / 10);
  557. bytes /= Math.pow(2, 10 * i);
  558. if (bytes.toString().length > bytes.toFixed(2).toString().length) {
  559. bytes = bytes.toFixed(2);
  560. }
  561. return bytes + ' ' + symbols[i];
  562. };