util.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. // Copyright (C) 2004-2022 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. Error.prototype.toString = function() {
  23. if (this.stackTrace) return this.name + ': ' + this.message + this.stackTrace;
  24. return this.name + ': ' + this.message;
  25. };
  26. // display must be kept in sync with an enum in pdf_form.c
  27. var display = {
  28. visible: 0,
  29. hidden: 1,
  30. noPrint: 2,
  31. noView: 3,
  32. };
  33. var border = {
  34. b: 'beveled',
  35. d: 'dashed',
  36. i: 'inset',
  37. s: 'solid',
  38. u: 'underline',
  39. };
  40. var color = {
  41. transparent: [ 'T' ],
  42. black: [ 'G', 0 ],
  43. white: [ 'G', 1 ],
  44. gray: [ 'G', 0.5 ],
  45. ltGray: [ 'G', 0.75 ],
  46. dkGray: [ 'G', 0.25 ],
  47. red: [ 'RGB', 1, 0, 0 ],
  48. green: [ 'RGB', 0, 1, 0 ],
  49. blue: [ 'RGB', 0, 0, 1 ],
  50. cyan: [ 'CMYK', 1, 0, 0, 0 ],
  51. magenta: [ 'CMYK', 0, 1, 0, 0 ],
  52. yellow: [ 'CMYK', 0, 0, 1, 0 ],
  53. };
  54. color.convert = function (c, colorspace) {
  55. switch (colorspace) {
  56. case 'G':
  57. if (c[0] === 'RGB')
  58. return [ 'G', c[1] * 0.3 + c[2] * 0.59 + c[3] * 0.11 ];
  59. if (c[0] === 'CMYK')
  60. return [ 'CMYK', 1 - Math.min(1, c[1] * 0.3 + c[2] * 0.59 + c[3] * 0.11 + c[4])];
  61. break;
  62. case 'RGB':
  63. if (c[0] === 'G')
  64. return [ 'RGB', c[1], c[1], c[1] ];
  65. if (c[0] === 'CMYK')
  66. return [ 'RGB',
  67. 1 - Math.min(1, c[1] + c[4]),
  68. 1 - Math.min(1, c[2] + c[4]),
  69. 1 - Math.min(1, c[3] + c[4]) ];
  70. break;
  71. case 'CMYK':
  72. if (c[0] === 'G')
  73. return [ 'CMYK', 0, 0, 0, 1 - c[1] ];
  74. if (c[0] === 'RGB')
  75. return [ 'CMYK', 1 - c[1], 1 - c[2], 1 - c[3], 0 ];
  76. break;
  77. }
  78. return c;
  79. }
  80. color.equal = function (a, b) {
  81. var i, n;
  82. if (a[0] === 'G')
  83. a = color.convert(a, b[0]);
  84. else
  85. b = color.convert(b, a[0]);
  86. if (a[0] !== b[0])
  87. return false;
  88. switch (a[0]) {
  89. case 'G': n = 1; break;
  90. case 'RGB': n = 3; break;
  91. case 'CMYK': n = 4; break;
  92. default: n = 0; break;
  93. }
  94. for (i = 1; i <= n; ++i)
  95. if (a[i] !== b[i])
  96. return false;
  97. return true;
  98. }
  99. var font = {
  100. Cour: 'Courier',
  101. CourB: 'Courier-Bold',
  102. CourBI: 'Courier-BoldOblique',
  103. CourI: 'Courier-Oblique',
  104. Helv: 'Helvetica',
  105. HelvB: 'Helvetica-Bold',
  106. HelvBI: 'Helvetica-BoldOblique',
  107. HelvI: 'Helvetica-Oblique',
  108. Symbol: 'Symbol',
  109. Times: 'Times-Roman',
  110. TimesB: 'Times-Bold',
  111. TimesBI: 'Times-BoldItalic',
  112. TimesI: 'Times-Italic',
  113. ZapfD: 'ZapfDingbats',
  114. };
  115. var highlight = {
  116. i: 'invert',
  117. n: 'none',
  118. o: 'outline',
  119. p: 'push',
  120. };
  121. var position = {
  122. textOnly: 0,
  123. iconOnly: 1,
  124. iconTextV: 2,
  125. textIconV: 3,
  126. iconTextH: 4,
  127. textIconH: 5,
  128. overlay: 6,
  129. };
  130. var scaleHow = {
  131. proportional: 0,
  132. anamorphic: 1,
  133. };
  134. var scaleWhen = {
  135. always: 0,
  136. never: 1,
  137. tooBig: 2,
  138. tooSmall: 3,
  139. };
  140. var style = {
  141. ch: 'check',
  142. ci: 'circle',
  143. cr: 'cross',
  144. di: 'diamond',
  145. sq: 'square',
  146. st: 'star',
  147. };
  148. var zoomtype = {
  149. fitH: 'FitHeight',
  150. fitP: 'FitPage',
  151. fitV: 'FitVisibleWidth',
  152. fitW: 'FitWidth',
  153. none: 'NoVary',
  154. pref: 'Preferred',
  155. refW: 'ReflowWidth',
  156. };
  157. util.scand = function (fmt, input) {
  158. // This seems to match Acrobat's parsing behavior
  159. return AFParseDateEx(input, fmt);
  160. }
  161. util.printd = function (fmt, date) {
  162. var monthName = [
  163. 'January',
  164. 'February',
  165. 'March',
  166. 'April',
  167. 'May',
  168. 'June',
  169. 'July',
  170. 'August',
  171. 'September',
  172. 'October',
  173. 'November',
  174. 'December'
  175. ];
  176. var dayName = [
  177. 'Sunday',
  178. 'Monday',
  179. 'Tuesday',
  180. 'Wednesday',
  181. 'Thursday',
  182. 'Friday',
  183. 'Saturday'
  184. ];
  185. if (fmt === 0)
  186. fmt = 'D:yyyymmddHHMMss';
  187. else if (fmt === 1)
  188. fmt = 'yyyy.mm.dd HH:MM:ss';
  189. else if (fmt === 2)
  190. fmt = 'm/d/yy h:MM:ss tt';
  191. if (!date)
  192. date = new Date();
  193. else if (!(date instanceof Date))
  194. date = new Date(date);
  195. var tokens = fmt.match(/(\\.|m+|d+|y+|H+|h+|M+|s+|t+|[^\\mdyHhMst]*)/g);
  196. var out = '';
  197. for (var i = 0; i < tokens.length; ++i) {
  198. var token = tokens[i];
  199. switch (token) {
  200. case 'mmmm': out += monthName[date.getMonth()]; break;
  201. case 'mmm': out += monthName[date.getMonth()].substring(0, 3); break;
  202. case 'mm': out += util.printf('%02d', date.getMonth()+1); break;
  203. case 'm': out += date.getMonth()+1; break;
  204. case 'dddd': out += dayName[date.getDay()]; break;
  205. case 'ddd': out += dayName[date.getDay()].substring(0, 3); break;
  206. case 'dd': out += util.printf('%02d', date.getDate()); break;
  207. case 'd': out += date.getDate(); break;
  208. case 'yyyy': out += date.getFullYear(); break;
  209. case 'yy': out += date.getFullYear() % 100; break;
  210. case 'HH': out += util.printf('%02d', date.getHours()); break;
  211. case 'H': out += date.getHours(); break;
  212. case 'hh': out += util.printf('%02d', (date.getHours()+11)%12+1); break;
  213. case 'h': out += (date.getHours() + 11) % 12 + 1; break;
  214. case 'MM': out += util.printf('%02d', date.getMinutes()); break;
  215. case 'M': out += date.getMinutes(); break;
  216. case 'ss': out += util.printf('%02d', date.getSeconds()); break;
  217. case 's': out += date.getSeconds(); break;
  218. case 'tt': out += date.getHours() < 12 ? 'am' : 'pm'; break;
  219. case 't': out += date.getHours() < 12 ? 'a' : 'p'; break;
  220. default: out += (token[0] == '\\') ? token[1] : token; break;
  221. }
  222. }
  223. return out;
  224. }
  225. util.printx = function (fmt, val) {
  226. function toUpper(str) { return str.toUpperCase(); }
  227. function toLower(str) { return str.toLowerCase(); }
  228. function toSame(str) { return str; }
  229. var convertCase = toSame;
  230. var res = '';
  231. var i, m;
  232. var n = fmt ? fmt.length : 0;
  233. for (i = 0; i < n; ++i) {
  234. switch (fmt.charAt(i)) {
  235. case '\\':
  236. if (++i < n)
  237. res += fmt.charAt(i);
  238. break;
  239. case 'X':
  240. m = val.match(/\w/);
  241. if (m) {
  242. res += convertCase(m[0]);
  243. val = val.replace(/^\W*\w/, '');
  244. }
  245. break;
  246. case 'A':
  247. m = val.match(/[A-Za-z]/);
  248. if (m) {
  249. res += convertCase(m[0]);
  250. val = val.replace(/^[^A-Za-z]*[A-Za-z]/, '');
  251. }
  252. break;
  253. case '9':
  254. m = val.match(/\d/);
  255. if (m) {
  256. res += m[0];
  257. val = val.replace(/^\D*\d/, '');
  258. }
  259. break;
  260. case '*':
  261. res += convertCase(val);
  262. val = '';
  263. break;
  264. case '?':
  265. if (val !== '') {
  266. res += convertCase(val.charAt(0));
  267. val = val.substring(1);
  268. }
  269. break;
  270. case '=':
  271. convertCase = toSame;
  272. break;
  273. case '>':
  274. convertCase = toUpper;
  275. break;
  276. case '<':
  277. convertCase = toLower;
  278. break;
  279. default:
  280. res += convertCase(fmt.charAt(i));
  281. break;
  282. }
  283. }
  284. return res;
  285. }
  286. // To the best of my understanding, events are called with:
  287. // if (willCommit == false) {
  288. // event.value = <current value of field>
  289. // event.change = <text selection to drop into the selected area>
  290. // event.selStart = <index of start of selected area, <= 0 means start of string>
  291. // event.selEnd = <index of end of selected area, <= 0 means end of string>
  292. // If the routine can't rationalise the proposed input to something sane it should
  293. // return false, and the caller won't change anything. Otherwise, the routine
  294. // can update value/change/selStart/selEnd as required, and should return true.
  295. // The routine should accept 'partial' values (i.e. values that do not entirely
  296. // fulfill the requirements as they are being typed).
  297. // } else {
  298. // event.value = <proposed value>
  299. // event.change = ''
  300. // event.selStart = -1
  301. // event.selEnd = -1
  302. // The routine can rewrite the proposed value if required (by changing value, not
  303. // change or the selection). It should accept (return 1) or reject (return 0) the
  304. // value it returns.
  305. // }
  306. //
  307. // The following is a helper function to form the proposed 'changed' string that
  308. // various handlers use.
  309. function AFMergeChange(event) {
  310. var prefix, postfix;
  311. var value = event.value;
  312. if (event.willCommit)
  313. return value;
  314. if (event.selStart >= 0)
  315. prefix = value.substring(0, event.selStart);
  316. else
  317. prefix = '';
  318. if (event.selEnd >= 0 && event.selEnd <= value.length)
  319. postfix = value.substring(event.selEnd, value.length);
  320. else
  321. postfix = '';
  322. return prefix + event.change + postfix;
  323. }
  324. function AFExtractNums(string) {
  325. if (string.charAt(0) == '.' || string.charAt(0) == ',')
  326. string = '0' + string;
  327. return string.match(/\d+/g);
  328. }
  329. function AFMakeNumber(string) {
  330. if (typeof string == 'number')
  331. return string;
  332. if (typeof string != 'string')
  333. return null;
  334. var nums = AFExtractNums(string);
  335. if (!nums)
  336. return null;
  337. var result = nums.join('.');
  338. if (string.indexOf('-.') >= 0)
  339. result = '0.' + result;
  340. if (string.indexOf('-') >= 0)
  341. return -result;
  342. return +result;
  343. }
  344. function AFExtractTime(string) {
  345. var pattern = /\d\d?:\d\d?(:\d\d?)?\s*(am|pm)?/i;
  346. var match = pattern.exec(string);
  347. if (match) {
  348. var prefix = string.substring(0, match.index);
  349. var suffix = string.substring(match.index + match[0].length);
  350. return [ prefix + suffix, match[0] ];
  351. }
  352. return null;
  353. }
  354. function AFParseDateOrder(fmt) {
  355. var order = '';
  356. fmt += 'mdy'; // Default order if any parts are missing.
  357. for (var i = 0; i < fmt.length; i++) {
  358. var c = fmt.charAt(i);
  359. if ((c == 'y' || c == 'm' || c == 'd') && order.indexOf(c) < 0)
  360. order += c;
  361. }
  362. return order;
  363. }
  364. function AFMatchMonth(date) {
  365. var names = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];
  366. var month = date.match(/Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/i);
  367. if (month)
  368. return names.indexOf(month[0].toLowerCase()) + 1;
  369. return null;
  370. }
  371. function AFParseTime(string, date) {
  372. if (!date)
  373. date = new Date();
  374. if (!string)
  375. return date;
  376. var nums = AFExtractNums(string);
  377. if (!nums || nums.length < 2 || nums.length > 3)
  378. return null;
  379. var hour = nums[0];
  380. var min = nums[1];
  381. var sec = (nums.length == 3) ? nums[2] : 0;
  382. if (hour < 12 && (/pm/i).test(string))
  383. hour += 12;
  384. if (hour >= 12 && (/am/i).test(string))
  385. hour -= 12;
  386. date.setHours(hour, min, sec);
  387. if (date.getHours() != hour || date.getMinutes() != min || date.getSeconds() != sec)
  388. return null;
  389. return date;
  390. }
  391. function AFMakeDate(out, year, month, date, time)
  392. {
  393. year = year | 0; // force type to integer
  394. if (year < 50)
  395. year += 2000;
  396. if (year < 100)
  397. year += 1900;
  398. out.setFullYear(year, month, date);
  399. if (out.getFullYear() != year || out.getMonth() != month || out.getDate() != date)
  400. return null;
  401. if (time)
  402. out = AFParseTime(time, out);
  403. else
  404. out.setHours(0, 0, 0);
  405. return out;
  406. }
  407. function AFParseDateEx(string, fmt) {
  408. var out = new Date();
  409. var year = out.getFullYear();
  410. var month;
  411. var date;
  412. var i;
  413. out.setHours(12, 0, 0);
  414. var order = AFParseDateOrder(fmt);
  415. var time = AFExtractTime(string);
  416. if (time) {
  417. string = time[0];
  418. time = time[1];
  419. }
  420. var nums = AFExtractNums(string);
  421. if (!nums)
  422. return null;
  423. if (nums.length == 3) {
  424. year = nums[order.indexOf('y')];
  425. month = nums[order.indexOf('m')];
  426. date = nums[order.indexOf('d')];
  427. return AFMakeDate(out, year, month-1, date, time);
  428. }
  429. month = AFMatchMonth(string);
  430. if (nums.length == 2) {
  431. // We have a textual month.
  432. if (month) {
  433. if (order.indexOf('y') < order.indexOf('d')) {
  434. year = nums[0];
  435. date = nums[1];
  436. } else {
  437. year = nums[1];
  438. date = nums[0];
  439. }
  440. }
  441. // Year before date: set year and month.
  442. else if (order.indexOf('y') < order.indexOf('d')) {
  443. if (order.indexOf('y') < order.indexOf('m')) {
  444. year = nums[0];
  445. month = nums[1];
  446. date = 1;
  447. } else {
  448. year = nums[1];
  449. month = nums[0];
  450. date = 1;
  451. }
  452. }
  453. // Date before year: set date and month.
  454. else {
  455. if (order.indexOf('d') < order.indexOf('m')) {
  456. date = nums[0];
  457. month = nums[1];
  458. } else {
  459. date = nums[1];
  460. month = nums[0];
  461. }
  462. }
  463. return AFMakeDate(out, year, month-1, date, time);
  464. }
  465. if (nums.length == 1) {
  466. if (month) {
  467. if (order.indexOf('y') < order.indexOf('d')) {
  468. year = nums[0];
  469. date = 1;
  470. } else {
  471. date = nums[0];
  472. }
  473. return AFMakeDate(out, year, month-1, date, time);
  474. }
  475. // Only one number: must match format exactly!
  476. if (string.length == fmt.length) {
  477. year = month = date = '';
  478. for (i = 0; i < fmt.length; ++i) {
  479. switch (fmt.charAt(i)) {
  480. case '\\': ++i; break;
  481. case 'y': year += string.charAt(i); break;
  482. case 'm': month += string.charAt(i); break;
  483. case 'd': date += string.charAt(i); break;
  484. }
  485. }
  486. return AFMakeDate(out, year, month-1, date, time);
  487. }
  488. }
  489. return null;
  490. }
  491. var AFDate_oldFormats = [
  492. 'm/d',
  493. 'm/d/yy',
  494. 'mm/dd/yy',
  495. 'mm/yy',
  496. 'd-mmm',
  497. 'd-mmm-yy',
  498. 'dd-mm-yy',
  499. 'yy-mm-dd',
  500. 'mmm-yy',
  501. 'mmmm-yy',
  502. 'mmm d, yyyy',
  503. 'mmmm d, yyyy',
  504. 'm/d/yy h:MM tt',
  505. 'm/d/yy HH:MM'
  506. ];
  507. function AFDate_KeystrokeEx(fmt) {
  508. var value = AFMergeChange(event);
  509. if (event.willCommit && !AFParseDateEx(value, fmt)) {
  510. app.alert('The date/time entered ('+value+') does not match the format ('+fmt+') of the field [ '+event.target.name+' ]');
  511. event.rc = false;
  512. }
  513. }
  514. function AFDate_Keystroke(index) {
  515. AFDate_KeystrokeEx(AFDate_oldFormats[index]);
  516. }
  517. function AFDate_FormatEx(fmt) {
  518. var d = AFParseDateEx(event.value, fmt);
  519. event.value = d ? util.printd(fmt, d) : '';
  520. }
  521. function AFDate_Format(index) {
  522. AFDate_FormatEx(AFDate_oldFormats[index]);
  523. }
  524. function AFTime_Keystroke(index) {
  525. if (event.willCommit && !AFParseTime(event.value, null)) {
  526. app.alert('The value entered ('+event.value+') does not match the format of the field [ '+event.target.name+' ]');
  527. event.rc = false;
  528. }
  529. }
  530. function AFTime_FormatEx(fmt) {
  531. var d = AFParseTime(event.value, null);
  532. event.value = d ? util.printd(fmt, d) : '';
  533. }
  534. function AFTime_Format(index) {
  535. var formats = [ 'HH:MM', 'h:MM tt', 'HH:MM:ss', 'h:MM:ss tt' ];
  536. AFTime_FormatEx(formats[index]);
  537. }
  538. function AFSpecial_KeystrokeEx(fmt) {
  539. function toUpper(str) { return str.toUpperCase(); }
  540. function toLower(str) { return str.toLowerCase(); }
  541. function toSame(str) { return str; }
  542. var convertCase = toSame;
  543. var val = AFMergeChange(event);
  544. var res = '';
  545. var i = 0;
  546. var m;
  547. var length = fmt ? fmt.length : 0;
  548. // We always accept reverting to an empty string.
  549. if (!val || val == "") {
  550. event.rc = true;
  551. return;
  552. }
  553. while (i < length) {
  554. // In the !willCommit case, we'll exit nicely if we run out of value.
  555. if (!event.willCommit && (!val || val.length == 0))
  556. break;
  557. switch (fmt.charAt(i)) {
  558. case '\\':
  559. i++;
  560. if (i >= length)
  561. break;
  562. res += fmt.charAt(i);
  563. if (val && val.charAt(0) === fmt.charAt(i))
  564. val = val.substring(1);
  565. break;
  566. case 'X':
  567. m = val.match(/^\w/);
  568. if (!m) {
  569. event.rc = false;
  570. break;
  571. }
  572. res += convertCase(m[0]);
  573. val = val.substring(1);
  574. break;
  575. case 'A':
  576. m = val.match(/^[A-Za-z]/);
  577. if (!m) {
  578. event.rc = false;
  579. break;
  580. }
  581. res += convertCase(m[0]);
  582. val = val.substring(1);
  583. break;
  584. case '9':
  585. m = val.match(/^\d/);
  586. if (!m) {
  587. event.rc = false;
  588. break;
  589. }
  590. res += m[0];
  591. val = val.substring(1);
  592. break;
  593. case '*':
  594. res += convertCase(val);
  595. val = '';
  596. break;
  597. case '?':
  598. res += convertCase(val.charAt(0));
  599. val = val.substring(1);
  600. break;
  601. case '=':
  602. convertCase = toSame;
  603. break;
  604. case '>':
  605. convertCase = toUpper;
  606. break;
  607. case '<':
  608. convertCase = toLower;
  609. break;
  610. default:
  611. res += fmt.charAt(i);
  612. if (val && val.charAt(0) === fmt.charAt(i))
  613. val = val.substring(1);
  614. break;
  615. }
  616. i++;
  617. }
  618. // If we didn't make it through the fmt string then this is a failure
  619. // in the willCommit case.
  620. if (i < length && event.willCommit)
  621. event.rc = false;
  622. // If there are characters left over in the value, it's not a match.
  623. if (val.length > 0)
  624. event.rc = false;
  625. if (event.rc) {
  626. if (event.willCommit)
  627. event.value = res;
  628. else {
  629. event.change = res;
  630. event.selStart = 0;
  631. event.selEnd = event.value.length;
  632. }
  633. } else
  634. app.alert('The value entered ('+event.value+') does not match the format of the field [ '+event.target.name+' ] should be '+fmt);
  635. }
  636. function AFSpecial_Keystroke(index) {
  637. if (event.willCommit) {
  638. switch (index) {
  639. case 0:
  640. if (!event.value.match(/^\d{5}$/))
  641. event.rc = false;
  642. break;
  643. case 1:
  644. if (!event.value.match(/^\d{5}[-. ]?\d{4}$/))
  645. event.rc = false;
  646. break;
  647. case 2:
  648. if (!event.value.match(/^((\(\d{3}\)|\d{3})[-. ]?)?\d{3}[-. ]?\d{4}$/))
  649. event.rc = false;
  650. break;
  651. case 3:
  652. if (!event.value.match(/^\d{3}[-. ]?\d{2}[-. ]?\d{4}$/))
  653. event.rc = false;
  654. break;
  655. }
  656. if (!event.rc)
  657. app.alert('The value entered ('+event.value+') does not match the format of the field [ '+event.target.name+' ]');
  658. }
  659. }
  660. function AFSpecial_Format(index) {
  661. var res;
  662. if (!event.value)
  663. return;
  664. switch (index) {
  665. case 0:
  666. res = util.printx('99999', event.value);
  667. break;
  668. case 1:
  669. res = util.printx('99999-9999', event.value);
  670. break;
  671. case 2:
  672. res = util.printx('9999999999', event.value);
  673. res = util.printx(res.length >= 10 ? '(999) 999-9999' : '999-9999', event.value);
  674. break;
  675. case 3:
  676. res = util.printx('999-99-9999', event.value);
  677. break;
  678. }
  679. event.value = res ? res : '';
  680. }
  681. function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend) {
  682. var value = AFMergeChange(event);
  683. if (sepStyle & 2) {
  684. if (!value.match(/^[+-]?\d*[,.]?\d*$/))
  685. event.rc = false;
  686. } else {
  687. if (!value.match(/^[+-]?\d*\.?\d*$/))
  688. event.rc = false;
  689. }
  690. if (event.willCommit) {
  691. if (!value.match(/\d/))
  692. event.rc = false;
  693. if (!event.rc)
  694. app.alert('The value entered ('+value+') does not match the format of the field [ '+event.target.name+' ]');
  695. }
  696. }
  697. function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend) {
  698. var value = AFMakeNumber(event.value);
  699. var fmt = '%,' + sepStyle + '.' + nDec + 'f';
  700. if (value == null) {
  701. event.value = '';
  702. return;
  703. }
  704. if (bCurrencyPrepend)
  705. fmt = strCurrency + fmt;
  706. else
  707. fmt = fmt + strCurrency;
  708. if (value < 0) {
  709. /* negStyle: 0=MinusBlack, 1=Red, 2=ParensBlack, 3=ParensRed */
  710. value = Math.abs(value);
  711. if (negStyle == 2 || negStyle == 3)
  712. fmt = '(' + fmt + ')';
  713. else if (negStyle == 0)
  714. fmt = '-' + fmt;
  715. if (negStyle == 1 || negStyle == 3)
  716. event.target.textColor = color.red;
  717. else
  718. event.target.textColor = color.black;
  719. } else {
  720. event.target.textColor = color.black;
  721. }
  722. event.value = util.printf(fmt, value);
  723. }
  724. function AFPercent_Keystroke(nDec, sepStyle) {
  725. AFNumber_Keystroke(nDec, sepStyle, 0, 0, '', true);
  726. }
  727. function AFPercent_Format(nDec, sepStyle) {
  728. var val = AFMakeNumber(event.value);
  729. if (val == null) {
  730. event.value = '';
  731. return;
  732. }
  733. event.value = (val * 100) + '';
  734. AFNumber_Format(nDec, sepStyle, 0, 0, '%', false);
  735. }
  736. function AFSimple_Calculate(op, list) {
  737. var i, res;
  738. switch (op) {
  739. case 'SUM': res = 0; break;
  740. case 'PRD': res = 1; break;
  741. case 'AVG': res = 0; break;
  742. }
  743. if (typeof list === 'string')
  744. list = list.split(/ *, */);
  745. for (i = 0; i < list.length; i++) {
  746. var field = this.getField(list[i]);
  747. var value = Number(field.value);
  748. switch (op) {
  749. case 'SUM': res += value; break;
  750. case 'PRD': res *= value; break;
  751. case 'AVG': res += value; break;
  752. case 'MIN': if (i === 0 || value < res) res = value; break;
  753. case 'MAX': if (i === 0 || value > res) res = value; break;
  754. }
  755. }
  756. if (op === 'AVG')
  757. res /= list.length;
  758. event.value = res;
  759. }
  760. function AFRange_Validate(lowerCheck, lowerLimit, upperCheck, upperLimit) {
  761. if (upperCheck && event.value > upperLimit)
  762. event.rc = false;
  763. if (lowerCheck && event.value < lowerLimit)
  764. event.rc = false;
  765. if (!event.rc) {
  766. if (lowerCheck && upperCheck)
  767. app.alert(util.printf('The entered value ('+event.value+') must be greater than or equal to %s and less than or equal to %s', lowerLimit, upperLimit));
  768. else if (lowerCheck)
  769. app.alert(util.printf('The entered value ('+event.value+') must be greater than or equal to %s', lowerLimit));
  770. else
  771. app.alert(util.printf('The entered value ('+event.value+') must be less than or equal to %s', upperLimit));
  772. }
  773. }
  774. // Create Doc.info proxy object.
  775. function mupdf_createInfoProxy(doc) {
  776. doc.info = {
  777. get Title() { return doc.title; },
  778. set Title(value) { doc.title = value; },
  779. get Author() { return doc.author; },
  780. set Author(value) { doc.author = value; },
  781. get Subject() { return doc.subject; },
  782. set Subject(value) { doc.subject = value; },
  783. get Keywords() { return doc.keywords; },
  784. set Keywords(value) { doc.keywords = value; },
  785. get Creator() { return doc.creator; },
  786. set Creator(value) { doc.creator = value; },
  787. get Producer() { return doc.producer; },
  788. set Producer(value) { doc.producer = value; },
  789. get CreationDate() { return doc.creationDate; },
  790. set CreationDate(value) { doc.creationDate = value; },
  791. get ModDate() { return doc.modDate; },
  792. set ModDate(value) { doc.modDate = value; },
  793. };
  794. }
  795. mupdf_createInfoProxy(global);
  796. /* Compatibility ECMAScript functions */
  797. String.prototype.substr = function (start, length) {
  798. if (start < 0)
  799. start = this.length + start;
  800. if (length === undefined)
  801. return this.substring(start, this.length);
  802. return this.substring(start, start + length);
  803. }
  804. Date.prototype.getYear = Date.prototype.getFullYear;
  805. Date.prototype.setYear = Date.prototype.setFullYear;
  806. Date.prototype.toGMTString = Date.prototype.toUTCString;
  807. app.plugIns = [];
  808. app.viewerType = 'Reader';
  809. app.language = 'ENU';
  810. app.viewerVersion = NaN;
  811. app.execDialog = function () { return 'cancel'; }