File indexing completed on 2024-05-12 04:33:31

0001 /*
0002     SPDX-FileCopyrightText: 2018 Intevation GmbH <intevation@intevation.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 /* QJSEngine limits what we can do with the global object in C++, so map Doc into this here. */
0008 {
0009     const props = Object.getOwnPropertyDescriptors(Doc);
0010     for (prop in props) {
0011         Object.defineProperty(this, prop, props[prop]);
0012     }
0013     for (const name of Object.getOwnPropertyNames(Doc)) {
0014         if (typeof Doc[name] === 'function') {
0015             this.__proto__[name] = Doc[name];
0016         }
0017     }
0018 }
0019 
0020 /* Builtin functions for Okular's PDF JavaScript interpretation. */
0021 
0022 /** AFSimple_Calculate
0023  *
0024  * cFunction is a string that identifies the operation.
0025  *           It is one of AVG, SUM, PRD, MIN, MAX
0026  * cFields is an array of the names of the fields used to calculate.
0027  */
0028 function AFSimple_Calculate( cFunction, cFields )
0029 {
0030     var ret = 0;
0031 
0032     if ( cFunction === "PRD" )
0033     {
0034         ret = 1;
0035     }
0036 
0037     for (i = 0; i < cFields.length; i++)
0038     {
0039         var field = Doc.getField( cFields[i] );
0040         var val = util.stringToNumber( field.value );
0041 
0042         if ( cFunction === "SUM" || cFunction === "AVG" )
0043         {
0044             ret += val;
0045         }
0046         else if ( cFunction === "PRD" )
0047         {
0048             ret *= val;
0049         }
0050         else if ( cFunction === "MIN" )
0051         {
0052             if ( i === 0 || val < ret )
0053             {
0054                 ret = val;
0055             }
0056         }
0057         else if ( cFunction === "MAX" )
0058         {
0059             if ( i === 0 || val > ret )
0060             {
0061                 ret = val;
0062             }
0063         }
0064     }
0065 
0066     if ( cFunction === "AVG" )
0067     {
0068         ret /= cFields.length;
0069     }
0070 
0071     event.value = util.numberToString( ret, "g", 32 );
0072 }
0073 
0074 
0075 /** AFNumber_Format
0076  *
0077  * Formats event.value based on parameters.
0078  *
0079  * Parameter description based on Acrobat Help:
0080  *
0081  * nDec is the number of places after the decimal point.
0082  *
0083  * sepStyle is an integer denoting separator style
0084  *          0 => . as decimal separator   , as thousand separators => 1,234.56
0085  *          1 => . as decimal separator     no thousand separators => 1234.56
0086  *          2 => , as decimal separator   . as thousand separators => 1.234,56
0087  *          3 => , as decimal separator     no thousand separators => 1234,56
0088  *
0089  * negStyle is the formatting used for negative numbers: - not implemented.
0090  * 0 = MinusBlack
0091  * 1 = Red
0092  * 2 = ParensBlack
0093  * 3 = ParensRed
0094  *
0095  * currStyle is the currency style - not used.
0096  *
0097  * strCurrency is the currency symbol.
0098  *
0099  * bCurrencyPrepend is true to prepend the currency symbol;
0100  *  false to display on the end of the number.
0101  */
0102 function AFNumber_Format( nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend )
0103 {
0104     if ( !event.value )
0105     {
0106         return;
0107     }
0108 
0109     var ret;
0110     var localized = util.stringToNumber( event.value );
0111 
0112     if ( sepStyle === 2 || sepStyle === 3 )
0113     {
0114         // Use de_DE as the locale for the dot separator format
0115         ret = util.numberToString( localized, "f", nDec, 'de_DE' );
0116 
0117         if ( sepStyle === 3 )
0118         {
0119             // No thousands separators. Remove all dots from the DE format.
0120             ret = ret.replace( /\./g, '' );
0121         }
0122     }
0123     else
0124     {
0125         // Otherwise US
0126         ret = util.numberToString( localized, "f", nDec, 'en_US' );
0127 
0128         if ( sepStyle === 1 )
0129         {
0130             // No thousands separators. Remove all commas from the US format.
0131             ret = ret.replace( /,/g, '' );
0132         }
0133     }
0134 
0135     if ( strCurrency )
0136     {
0137         if ( bCurrencyPrepend )
0138         {
0139             ret = strCurrency + ret;
0140         }
0141         else
0142         {
0143             ret = ret + strCurrency;
0144         }
0145     }
0146 
0147     event.value = ret;
0148 }
0149 
0150 function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend)
0151 {
0152     // TODO
0153     return;
0154 }
0155 
0156 function AFMakeNumber(string)
0157 {
0158     var type = typeof string;
0159     if ( type == "number" )
0160         return string;
0161     if ( type != "string" )
0162         return 0;
0163     return util.stringToNumber( string );
0164 }
0165 
0166 /** AFTime_Format
0167  *
0168  * Formats event.value based on parameters.
0169  *
0170  * Parameter description based on Acrobat Help:
0171  *
0172  * ptf is the number which should be used to format the time, is one of:
0173  * 0 = 24HR_MM [ 14:30 ] 
0174  * 1 = 12HR_MM [ 2:30 PM ] 
0175  * 2 = 24HR_MM_SS [ 14:30:15 ] 
0176  * 3 = 12HR_MM_SS [ 2:30:15 PM ]
0177  */
0178 function AFTime_Format( ptf )
0179 {
0180     if( !event.value )
0181     {
0182         return;
0183     }
0184     var tokens = event.value.split( /\D/ );
0185     var invalidDate = false;
0186 
0187     // Remove empty elements of the array
0188     tokens = tokens.filter(Boolean);
0189 
0190     if( tokens.length < 2 )
0191         invalidDate = true;
0192 
0193     // Check if every number is valid
0194     for( i = 0 ; i < tokens.length ; ++i )
0195     {
0196         if( isNaN( tokens[i] ) )
0197         {
0198             invalidDate = true;
0199             break;
0200         }
0201         switch( i )
0202         {
0203             case 0:
0204             {
0205                 if( tokens[i] > 23 || tokens[i] < 0 )
0206                     invalidDate = true;
0207                 break;
0208             }
0209             case 1:
0210             case 2:
0211             {
0212                 if( tokens[i] > 59 || tokens[i] < 0 )
0213                     invalidDate = true;
0214                 break;
0215             }
0216         }
0217     }
0218     if( invalidDate )
0219     {
0220         event.value = "";
0221         return;
0222     }
0223 
0224     // Make it of lenght 3, since we use hh, mm, ss
0225     while( tokens.length < 3 )
0226         tokens.push( 0 );
0227 
0228     // We get pm string in the user locale to search.
0229     var dummyPm = util.printd( 'ap', new Date( 2018, 5, 11, 23, 11, 11) ).toLocaleLowerCase();
0230     // Add 12 to time if it's PM and less than 12
0231     if( event.value.toLocaleLowerCase().search( dummyPm ) !== -1 && Number( tokens[0] ) < 12 )
0232         tokens[0] = Number( tokens[0] ) + 12;
0233 
0234     // We use a random date, because we only care about time.
0235     var date = new Date( 2019, 7, 12, tokens[0], tokens[1], tokens[2] );
0236     var ret;
0237     switch( ptf )
0238     {
0239         case 0:
0240             ret = util.printd( "hh:MM", date );
0241             break;
0242         case 1:
0243             ret = util.printd( "h:MM ap", date );
0244             break;
0245         case 2:
0246             ret = util.printd( "hh:MM:ss", date );
0247             break;
0248         case 3:
0249             ret = util.printd( "h:MM:ss ap", date );
0250             break;
0251     }
0252     event.value = ret;
0253 }
0254 
0255 /** AFTime_Keystroke
0256  *
0257  * Checks if the string in event.value is valid. Not used.
0258  */
0259 function AFTime_Keystroke( ptf )
0260 {
0261     return;
0262 }
0263 
0264 /** AFSpecial_Format
0265  * psf is the type of formatting to use: 
0266  * 0 = zip code
0267  * 1 = zip + 4 
0268  * 2 = phone 
0269  * 3 = SSN
0270  *
0271  * These are all in the US format.
0272 */
0273 function AFSpecial_Format( psf )
0274 {
0275     if( !event.value || psf == 0 )
0276     {
0277         return;
0278     }
0279 
0280     var ret = event.value;
0281 
0282     if( psf === 1 )
0283         ret = ret.substr( 0, 5 ) + '-' + ret.substr( 5, 4 );
0284 
0285     else if( psf === 2 )
0286         ret = '(' + ret.substr( 0, 3 ) + ') ' + ret.substr( 3, 3 ) + '-' + ret.substr( 6, 4 );
0287 
0288     else if( psf === 3 )
0289         ret = ret.substr( 0, 3 ) + '-' + ret.substr( 3, 2 ) + '-' + ret.substr( 5, 4 );
0290 
0291     event.value = ret;
0292 } 
0293 
0294 /** AFSpecial_Keystroke
0295  *
0296  * Checks if the String in event.value is valid.
0297  *
0298  * Parameter description based on Acrobat Help:
0299  *
0300  * psf is the type of formatting to use: 
0301  * 0 = zip code
0302  * 1 = zip + 4 
0303  * 2 = phone 
0304  * 3 = SSN
0305  *
0306  * These are all in the US format. We check to see if only numbers are inserted and the length of the string.
0307 */
0308 function AFSpecial_Keystroke( psf )
0309 {
0310     if ( !event.value )
0311     {
0312         return;
0313     }
0314 
0315     var str = event.value;
0316     if( psf === 0 )
0317     {
0318         if( str.length > 5 )
0319         {
0320             event.rc = false;
0321             return;
0322         }
0323     }
0324 
0325     else if( psf === 1 || psf === 3 )
0326     {
0327         if( str.length > 9 )
0328         {
0329             event.rc = false;
0330             return;
0331         }
0332     }
0333 
0334     else if( psf === 2 )
0335     {
0336         if( str.length > 10 )
0337         {
0338             event.rc = false;
0339             return;
0340         }
0341     }
0342 
0343     for( i = 0 ; i < str.length ; ++i )
0344     {
0345         if( !( str[i] <= '9' && str[i] >= '0' ) )
0346         {
0347             event.rc = false;
0348             return;
0349         }
0350     }
0351 }
0352 
0353 function AFPercent_Keystroke( nDec, sepStyle )
0354 {
0355     if (event.willCommit) {
0356         event.rc = true
0357     } else {
0358         // Allow only numbers plus possible separators
0359         // TODO disallow too many separators
0360         // TODO Make separator locale-dependen/use sepStyle properly
0361         event.rc = !isNaN(event.change) || event.change == "." || event.change == ","
0362     }
0363 }
0364 
0365 app.popUpMenuEx = function() {
0366     return app.okular_popUpMenuEx(arguments);
0367 }
0368 
0369 app.popUpMenu = function() {
0370     // Convert arguments like this:
0371     //   app.popUpMenu(["Fruits","Apples","Oranges"], "-","Beans","Corn");
0372     // into this:
0373     //   app.popUpMenuEx(
0374     //     {cName:"Fruits", oSubMenu:[
0375     //       {cName:"Apples"},
0376     //       {cName:"Oranges"}
0377     //     ]},
0378     //     {cName:"-"},
0379     //     {cName:"Beans"},
0380     //     {cName:"Corn"}
0381     //   );
0382     function convertArgument(arg) {
0383         var exArguments = [];
0384 
0385         for (element of arg) {
0386             var newElement = null;
0387 
0388             if (Array.isArray(element) && element.length > 0) {
0389                 newElement = {
0390                     cName: element[0],
0391                     oSubMenu: convertArgument(element.slice(1))
0392                 };
0393             } else if (!Array.isArray(element)) {
0394                 newElement = {
0395                     cName: element
0396                 };
0397             }
0398 
0399             if (newElement !== null)
0400                 exArguments.push(newElement);
0401         }
0402 
0403         return exArguments;
0404     }
0405 
0406     var exArguments = convertArgument(arguments);
0407     var result =  app.okular_popUpMenuEx(exArguments);
0408     return result;
0409 }