home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 April / Chip_2004-04_cd2.bin / program / reader / Data1.cab / Annots.js < prev    next >
Text File  |  2003-07-17  |  43KB  |  1,637 lines

  1. var AcroPlugins = app.plugIns;
  2. var annotsPresent = false;
  3.  
  4. for (var iAcroPlugins = 0; iAcroPlugins < AcroPlugins.length ; iAcroPlugins++)
  5.     if (AcroPlugins[iAcroPlugins].name.toLowerCase() == "annots")
  6.     {
  7.         annotsPresent = true;
  8.         break;
  9.     }
  10.  
  11. if (annotsPresent)
  12. {
  13.     var strsToExport = new Array (
  14.         "IDS_SUM_TITLE1",
  15.         "IDS_SUM_TITLE2",
  16.         "IDS_UNNAMED",
  17.         "IDS_SUM_DATE1",
  18.         "IDS_SUM_DATE2",
  19.         "IDS_SUM_AUTHOR1",
  20.         "IDS_SUM_AUTHOR2",
  21.         "IDS_SUM_SUBJ1",
  22.         "IDS_SUM_SUBJ2",
  23.         "IDS_SUM_LABEL1",
  24.         "IDS_SUM_LABEL2",
  25.         "IDS_SUM_PAGE1",
  26.         "IDS_SUM_PAGE2",
  27.         "IDS_SUM_TYPE1",
  28.         "IDS_SUM_TYPE2",
  29.         "IDS_SUM_SEQ1",
  30.         "IDS_SUM_SEQ2",
  31.         "IDS_SUM_NO_ANNOTS1",
  32.         "IDS_SUM_NO_ANNOTS2",
  33.         "IDS_STORE_WEB_DISCUSSIONS",
  34.         "IDS_STORE_DAVFDF",
  35.         "IDS_STORE_FSFDF",
  36.         "IDS_STORE_DATABASE",
  37.         "IDS_STORE_NONE",
  38.         "IDS_PROGRESS_SUMMARIZE",
  39.         "IDS_PROGRESS_SORTING",
  40.         "IDS_PROGRESS_FETCHING",
  41.         "IDS_PROGRESS_FETCHING_BIG",
  42.         "IDS_PROGRESS_ADDING",
  43.         "IDS_PROGRESS_DELETING",
  44.         "IDS_PROGRESS_CHANGING",
  45.         "IDS_ANNOTS_JS_BUILTIN",
  46.         "IDS_DATE_INDETERMINATE",
  47.         "IDS_EMAIL_PLEASE",
  48.         "IDS_EMAIL_BLANKNAME",
  49.         "IDS_EMAIL_VERIFY",
  50.         "IDS_EMAIL_TITLE",
  51.         "IDS_EMAIL_LABEL",
  52.         "IDS_SEND_FOR_REVIEW_SUBJ",
  53.         "IDS_SEND_FOR_REVIEW_MSG",
  54.         "IDS_SEND_FOR_REVIEW_MSG_LABEL",
  55.         "IDS_SEND_FOR_REVIEW",
  56.         "IDS_SEND_FOR_REVIEW_TITLE",
  57.         "IDS_SEND_FOR_REVIEW_TITLE_NONAME",
  58.         "IDS_SEND_FOR_REVIEW_INST1",
  59.         "IDS_SEND_FOR_REVIEW_INST2",
  60.         "IDS_SEND_FOR_REVIEW_GET_ADDRS_CAPTION",
  61.         "IDS_SEND_FOR_REVIEW_VERSION_ERR",
  62.         "IDS_SEND_FOR_REVIEW_CONFIRM_MSG",
  63.         "IDS_SEND_FOR_REVIEW_CONFIRM_TIP",
  64.         "IDS_SEND_FOR_REVIEW_CONFIRM_TITLE",
  65.         "IDS_SEND_FOR_REVIEW_PROBLEM",
  66.         "IDS_SEND_FOR_REVIEW_NO_RECIPIENT",
  67.         "IDS_SEND_COMMENTS_TO_AUTHOR_TITLE",
  68.         "IDS_SEND_COMMENTS_TO_AUTHOR_TO",
  69.         "IDS_SEND_COMMENTS_TO_AUTHOR_SUBJ",
  70.         "IDS_SEND_COMMENTS_TO_AUTHOR_MSG",
  71.         "IDS_SEND_COMMENTS_TO_AUTHOR_MSG_LABEL",
  72.         "IDS_SEND_COMMENTS_TO_AUTHOR_INST1",
  73.         "IDS_SEND_COMMENTS_TO_AUTHOR_INST2",
  74.         "IDS_SEND_COMMENTS_TO_AUTHOR_CONFIRM",
  75.         "IDS_SEND_COMMENTS_TO_AUTHOR",
  76.         "IDS_COLLAB_TITLE",
  77.         "IDS_ACTION_REVIEW",
  78.         "IDS_STATE_NONE",
  79.         "IDS_STATE_ACCEPT",
  80.         "IDS_STATE_REJECT",
  81.         "IDS_STATE_CANCELLED",
  82.         "IDS_STATE_COMPLETED",
  83.         "IDS_ACTION_COLLAB",
  84.         "IDS_STATE_COLLAB_ACTIVE",
  85.         "IDS_STATE_COLLAB_COMPLETED",
  86.         "IDS_ACTION_MARKED",
  87.         "IDS_STATE_MARKED",
  88.         "IDS_STATE_UNMARKED",
  89.         "IDS_SUM_FOOTER1",
  90.         "IDS_SUM_FOOTER2",
  91.         "IDS_SUM_STATUS_TITLE",
  92.         "IDS_SEND_FOR_REVIEW_DOC_IS_DIRTY",
  93.         "IDS_EMAIL_INVALID",
  94.         "IDS_SEND_COMMENTS_ATTACHMENT",
  95.         "IDS_SEND_FOR_REVIEW_ATTACHMENT"
  96.     );
  97.  
  98.     for(var nToExport = 0; nToExport < strsToExport.length; nToExport++)
  99.     {
  100.         var strID = strsToExport[nToExport];
  101.  
  102.         eval(strID + " = " + app.getString("Annots", strID).toSource());
  103.     }
  104.  
  105.     console.println(IDS_ANNOTS_JS_BUILTIN);
  106.  
  107.     /* for debugging */
  108.     function debugExcept(e)
  109.     {
  110.         if((typeof app._DEBUG != "undefined") && app._DEBUG)
  111.           console.println(e)
  112.     }
  113.  
  114.     /* Sort methods */
  115.     ANSB_None = 0;
  116.     ANSB_Page = 1;
  117.     ANSB_Seq = 2;
  118.     ANSB_Author = 3;
  119.     ANSB_ModDate = 4;
  120.     ANSB_Type = 5;
  121.     ANSB_Subject = 6;
  122.  
  123.     ANFB_ShouldPrint = 0;
  124.     ANFB_ShouldView = 1;
  125.     ANFB_ShouldEdit = 2;
  126.     ANFB_ShouldAppearInPanel = 3;
  127.     ANFB_ShouldSummarize = 4;
  128.     ANFB_ShouldExport = 5;
  129.     ANFB_ShouldCollaborate = 6;
  130.     ANFB_ShouldNone = 7;
  131.  
  132.     /* Field to summary functions by property name */
  133.     ANsums =
  134.     [
  135.     /* None */        function(a){return "*None*";},
  136.     /* Page */        function(a){return IDS_SUM_PAGE1+a.doc.getPageLabel(a.page)+IDS_SUM_PAGE2;},
  137.     /* Sequence */    function(a, s){return IDS_SUM_SEQ1+(s ? s : a.seqNum)+IDS_SUM_SEQ2;},
  138.     /* Author */    function(a){return IDS_SUM_AUTHOR1+a.author+IDS_SUM_AUTHOR2;},
  139.     /* ModDate */    function(a){
  140.         var d = a.modDate; 
  141.         return IDS_SUM_DATE1+ (d ? util.printd(2, a.modDate) : IDS_DATE_INDETERMINATE )+IDS_SUM_DATE2;
  142.         },
  143.     /* Type */        function(a){return IDS_SUM_TYPE1+a.uiType+IDS_SUM_TYPE2;},
  144.     /* Subject */    function(a){
  145.         var s = a.subject;
  146.         return s ? IDS_SUM_SUBJ1+s+IDS_SUM_SUBJ2 : "";
  147.         },
  148.     ];
  149.  
  150.     /* Order of summary fields */
  151.     ANsumorder = [ ANSB_Page, ANSB_Author, ANSB_Subject, ANSB_ModDate ];
  152.  
  153.     /* binary insertion into sorted list */
  154.     function binsert(a, m)
  155.     {
  156.         var nStart = 0, nEnd = a.length - 1;
  157.  
  158.         while(nStart < nEnd)
  159.         {
  160.             var nMid = Math.floor((nStart + nEnd) / 2);
  161.  
  162.             if(m.toString() < a[nMid].toString())
  163.                 nEnd = nMid - 1;
  164.             else
  165.                 nStart = nMid + 1;
  166.         }
  167.         if((nStart < a.length) && (m.toString() >= a[nStart].toString()))
  168.             a.splice(nStart + 1, 0, m);
  169.         else
  170.             a.splice(nStart, 0, m);
  171.     }
  172.  
  173.     /* perform a worst case n log ( n ) sort with status */
  174.     function isort(a, status)
  175.     {
  176.         var i;
  177.         var aNew = new Array();
  178.  
  179.         if(status)
  180.         {
  181.             app.thermometer.begin();
  182.             app.thermometer.duration = a.length;
  183.             app.thermometer.text = status;
  184.         }
  185.         for(i = 0; i < a.length; i++)
  186.         {
  187.             if(status)
  188.                 app.thermometer.value = i;
  189.             binsert(aNew, a[i]);
  190.         }
  191.         if(status)
  192.             app.thermometer.end();
  193.         return aNew;
  194.     }
  195.  
  196.     function ANstateful(annot)
  197.     {
  198.       return annot &&
  199.         typeof annot.state == "object" &&
  200.         typeof annot.state.state != "undefined" &&
  201.         annot.state.state;
  202.     }
  203.  
  204.     function ANsumFlatten(a, m, i, s)
  205.     {
  206.         var result = [];
  207.  
  208.         if(a)
  209.         {
  210.             if(s)
  211.                 /* if we're sorting, sort by creation date */
  212.                 a.sort(function(a,b){
  213.                     return a.creationDate.getTime() - b.creationDate.getTime();
  214.                 });
  215.  
  216.             for(var n = 0; n < a.length; n++)
  217.             {
  218.                 var item = a[n];
  219.  
  220.                 result.push(item); /* push on the item */
  221.                 result.push(i); /* the indent level */
  222.  
  223.                 // don't indent if this one is stateful
  224.                 var sub = ANsumFlatten(m[item.name], m, i + (ANstateful(item) ? 0 : 1), true);
  225.  
  226.                 for(var j = 0; j < sub.length; j++)
  227.                     result.push(sub[j]); /* and the sub stuff */
  228.             }
  229.         }
  230.         return result;
  231.     }
  232.  
  233.     function ANsummAnnot(annot, scale, doc, r, p, seqNum)
  234.     {
  235.       var assoc = true;
  236.  
  237.       r.size = 1 * scale;
  238.  
  239.          if(seqNum && !annot.inReplyTo)
  240.         r.writeText(ANsums[ANSB_Seq](annot, seqNum), doc, annot.page, annot.rect, false, "" + seqNum, annot.containedPopupHeelPoint);
  241.  
  242.          for(j = 0; j < ANsumorder.length; j++)
  243.              if(ANsumorder[j] != p)
  244.              {
  245.                  var s = (ANsums[ANsumorder[j]])(annot);
  246.    
  247.                  if(s)
  248.                  {
  249.                   if(assoc)
  250.                   {
  251.                     assoc = false;
  252.                      if(!annot.inReplyTo && !seqNum)
  253.                     r.writeText(s, doc, annot.page, annot.rect);
  254.                      else
  255.                        r.writeText(s, doc, annot.page);
  256.                   }
  257.                   else
  258.                     r.writeText(s, doc, annot.page);
  259.                  }
  260.              }
  261.  
  262.       var contents = annot.richContents;
  263.  
  264.       if(contents)
  265.       {
  266.           r.style = "DefaultNoteText";
  267.           r.indent({nAmount: 16 * scale, oIcon: annot.uiIcon, color: annot.strokeColor});
  268. // qWR          r.writeText(contents, doc, annot.page);
  269.           r.writeText(contents, doc, annot.page, annot.MEOptions); // [melall]
  270.           r.writeText(" ", doc, annot.page);
  271.           r.outdent(16 * scale);
  272.       }
  273.       else
  274.       {
  275.           r.indent({nAmount: 16 * scale, oIcon: annot.uiIcon, color: annot.strokeColor});
  276.           r.writeText(" ", doc, annot.page);
  277.           r.writeText(" ", doc, annot.page);
  278.           r.outdent(16 * scale);
  279.       }
  280.  
  281.       // Add the state info
  282.       var models = Collab.getStateModels(false);
  283.       for(i = 0; i < models.length; i++)
  284.       {
  285.           var states = annot.getStateInModel(models[i].name);
  286.  
  287.           if(states.length > 0)
  288.           {
  289.               r.writeText("" + IDS_SUM_STATUS_TITLE, doc, annot.page);
  290.  
  291.               for(j = 0; j < states.length; j++)
  292.               {
  293.                 var d = util.printd(2, states[j].modDate);
  294.                 var s = states[j].state;
  295.                 var a = states[j].author;
  296.  
  297.                   r.writeText(a + " " + s + " " + d, doc, annot.page);
  298.             }
  299.           }
  300.       }
  301.     }
  302.  
  303.     function ANsummState(annot, scale, doc, r)
  304.     {
  305.       var contents = annot.richContents;
  306.  
  307.       if(contents)
  308.       {
  309.           r.style = "DefaultNoteText";
  310.           r.size = 1 * scale;
  311.           r.writeText(util.printd(2, annot.modDate), doc, annot.page);
  312.           r.writeText(contents, doc, annot.page);
  313.       }
  314.       r.writeText(" ", doc, annot.page);
  315.     }
  316.  
  317.     /*    qWR - [melall] 12/2001: added the optional alignRight parameter.
  318.         added the annotation ME options to the writeText report method.
  319.      */
  320.     function ANsummarize(doc, title, p, r, dest, fs, print, twoUp, useSeqNum, scale, noAssocDoc, filter, alignRight)
  321.     {    /* Summarize annotations sorted primarily by property p */
  322.         if(!scale)
  323.             scale = 1;
  324.  
  325.         var thermoUp = true;
  326.         app.thermometer.begin();
  327.  
  328.         try
  329.         {
  330.             app.thermometer.text = IDS_PROGRESS_SUMMARIZE;
  331.  
  332.             if(!ANsums[p])
  333.                 p = ANSB_Page;
  334.             if(!title)
  335.                 title = IDS_UNNAMED;
  336.  
  337.             /* make sure we have all annots */
  338.             this.syncAnnotScan();
  339.  
  340.             /* Get all summarizable annots on all pages sorted in the given manner */
  341.             var a = [];
  342.  
  343.             for(var n = 0; n < doc.numPages; n++)
  344.             {
  345.               var a2 = doc.getAnnots(n, p, r, typeof filter == "undefined" ? ANFB_ShouldSummarize : filter);
  346.  
  347.               for(var n2 = 0; a2 && n2 < a2.length; n2++)
  348.               {
  349.                   // If it's hidden, or a state annot (or both) don't show it
  350.                   var curAnnot = a2[n2];
  351.  
  352.                   if(!curAnnot.hidden && (!curAnnot.state || !curAnnot.state.state))
  353.                     a.push(curAnnot);
  354.               }
  355.             }
  356.  
  357.             if(a && a.length > 0) /* Put in thread order */ 
  358.             {
  359.                 app.thermometer.duration = a.length * 4;
  360.  
  361.                 var t = {};
  362.  
  363.                 for(var n = 0; n < a.length; n++, app.thermometer.value = n * 2)
  364.                 {
  365.                     var item = a[n];
  366.  
  367.                     if(!t[item.inReplyTo])
  368.                         t[item.inReplyTo] = [ item ];
  369.                     else
  370.                         t[item.inReplyTo].push(item);
  371.                 }
  372.  
  373.  
  374.                 /* don't sort the top level 'cuz it's already sorted */
  375.                 a = ANsumFlatten(t[""], t, 0, false);
  376.  
  377.                 /* make the indents differential */
  378.                 for(var j = a.length - 1; j > 2; j -= 2)
  379.                     a[j] -= a[j - 2];
  380.             }
  381.  
  382.             var t;
  383.             var r = new Report();
  384.             var assocDoc = noAssocDoc ? null : doc;
  385.  
  386.             if (typeof alignRight != "undefined")    // [melall]
  387.                 r.alignment = alignRight;            // [melall]
  388.  
  389.             r.ignoreAnnotLayers = (filter == ANFB_ShouldNone);
  390.             r.joinAssocs = twoUp;
  391.             r.style = "NoteTitle";
  392.             r.size = 3 * scale;
  393.             t = IDS_SUM_TITLE1 + title + IDS_SUM_TITLE2;
  394.             r.writeText(t);
  395.             r.divide(3.5 * scale);
  396.  
  397.             var i, j, contents;
  398.             var oldHeading;
  399.             var lastAnnotPage;
  400.             var curFooterText = "";
  401.             var seqNum = 1;
  402.  
  403.             if(a && a.length > 0)
  404.             {
  405.               for(i = 0; i < a.length; i += 2)
  406.               {
  407.                 app.thermometer.value = a.length + i;
  408.  
  409.                 // update the indent level
  410.                 var ind = a[i + 1];
  411.                 var curAnnot = a[i];
  412.                 var footerText = "";
  413.  
  414.                 // update the footer
  415.                   r.style = "NoteTitle";
  416.                   r.size = 2 * scale;
  417.                 if((typeof lastAnnotPage != "undefined") && (curAnnot.page != lastAnnotPage))
  418.                 {
  419.                     footerText = curFooterText = "";
  420.                     r.setFooterText();
  421.                     r.breakPage();
  422.                     seqNum = 1;
  423.                 }
  424.  
  425.                 footerText = "\r" + IDS_SUM_FOOTER1 + doc.getPageLabel(curAnnot.page) + IDS_SUM_FOOTER2; 
  426.  
  427.                 if(footerText != curFooterText)
  428.                 {
  429.                     curFooterText = footerText;
  430.                     r.setFooterText(footerText);
  431.                 }
  432.  
  433.                 for(; ind < 0; ind++)
  434.                     { r.outdent(16 * scale); r.outdent(16 * scale); }
  435.                 for(; ind > 0; ind--)
  436.                     { r.indent(16 * scale); r.indent(16 * scale); }
  437.  
  438.                 // maybe do the heading
  439.                   var heading = (ANsums[p])(curAnnot);
  440.                   if(heading != oldHeading)
  441.                   {
  442.                     if(typeof oldHeading != "undefined")
  443.                       r.writeText(" ");
  444.                     r.writeText(heading, assocDoc, curAnnot.page);
  445.                     oldHeading = heading;
  446.                     r.divide();
  447.                   }
  448.  
  449.                 if(useSeqNum)
  450.                     ANsummAnnot(curAnnot, scale, assocDoc, r, p, seqNum);
  451.                 else
  452.                     ANsummAnnot(curAnnot, scale, assocDoc, r, p);
  453.  
  454.                 if(!curAnnot.inReplyTo)
  455.                     seqNum++;
  456.  
  457.                   r.divide(1 * scale);
  458.                   lastAnnotPage = curAnnot.page;
  459.               }
  460.               r.setFooterText();
  461.             }
  462.             else
  463.               r.writeText(IDS_SUM_NO_ANNOTS1 + title + IDS_SUM_NO_ANNOTS2);
  464.  
  465.             if(thermoUp)
  466.             {
  467.                 thermoUp = false;
  468.                 app.thermometer.end();
  469.             }
  470.  
  471.             if (typeof dest != "undefined")
  472.                 r.save(dest, fs);
  473.             else if(print)
  474.                 r.print();
  475.             else
  476.                 r.open(t);
  477.         }
  478.         catch(e)
  479.         {
  480.             app.alert({cMsg: e["message"], oDoc: doc});
  481.         }
  482.         if(thermoUp)
  483.             app.thermometer.end();
  484.  
  485.         return a ? a.length / 2 : 0;
  486.     }
  487. }
  488.  
  489. if(typeof Collab != "undefined")
  490. {
  491.     /* flags used by collaboration
  492.     */
  493.     CBFNiceTableName = 1;
  494.     CBFNiceDBName = 2;
  495.     CBFDBPerDoc = 4;
  496.  
  497.     function CBgetTableDesc(doc, author)
  498.     {
  499.       var frag = Collab.URL2PathFragment(doc.URL);
  500.       var DBName;
  501.       var tableName;
  502.  
  503.       if(doc.collabDBFlags & CBFDBPerDoc)
  504.       {
  505.         DBName = frag;
  506.         tableName = author;
  507.       }
  508.       else
  509.       {
  510.         DBName = "";
  511.         tableName = frag;
  512.       }
  513.  
  514.       if(doc.collabDBFlags & CBFNiceTableName)
  515.         tableName = Collab.hashString(tableName);
  516.       if(doc.collabDBFlags & CBFNiceDBName)
  517.         DBName = Collab.hashString(DBName);
  518.       return {DBName: DBName ? doc.collabDBRoot + DBName : doc.collabDBRoot,
  519.         tableName: tableName,
  520.         URL: doc.URL,
  521.         user: author,
  522.         flags: doc.collabDBFlags};
  523.     }
  524.  
  525.     function CBgetTableConnect(desc)
  526.     {
  527.       var e;
  528.  
  529.       try
  530.       {
  531.         var conn = ADBC.newConnection(desc.DBName);
  532.         var stmt = conn.newStatement();
  533.  
  534.         return {conn: conn,
  535.           stmt: stmt,
  536.           tableName: desc.tableName,
  537.           user: desc.user,
  538.           flags: desc.flags};
  539.       }
  540.       catch(e) { debugExcept(e); return false; }
  541.     }
  542.  
  543.     function CBgetInfo(conn, name)
  544.     {
  545.       var e;
  546.  
  547.       try
  548.       {
  549.         conn.stmt.execute("select CONTENTS from \"" + conn.tableName + "\" where AUTHOR like ?;",
  550.           "~" + name + "~");
  551.         conn.stmt.nextRow();
  552.         return conn.stmt.getColumn("CONTENTS").value;
  553.       }
  554.       catch(e) { debugExcept(e); return false; }
  555.     }
  556.  
  557.     function CBsetInfo(conn, name, value)
  558.     {
  559.       var e;
  560.  
  561.       /* add the field */
  562.       try { return conn.stmt.execute("insert into \"" + conn.tableName + "\" (AUTHOR, CONTENTS) values (?, ?);",
  563.           "~" + name + "~",
  564.           value); }
  565.       catch(e) { debugExcept(e); return false; }
  566.     }
  567.  
  568.     function CBcreateTable(desc)
  569.     {
  570.       var e;
  571.  
  572.       try
  573.       {
  574.         var conn = ADBC.newConnection(desc.DBName);
  575.         var stmt = conn ? conn.newStatement() : null;
  576.  
  577.         /* come up with the SQL query to do it */
  578.         var sql1 = "create table \"" + desc.tableName + "\" (AUTHOR varchar(64), PAGE integer, NAME varchar(64), CONTENTS text, DATA image);";
  579.         var sql2 = "create table \"" + desc.tableName + "\" (AUTHOR varchar(64), PAGE integer, NAME varchar(64), CONTENTS clob, DATA blob);";
  580.  
  581.         var conn = {conn: conn,
  582.           stmt: stmt,
  583.           tableName: desc.tableName,
  584.           user: desc.user,
  585.           flags: desc.flags};
  586.  
  587.         // first try...
  588.         try
  589.         {
  590.           stmt.execute(sql1);
  591.         } catch(e) { debugExcept(e); }
  592.         // second try...
  593.         try
  594.         {
  595.           stmt.execute(sql2);
  596.         } catch(e) { debugExcept(e); }
  597.         // these will throw if the table wasn't created
  598.         CBsetInfo(conn, "URL", desc.URL);
  599.         CBsetInfo(conn, "creator", desc.user);
  600.         return conn;
  601.       }
  602.       /* we failed... */
  603.       catch(e) { debugExcept(e); return false; }
  604.     }
  605.  
  606.     function CBconnect(desc, bDoNotCreate)
  607.     {
  608.       var conn = CBgetTableConnect(desc);
  609.       var e;
  610.  
  611.       /* if we can't get the URL from it, it doesn't exist */
  612.       if(!CBgetInfo(conn, "URL"))
  613.       {
  614.         if (!bDoNotCreate)
  615.           conn = CBcreateTable(desc);
  616.         else
  617.           return false;
  618.       }
  619.  
  620.       /* here it is! */
  621.       return conn;
  622.     }
  623.  
  624.     /* mapping of annot types to data properties */
  625.     CBannotdata =
  626.     {
  627.         FileAttachment:    function(p) { return "FSCosObj" },
  628.         Sound:            function(p) { return "SCosObj" },
  629.         Stamp:            function(p) { return /^\#/.exec(p.AP) ? "APCosObj" : false }
  630.     };
  631.  
  632.     /* returns the data fork for an annot */
  633.     function CBannotData(annot)
  634.     {
  635.       var prop = CBannotdata[annot.type];
  636.       if(prop != null)
  637.       {
  638.           prop = prop(annot);
  639.           var stm = prop ? Collab.cosObj2Stream(annot[prop]) : null;
  640.  
  641.           if(stm && typeof ADBC != "undefined") 
  642.             stm.type = ADBC.SQLT_LONGVARBINARY;
  643.           return stm;
  644.       }
  645.     }
  646.  
  647.     /* sets the data fork of an annot */
  648.     function CBannotSetData(annot, data)
  649.     {
  650.       var prop = CBannotdata[annot.type];
  651.       if(prop)
  652.       {
  653.           prop = prop(annot);
  654.           if(prop) annot[prop] = data;
  655.       }
  656.     }
  657.  
  658.  
  659.     /* recursive function that deletes a reply chain */
  660.     function CBDeleteReplyChain(disc)
  661.     {
  662.         var replies = Discussions.getDiscussions(disc);
  663.  
  664.         if (replies && (replies.length == 1))
  665.         {
  666.             var currentReply = replies[0];
  667.             var looper = 1;
  668.             while (looper)
  669.             {
  670.                 /*
  671.                 ** There better only be one reply 
  672.                 */
  673.                 var saveChild = Discussions.getDiscussions(currentReply);
  674.  
  675.     //            console.println("Delete reply");
  676.                 currentReply.Delete();
  677.  
  678.                 if (saveChild && (saveChild.length == 1))
  679.                     currentReply = saveChild[0];
  680.                 else
  681.                     looper = 0;
  682.             }
  683.         }
  684.  
  685.     }
  686.  
  687.     /* gets the reply chain, stuffs it in a stream */
  688.     /* and then puts it in the annot */
  689.     function CBGetReplyChain(dstAnnot, discussion)
  690.     {
  691.         var discList = Discussions.getDiscussions(discussion);
  692.  
  693.         var cos = Collab.newWrStreamToCosObj();
  694.  
  695.         var data = 0;
  696.         while (discList && (discList.length > 0))
  697.         {
  698.             data = 1;
  699.             cos.write(discList[0].Text);
  700.             //console.println("Write to cos stream " + discList[0].Text.length + " characters");
  701.  
  702.             discList = Discussions.getDiscussions(discList[0]);
  703.         }
  704.  
  705.         if (data == 1)
  706.             CBannotSetData(dstAnnot, cos.getCosObj());
  707.     }
  708.  
  709.     /* get the stream and puts the data as replies */
  710.     function CBPutReplyChain(discussion, bookmark, srcAnnot)
  711.     {
  712.         var cosStream = CBannotData(srcAnnot);
  713.  
  714.         if(cosStream)
  715.         {
  716.             var s = cosStream.read(Collab.wdBlockSize);
  717.  
  718.             while (discussion && (s.length > 0))
  719.             {
  720.                 discussion = Discussions.addDiscussion(discussion, "Data", s, bookmark);
  721.  
  722.                 s = null;
  723.             
  724.                 s = cosStream.read(Collab.wdBlockSize);
  725.             }
  726.         }
  727.     }
  728.  
  729.     /* ADBC based annot enumerator constructor
  730.     */
  731.     function ADBCAnnotEnumerator(parent, sorted)
  732.     {
  733.       /* store away parameters */
  734.       this.parent = parent;
  735.       this.sorted = sorted;
  736.       /* add enumeration method */
  737.       this.next = function()
  738.       {
  739.         var e;
  740.  
  741.         try
  742.         {
  743.           if(!this.conn)
  744.           {
  745.             this.conn = CBconnect(this.parent.desc, true);
  746.             this.conn.stmt.execute("select CONTENTS from \"" + this.parent.desc.tableName + "\" where AUTHOR not like '~%~'" +
  747.               (this.sorted ? " order by PAGE, NAME;" : ";"));
  748.           }
  749.           this.conn.stmt.nextRow();
  750.           return eval(this.conn.stmt.getColumn("CONTENTS").value);
  751.         }
  752.         catch(e) { debugExcept(e); return false; }
  753.       }
  754.     }
  755.  
  756.     function CBStrToLongColumnThing(s)
  757.     {
  758.       return { type: ADBC.SQLT_LONGVARCHAR, value: s, size: s.length };
  759.     }
  760.  
  761.     /* ADBC based annot store constructor
  762.     */
  763.     function ADBCAnnotStore(doc, user)
  764.     {
  765.       this.desc = CBgetTableDesc(doc, user);
  766.       this.enumerate = function(sorted)
  767.       {
  768.         return new ADBCAnnotEnumerator(this, sorted);
  769.       }
  770.       this.complete = function(toComplete)
  771.       {
  772.         var i;
  773.         var conn = CBconnect(this.desc,true);
  774.  
  775.         if (conn) 
  776.             {
  777.           for(i = 0; toComplete && i < toComplete.length; i++)
  778.           {
  779.             if(CBannotdata[toComplete[i].type])
  780.             {
  781.               var e;
  782.   
  783.               try
  784.               {
  785.                 conn.stmt.execute("select DATA from \"" + this.desc.tableName + "\" where PAGE = ? and NAME like ?;",
  786.                   toComplete[i].page, toComplete[i].name);
  787.                 conn.stmt.nextRow();
  788.                 var cos = Collab.newWrStreamToCosObj();
  789.  
  790.                 conn.stmt.getColumn("DATA", ADBC.Binary | ADBC.Stream, cos);
  791.                 CBannotSetData(toComplete[i], cos.getCosObj());
  792.               }
  793.               catch(e) 
  794.               { 
  795.                   debugExcept(e);
  796.                 return false;
  797.               }
  798.             }
  799.               }
  800.         } else return false;
  801.         return true;
  802.       }
  803.       this.update = function(toDelete, toAdd, toUpdate)
  804.       {
  805.         var i;
  806.         var e;
  807.         var conn = CBconnect(this.desc);
  808.         if(conn == null) return false;
  809.  
  810.         for(i = 0; toDelete && i < toDelete.length; i++)
  811.         {
  812.           try
  813.           {
  814.             conn.stmt.execute("delete from \"" + this.desc.tableName + "\" where PAGE = ? and NAME like ?;",
  815.               toDelete[i].page, toDelete[i].name);
  816.           }
  817.           catch(e) 
  818.           { 
  819.               debugExcept(e);
  820.             return false;
  821.           }
  822.         }
  823.         for(i = 0; toAdd && i < toAdd.length; i++)
  824.         {
  825.           try
  826.           {
  827.             conn.stmt.execute("insert into \"" + this.desc.tableName + "\" (AUTHOR, PAGE, NAME, CONTENTS, DATA) values (?, ?, ?, ?, ?);",
  828.               toAdd[i].author, toAdd[i].page, toAdd[i].name, CBStrToLongColumnThing(toAdd[i].toSource()), CBannotData(toAdd[i]));
  829.           }
  830.           catch(e) 
  831.           { 
  832.               debugExcept(e);
  833.             return false;
  834.           }
  835.         }
  836.         for(i = 0; toUpdate&& i < toUpdate.length; i++)
  837.         {
  838.           try
  839.           {      
  840.             conn.stmt.execute("update \"" + this.desc.tableName + "\" set CONTENTS = ?, DATA = ? where PAGE = ? and NAME like ?;",
  841.               CBStrToLongColumnThing(toUpdate[i].toSource()), CBannotData(toUpdate[i]), toUpdate[i].page, toUpdate[i].name);
  842.           }
  843.           catch(e) 
  844.           { 
  845.               debugExcept(e);
  846.             return false;
  847.           }
  848.         }
  849.         return true;
  850.       }
  851.     }
  852.  
  853.     /* Munge an URL such that Web Discussions won't put our data in the discussions pane
  854.     */
  855.     function WDmungeURL(url)
  856.     {
  857.         return url + "/ACData";
  858.     }
  859.  
  860.     /* Web discussions based annot enumerator constructor
  861.     */
  862.     function WDAnnotEnumerator(parent, sorted)
  863.     {
  864.     //  console.println("WDAnnotEnumerator(): Begin");
  865.  
  866.       this.parent = parent;
  867.       this.sorted = sorted;
  868.       this.next = function()
  869.       {
  870.         try
  871.         {
  872.             app.thermometer.begin();
  873.             app.thermometer.text = IDS_PROGRESS_FETCHING;
  874.  
  875.             if(!this.discussions)
  876.             {
  877.                   this.discussions = Discussions.getDiscussions(WDmungeURL(this.parent.doc.URL));
  878.  
  879.                 // always sort as our completion callback relies on a sorted list
  880.                   if(this.discussions)
  881.                   {
  882.                     this.discussions = isort(this.discussions, IDS_PROGRESS_SORTING);
  883.                     app.thermometer.duration = this.discussions.length;
  884.                   }
  885.                   this.index = 0;
  886.             }
  887.  
  888.             // skip non-Acro discussions
  889.             while(this.discussions && this.index < this.discussions.length && this.discussions[this.index] == "[Discussion]")
  890.                   app.thermometer.value = this.index++;
  891.  
  892.               app.thermometer.end();
  893.  
  894.             if(!this.discussions || this.index >= this.discussions.length)
  895.             {
  896.                   return false;
  897.             }
  898.             return eval(this.discussions[this.index++].Text);
  899.            }
  900.  
  901.         catch(e)
  902.         {
  903.               debugExcept(e);
  904.               app.thermometer.end();
  905.             return false;
  906.         }
  907.       } // next
  908.     }
  909.  
  910.     /* Web discussion based annot store constructor
  911.     */
  912.     function WDAnnotStore(doc, user)
  913.     {
  914.     //  console.println("WDAnnotStore(): Begin");
  915.  
  916.       this.doc = doc;
  917.       this.user = user;
  918.       this.enumerate = function(sorted)
  919.       {
  920.     //    console.println("WDAnnotStore.enumerate(): Begin");
  921.         return new WDAnnotEnumerator(this, sorted);
  922.       }
  923.       this.complete = function(toComplete)
  924.       {
  925.     //    console.show();
  926.     //    console.println("WDAnnotStore.toComplete(): Begin");
  927.  
  928.         var i,j;
  929.     //    console.println("get discussions for "+WDmungeURL(this.doc.URL));
  930.         var discussions = Discussions.getDiscussions(WDmungeURL(this.doc.URL));
  931.  
  932.         if (discussions == null) return false;
  933.  
  934.         if (discussions.length) 
  935.         {
  936.             // sort them to perform fast searches
  937.             // JS sort is a SLOW qsort... use our worst case N log ( N )
  938.             discussions = isort(discussions, IDS_PROGRESS_SORTING);
  939.  
  940.             try
  941.             {
  942.                 app.thermometer.begin();
  943.                 app.thermometer.text = IDS_PROGRESS_FETCHING_BIG;
  944.                 app.thermometer.duration = toComplete.length;
  945.  
  946.                 for(i = 0, j = 0; discussions && (i < toComplete.length) && (j < discussions.length); app.thermometer.value = ++i)
  947.                 {
  948.                     // create a string that'll look like the corresponding discussion
  949.                     var discString = Discussions.makeDiscussionString(toComplete[i].page, toComplete[i].name);
  950.  
  951.                     // keep skipping annots while they are "less" than the current one
  952.                     while(discString > discussions[j])
  953.                         j++;
  954.  
  955.                     // if we found it
  956.                     if(discString == discussions[j])
  957.                     {
  958.                         //console.println("found it - Annot to Complete " + i + " is in discussion slot " + j);
  959.                         //console.println("subject "+discussions[j].Subject);
  960.  
  961.                         /*
  962.                         ** We found the discussion, now gather replys which will
  963.                         ** contain the "data" for the stream
  964.                         */
  965.                         if (CBannotdata[toComplete[i].type])
  966.                             CBGetReplyChain(toComplete[i], discussions[j]);
  967.  
  968.                     }
  969.  
  970.                 }
  971.                 app.thermometer.end();
  972.             }
  973.             catch(e)
  974.             {
  975.                   debugExcept(e);
  976.                 app.thermometer.end();
  977.                 return false;
  978.             }
  979.         }
  980.         return true;
  981.       }
  982.       this.update = function(toDelete, toAdd, toUpdate)
  983.       {
  984.           var result = true;
  985.  
  986.     //    console.println("WDAnnotStore.update(): Begin");
  987.  
  988.         // get the list of discussions
  989.     //    console.println("WDAnnotStore.update(): get discussions "+WDmungeURL(this.doc.URL();
  990.         var discussions = Discussions.getDiscussions(WDmungeURL(this.doc.URL));
  991.         var i, j;
  992.  
  993.         // if we got any...
  994.         if(discussions && discussions.length)
  995.         {
  996.     //        console.println("WDAnnotStore.update(): got some " + discussions.length);
  997.             // sort them to perform fast searches
  998.             discussions = isort(discussions, IDS_PROGRESS_SORTING);
  999.  
  1000.             // if we've got any to update
  1001.             if(toUpdate && toUpdate.length)
  1002.             {
  1003.                 try
  1004.                 {
  1005.                     app.thermometer.begin();
  1006.                     app.thermometer.text = IDS_PROGRESS_CHANGING;
  1007.                     app.thermometer.duration = toUpdate.length;
  1008.  
  1009.                     for(i = 0, j = 0; i < toUpdate.length && j < discussions.length; app.thermometer.value = ++i)
  1010.                     {
  1011.                           // create a string that'll look like 
  1012.                         // the corresponding discussion
  1013.                           var discString = Discussions.makeDiscussionString(toUpdate[i].page, toUpdate[i].name);
  1014.  
  1015.                           // keep skipping annots while they are 
  1016.                         // "less" than the current one
  1017.                           while(discString > discussions[j]) j++;
  1018.  
  1019.                           // if we found it
  1020.                           if(discString == discussions[j])
  1021.                           {
  1022.                             // then update it!
  1023.                             CBDeleteReplyChain(discussions[j]);
  1024.                             discussions[j].Delete();
  1025.  
  1026.                             var bookmark = Discussions.makeBookmark(toUpdate[i].page, toUpdate[i].name);
  1027.                             discussions[j] = Discussions.addDiscussion(WDmungeURL(this.doc.URL), "Markup", toUpdate[i].toSource(), bookmark);
  1028.                             CBPutReplyChain(discussions[j], bookmark, toUpdate[i]);
  1029.                             j++;
  1030.                           }
  1031.                     }
  1032.                     app.thermometer.end();
  1033.                 }
  1034.  
  1035.                 catch(e)
  1036.                 {
  1037.                     app.thermometer.end();
  1038.                     return false;
  1039.                 }
  1040.             }
  1041.  
  1042.             // delete is just like update
  1043.             if(toDelete && toDelete.length) 
  1044.             {
  1045.                 try
  1046.                 {
  1047.                     app.thermometer.begin();
  1048.                     app.thermometer.text = IDS_PROGRESS_DELETING;
  1049.                     app.thermometer.duration = toDelete.length;
  1050.                     for(i = 0, j = 0; i < toDelete.length && j < discussions.length; app.thermometer.value = ++i)
  1051.                     {
  1052.                           var discString = Discussions.makeDiscussionString(toDelete[i].page, toDelete[i].name);
  1053.  
  1054.                           while(discString > discussions[j])
  1055.                             j++;
  1056.  
  1057.                           if(discString == discussions[j])
  1058.                           {
  1059.                             CBDeleteReplyChain(discussions[j]);
  1060.                             discussions[j].Delete();
  1061.                             j++;
  1062.                           }
  1063.                     }
  1064.                     app.thermometer.end();
  1065.                 }
  1066.  
  1067.                 catch(e)
  1068.                 {
  1069.                     app.thermometer.end();
  1070.                     return false;
  1071.                 }
  1072.             }
  1073.         }
  1074.         if(toAdd && toAdd.length)
  1075.         {
  1076.             try
  1077.             {
  1078.                 app.thermometer.begin();
  1079.                 app.thermometer.text = IDS_PROGRESS_ADDING;
  1080.                 app.thermometer.duration = toAdd.length;
  1081.  
  1082.                 for(i = 0; toAdd && i < toAdd.length; app.thermometer.value = ++i)
  1083.                 {
  1084.                       var bookmark = Discussions.makeBookmark(toAdd[i].page, toAdd[i].name);
  1085.  
  1086.                       var discussion = Discussions.addDiscussion(WDmungeURL(this.doc.URL), "Markup", toAdd[i].toSource(), bookmark);
  1087.  
  1088.                     if(discussion == null)
  1089.                     {
  1090.                         result = false;
  1091.                         break;
  1092.                     }
  1093.  
  1094.                       if (CBannotdata[toAdd[i].type])
  1095.                     {
  1096.                         CBPutReplyChain(discussion, bookmark, toAdd[i]);
  1097.                     }
  1098.  
  1099.                 }
  1100.                 app.thermometer.end();
  1101.             }
  1102.  
  1103.             catch(e)
  1104.             {
  1105.                 app.thermometer.end();
  1106.                 return false;
  1107.             }
  1108.  
  1109.         }
  1110.         return result;
  1111.       }
  1112.     }
  1113.  
  1114.     /* Set up default annot stores */
  1115.     Collab.addAnnotStore("NONE", IDS_STORE_NONE,
  1116.         {create: function(doc, user, settings){ return null; }});
  1117.     Collab.setStoreNoSettings("NONE", true);
  1118.     if(typeof Discussions != "undefined")
  1119.     {
  1120.       Collab.addAnnotStore("WD", IDS_STORE_WEB_DISCUSSIONS,
  1121.             {create: function(doc, user, settings){ return new WDAnnotStore(doc, user); }});
  1122.         Collab.setStoreNoSettings("WD", true);
  1123.     }
  1124.     if(typeof ADBC != "undefined")
  1125.         Collab.addAnnotStore("DB", IDS_STORE_DATABASE,
  1126.             {create: function(doc, user, settings){ doc.collabDBRoot = settings; doc.collabDBFlags = CBFNiceTableName; return (settings && settings != "") ? new ADBCAnnotStore(doc, user) : null; }});
  1127.     Collab.addAnnotStore("DAVFDF", IDS_STORE_DAVFDF,
  1128.         {create: function(doc, user, settings){ return (settings && settings != "") ? new FSAnnotStore(doc, user, settings + doc.Collab.docID + "/", "CHTTP") : null; }});
  1129.     Collab.addAnnotStore("FSFDF", IDS_STORE_FSFDF,
  1130.         {create: function(doc, user, settings){ return (settings && settings != "") ? new FSAnnotStore(doc, user, settings + doc.Collab.docID + "/") : null; }});
  1131.     Collab.setStoreFSBased("FSFDF", true);
  1132.  
  1133.     // Web Discussion data block size
  1134.     Collab.wdBlockSize = 16384;
  1135.  
  1136.     // Add default state handlers -- this should go in a seperate file.
  1137.     Collab.addStateModel
  1138.     ({
  1139.         cName: "Review", 
  1140.         cUIName: IDS_ACTION_REVIEW, 
  1141.         oStates:
  1142.         { 
  1143.             "None": IDS_STATE_NONE,
  1144.             "Accepted":
  1145.             {
  1146.                 cUIName: IDS_STATE_ACCEPT,
  1147.                 cIconName: "C_Accept_Md_N.png"
  1148.             },
  1149.             "Rejected": 
  1150.             {
  1151.                 cUIName: IDS_STATE_REJECT,
  1152.                 cIconName: "C_Reject_Md_N.png"
  1153.             },
  1154.             "Cancelled": 
  1155.             {
  1156.                 cUIName: IDS_STATE_CANCELLED,
  1157.                 cIconName: "C_Cancel_Md_N.png"
  1158.             },
  1159.             "Completed": 
  1160.             {
  1161.                 cUIName: IDS_STATE_COMPLETED,
  1162.                 cIconName: "C_Complete_Md_N.png"
  1163.             }
  1164.         },
  1165.         cDefault: "None"
  1166.     });
  1167.  
  1168.     Collab.addStateModel
  1169.     ({
  1170.         cName: "CollabStatus", 
  1171.         cUIName: IDS_ACTION_COLLAB,
  1172.         oStates:
  1173.         {
  1174.             "Modified": IDS_STATE_COLLAB_ACTIVE,
  1175.             "Completed": IDS_STATE_COLLAB_COMPLETED
  1176.         },
  1177.         bHidden: true,
  1178.         cDefault: "Modified"
  1179.     });
  1180.  
  1181.     Collab.addStateModel
  1182.     ({
  1183.         cName: "Marked", 
  1184.         cUIName: IDS_ACTION_MARKED,
  1185.         oStates:
  1186.         {
  1187.             "Marked": IDS_STATE_MARKED,
  1188.             "Unmarked": IDS_STATE_UNMARKED
  1189.         },
  1190.         cDefault: "Unmarked",
  1191.         bHidden: true,
  1192.         bHistory: false
  1193.     });
  1194. }
  1195.  
  1196. /* E-mail ad-hoc workflow stuff */
  1197.  
  1198. // Send the current document out for review
  1199. function ANSendForReview(doc)
  1200. {
  1201.     if(!doc.dirty || app.alert({cMsg: IDS_SEND_FOR_REVIEW_DOC_IS_DIRTY, cTitle: IDS_SEND_FOR_REVIEW_TITLE_NONAME, nType: 2, nIcon: 1, oDoc: doc}) == 4)
  1202.     {
  1203.         // is there a valid e-mail address attached to the doc?
  1204.         var eaddr = identity.email;
  1205.         var explicitlyEntered = false;
  1206.  
  1207.         // Make sure the document gets saved if it's readonly
  1208.         if(doc.dirty && !doc.requestPermission(permission.document, permission.remove))
  1209.         {
  1210.             app.execMenuItem("Save");
  1211.             if(doc.dirty)
  1212.                 return 0; // Cancelled
  1213.         }
  1214.  
  1215.         // if not, try to get one
  1216.         if(!eaddr)
  1217.         {
  1218.             var emailRE = /^([a-zA-Z0-9_\-\.\/]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
  1219.  
  1220.             // pop the question
  1221.             do
  1222.             {
  1223.                 eaddr = app.response({    cQuestion: IDS_EMAIL_PLEASE, 
  1224.                                         cTitle: IDS_EMAIL_TITLE, 
  1225.                                         cDefault: IDS_EMAIL_BLANKNAME, 
  1226.                                         cLabel: IDS_EMAIL_LABEL});
  1227.                 if(eaddr != null && !eaddr.match(emailRE))
  1228.                     app.alert({cMsg: IDS_EMAIL_INVALID, cTitle: IDS_SEND_FOR_REVIEW_TITLE_NONAME, nIcon: 1, oDoc: doc});
  1229.             } while(eaddr != null && !eaddr.match(emailRE));
  1230.  
  1231.             // got a response?    pop it into the author info field
  1232.             if(eaddr != null)
  1233.                 identity.email = eaddr;
  1234.         }
  1235.  
  1236.         // shall we?
  1237.         var name;        
  1238.         var match = doc.path.match(/\/([^\/]*$)/);
  1239.  
  1240.         if (match && match.length == 2)
  1241.             name = match[1];
  1242.  
  1243.         if(!name)
  1244.             name = doc.path;
  1245.  
  1246.         // plop it into a localized string
  1247.         name = name.replace(/\.pdf\w*$/i, "");
  1248.         name = IDS_SEND_FOR_REVIEW_ATTACHMENT.replace(/\%pdfbase\%/g, name);
  1249.  
  1250.         if(eaddr != null)
  1251.         {
  1252.             var result;
  1253.             var info;
  1254.             var firstTime = true;
  1255.             var keepTrying = true;
  1256.             var startDate;
  1257.  
  1258.             //
  1259.             // If the submitForm fails, allow the user to correct the situation by 
  1260.             // bringing them back to our email dialog.  To get out of this while 
  1261.             // loop either a successful email must occur or the user must cancel
  1262.             // the dialog. 
  1263.             //
  1264.             while (keepTrying)
  1265.             {
  1266.                 if (firstTime)
  1267.                 {
  1268.                     info = doc.Collab.collectEmailInfo({
  1269.                         subj: IDS_SEND_FOR_REVIEW_SUBJ, 
  1270.                         msg: IDS_SEND_FOR_REVIEW_MSG,
  1271.                         inst1: IDS_SEND_FOR_REVIEW_INST1,
  1272.                         inst2: IDS_SEND_FOR_REVIEW_INST2,
  1273.                         title: IDS_SEND_FOR_REVIEW_TITLE,
  1274.                         attach: name,
  1275.                         reviewStatusDisabled: true,
  1276.                         helpID: "Dlg_SendEmailReview",
  1277.                         cCaption: IDS_SEND_FOR_REVIEW_GET_ADDRS_CAPTION, 
  1278.                         msgLabel: IDS_SEND_FOR_REVIEW_MSG_LABEL
  1279.                         });
  1280.                     firstTime = false;
  1281.  
  1282.                 } else {
  1283.                     info = doc.Collab.collectEmailInfo({
  1284.                         to: info[0],
  1285.                         cc: info[1],
  1286.                         bcc: info[2],
  1287.                         subj: info[3],
  1288.                         msg: info[4],
  1289.                         inst1: IDS_SEND_FOR_REVIEW_INST1,
  1290.                         inst2: IDS_SEND_FOR_REVIEW_INST2,
  1291.                         title: IDS_SEND_FOR_REVIEW_TITLE,
  1292.                         attach: name,
  1293.                         reviewStatusDisabled: true,
  1294.                         helpID: "Dlg_SendEmailReview",
  1295.                         cCaption: IDS_SEND_FOR_REVIEW_GET_ADDRS_CAPTION, 
  1296.                         msgLabel: IDS_SEND_FOR_REVIEW_MSG_LABEL
  1297.                         });
  1298.                 }
  1299.  
  1300.                 if (info)
  1301.                 {
  1302.                     startDate = new Date();
  1303.                     var url = "mailto:" + escape(info[0]) + "?" +
  1304.                         "cc=" + escape(info[1]) + "&" +
  1305.                         "bcc=" + escape(info[2]) + "&" +
  1306.                         "subject=" + escape(info[3]) + "&" +
  1307.                         "body=" + escape(info[4]) + "&" +
  1308.                         "ui=false";
  1309.  
  1310.                     var deadSource = info[5] ? ", " + info[5].toSource() : "";
  1311.                     var script = "if(app.viewerType.match(\"Exchange\") == null || app.viewerVersion < 6) { app.alert({cMsg: \"" + IDS_SEND_FOR_REVIEW_VERSION_ERR + "\", oDoc: doc}) } else { Collab.registerReview(this, " + eaddr.toSource() + ", \"\", " + info[0].toSource() + ", " + info[1].toSource() + ", \"\", " + startDate.toSource() + deadSource + ");}";
  1312.  
  1313.                     if (app.platform == "WIN")
  1314.                     {
  1315.                         if((typeof info[0] == "undefined") || info[0].length == 0)
  1316.                         {
  1317.                             app.alert({cMsg: IDS_SEND_FOR_REVIEW_NO_RECIPIENT, oDoc: doc});
  1318.                             return 0; // Cancelled
  1319.                         }
  1320.                     }
  1321.         
  1322.                     var e;
  1323.                     try
  1324.                     {
  1325.                         keepTrying = false;
  1326.                         result = doc.submitForm({cURL: url, bEmbedForm: true, oJavaScript: { After: script } });
  1327.                     }
  1328.                     catch(e)
  1329.                     {
  1330.                         app.alert({cMsg: e["message"], oDoc: doc});
  1331.                         keepTrying = true;
  1332. //                        app.alert({cMsg: IDS_SEND_FOR_REVIEW_PROBLEM, oDoc: doc});
  1333. //                        return 0; // Cancelled
  1334.                     }
  1335.                 }
  1336.                 else
  1337.                     keepTrying = false;
  1338.             }
  1339.  
  1340.             // Check the return value: submitForm can fail but not throw 
  1341.             // if it popped it's own UI.
  1342.             if(!result) return 0; // Cancelled
  1343.  
  1344.             var fdfName = name.replace(/\.pdf\w*$/i,"\.fdf");
  1345.             var alertText = IDS_SEND_FOR_REVIEW_CONFIRM_MSG.replace(/\%docname\%/g, fdfName);
  1346.             if (doc.Collab.isOutlook)
  1347.             {
  1348.                 doc.Collab.alertWithHelp(
  1349.                     { cMsg: alertText,
  1350.                     cTip: IDS_SEND_FOR_REVIEW_CONFIRM_TIP,
  1351.                     cTitle: IDS_SEND_FOR_REVIEW_CONFIRM_TITLE,
  1352.                     cPref: "Annots:OutgoingEmailNotification"}
  1353.                   );
  1354.             }
  1355.  
  1356.             // note that only the first part of this message is displayed.  If the full text
  1357.             // is displayed there is a bug in the alert and the OK button is not located 
  1358.             // properly and cannot get focus and thus the dialog cannot be dismissed.
  1359.             if(info[5])
  1360.                 Collab.registerReview(doc, null, "", info[0], info[1], info[2], startDate, info[5]);
  1361.             else
  1362.                 Collab.registerReview(doc, null, "", info[0], info[1], info[2], startDate);
  1363.  
  1364.             return 1; // Success
  1365.  
  1366.         } else return 0; // Cancelled - if (eaddr is not NULL)
  1367.     } else return 0; // Cancelled - if (!docIsDirty)
  1368.   return -1; // Failed
  1369. }
  1370.  
  1371. // Should send for review be enabled?
  1372. function ANSendForReviewEnabled(doc)
  1373. {
  1374.   event.rc = doc && !doc.external && doc.requestPermission(permission.annot, permission.canExport) && doc.requestPermission(permission.annot, permission.modify);
  1375. }
  1376.  
  1377. function ANSendCommentsToAuthor(doc)
  1378. {
  1379.     var eaddr = doc.Collab.initiatorEmail;
  1380.     var name = doc.info.title;  // first attempt: title info
  1381.  
  1382.     // second attempt: last component of path
  1383.     if(!name)
  1384.     {
  1385.         var match = doc.path.match(/\/([^\/]*$)/);
  1386.  
  1387.         if(match && match.length == 2)
  1388.         name = match[1];
  1389.     }
  1390.  
  1391.     // third attempt: path
  1392.     if(!name)
  1393.         name = doc.path;
  1394.  
  1395.     var attachname;
  1396.     var amatch = doc.path.match(/\/([^\/]*$)/);
  1397.     if(amatch && amatch.length == 2)
  1398.         attachname = amatch[1];
  1399.     if(!attachname)
  1400.         attachname = doc.path;
  1401.     var fdfName = attachname.replace(/\.pdf\w*$/i,"\.fdf");
  1402.  
  1403.     var dlgTitle = IDS_SEND_COMMENTS_TO_AUTHOR_TITLE.replace(/\{docname\}/g, name);
  1404.     var subject = IDS_SEND_COMMENTS_TO_AUTHOR_SUBJ.replace(/\{title\}/g, name);
  1405.     var mesg = IDS_SEND_COMMENTS_TO_AUTHOR_MSG.replace(/\{title\}/g, name);
  1406.     var firstTime = true;
  1407.     var keepTrying = true;
  1408.     var info;
  1409.     var result;
  1410.   
  1411.     fdfName = fdfName.replace(/\.fdf\w*$/i, "");
  1412.      fdfName = IDS_SEND_COMMENTS_ATTACHMENT.replace(/\%pdfbase\%/g, fdfName);
  1413.  
  1414.     //
  1415.     // Keep trying to send to send email until the user cancels the email 
  1416.     // review dialog or we successfully send the email
  1417.     //
  1418.     while (keepTrying)
  1419.     {
  1420.         if (firstTime)
  1421.         {
  1422.             info = doc.Collab.collectEmailInfo({
  1423.                 to: eaddr,
  1424.                 subj: subject,
  1425.                 msg: mesg,
  1426.                 title: dlgTitle,
  1427.                 attach: fdfName,
  1428.                 inst1: IDS_SEND_COMMENTS_TO_AUTHOR_INST1,
  1429.                 inst2: IDS_SEND_COMMENTS_TO_AUTHOR_INST2,
  1430.                 toDisabled: true,
  1431.                 ccDisabled: true,
  1432.                 bccDisabled: true,
  1433.                 deadDateDisabled: true,
  1434.                 reviewStatusDisabled: false,
  1435.                 helpID: "Dlg_ReplyReview",
  1436.                 cCaption: IDS_SEND_FOR_REVIEW_GET_ADDRS_CAPTION,
  1437.                 msgLabel: IDS_SEND_COMMENTS_TO_AUTHOR_MSG_LABEL
  1438.                 });
  1439.             firstTime = false;
  1440.         } else {
  1441.  
  1442.             info = doc.Collab.collectEmailInfo({
  1443.                 to: info[0],
  1444.                 cc: info[1],
  1445.                 bcc: info[2],
  1446.                 subj: info[3],
  1447.                 msg: info[4],
  1448.                 title: dlgTitle,
  1449.                 attach: fdfName,
  1450.                 inst1: IDS_SEND_COMMENTS_TO_AUTHOR_INST1,
  1451.                 inst2: IDS_SEND_COMMENTS_TO_AUTHOR_INST2,
  1452.                 toDisabled: true,
  1453.                 ccDisabled: true,
  1454.                 bccDisabled: true,
  1455.                 deadDateDisabled: true,
  1456.                 reviewStatusDisabled: false,
  1457.                 helpID: "Dlg_ReplyReview",
  1458.                 cCaption: IDS_SEND_FOR_REVIEW_GET_ADDRS_CAPTION,
  1459.                 msgLabel: IDS_SEND_COMMENTS_TO_AUTHOR_MSG_LABEL
  1460.                 });
  1461.         }
  1462.  
  1463.         if(info)
  1464.         {
  1465.             var url = "mailto:" + escape(info[0]) + "?" +
  1466.                 "cc=" + escape(info[1]) + "&" +
  1467.                 "bcc=" + escape(info[2]) + "&" +
  1468.                 "subject=" + escape(info[3]) + "&" +
  1469.                 "body=" + escape(info[4]) + "&" +
  1470.                 "ui=false";
  1471.  
  1472.             var e;
  1473.             try
  1474.             {
  1475.                 keepTrying = false;
  1476.                 result = doc.submitForm({
  1477.                     aFields: [],
  1478.                     bAnnotations: true,
  1479.                     bInclNMKey: true,
  1480.                     cURL: url,
  1481.                     bExclFKey: true
  1482.                     });
  1483.             }
  1484.             catch(e)
  1485.             {
  1486.                 keepTrying = true;
  1487.                 app.alert({cMsg: e["message"], oDoc: doc});
  1488.             }
  1489.         }
  1490.         else
  1491.             keepTrying = false;
  1492.     }
  1493.  
  1494.     if(!result) return;
  1495.  
  1496.     var alertText = IDS_SEND_FOR_REVIEW_CONFIRM_MSG.replace(/\%docname\%/g, fdfName);
  1497.     if (doc.Collab.isOutlook)
  1498.     {
  1499.         doc.Collab.alertWithHelp(
  1500.             { cMsg: alertText,
  1501.               cTip: IDS_SEND_FOR_REVIEW_CONFIRM_TIP,
  1502.               cTitle: IDS_SEND_FOR_REVIEW_CONFIRM_TITLE,
  1503.               cPref: "Annots:OutgoingEmailNotification"}
  1504.               );
  1505.     }
  1506.     Collab.setReviewRespondedDate(doc, new Date());
  1507. }
  1508.  
  1509. function ANSendCommentsToAuthorEnabled(doc)
  1510. {
  1511.   return (event.rc = doc && doc.Collab.initiatorEmail && doc.requestPermission(permission.annot, permission.canExport) && doc.requestPermission(permission.annot, permission.modify));
  1512. }
  1513.  
  1514. if(!app.viewerType.match(/Reader/))
  1515. {
  1516.     // Add the menu item
  1517.     app.addMenuItem({
  1518.       cName: "SendForReview",
  1519.       cUser: IDS_SEND_FOR_REVIEW,
  1520.       cParent: "File",
  1521.       nPos: "endOptimizeGroup",
  1522.       cExec: "ANSendForReview(event.target);",
  1523.       cEnable: "ANSendForReviewEnabled(event.target);",
  1524.       bPrepend: false
  1525.     });
  1526.  
  1527.     // Add the menu item
  1528.     app.addMenuItem({
  1529.       cName: "SendCommentsToAuthor",
  1530.       cUser: IDS_SEND_COMMENTS_TO_AUTHOR,
  1531.       cParent: "File",
  1532.       nPos: "endSendCommentsMenuItem",
  1533.       cExec: "ANSendCommentsToAuthor(event.target);",
  1534.       cEnable: "ANSendCommentsToAuthorEnabled(event.target);",
  1535.       bPrepend: true
  1536.     });
  1537. }
  1538.  
  1539. function ANDefaultInvite(doc)
  1540. {
  1541.   if(!doc.external)
  1542.   {
  1543.     return ANSendForReview(doc);
  1544.   }
  1545.   // if external, fall through to the default C++ implementation
  1546. };
  1547.  
  1548. Collab.invite = ANDefaultInvite;
  1549.  
  1550. function CBdef(a, b)
  1551. {
  1552.   return typeof a == "undefined" ? b : a;
  1553. }
  1554.  
  1555. function Matrix2D(a, b, c, d, h, v)
  1556. {
  1557.     this.a = CBdef(a, 1);
  1558.     this.b = CBdef(b, 0);
  1559.     this.c = CBdef(c, 0);
  1560.     this.d = CBdef(d, 1);
  1561.     this.h = CBdef(h, 0);
  1562.     this.v = CBdef(v, 0);
  1563.     this.fromRotated = function(doc, page)
  1564.     {
  1565.         page = CBdef(page, 0);
  1566.  
  1567.         var cropBox = doc.getPageBox("Crop", page);
  1568.         var mediaBox = doc.getPageBox("Media", page);
  1569.         var mbHeight = mediaBox[1] - mediaBox[3];
  1570.         var mbWidth = mediaBox[2] - mediaBox[0];
  1571.         var rotation = doc.getPageRotation(page);
  1572.         var m = new Matrix2D(1, 0, 0, 1, cropBox[0] - mediaBox[0], cropBox[3] - mediaBox[3]);
  1573.  
  1574.         if(rotation == 90)
  1575.             return this.concat(m.rotate(Math.asin(1.0)).translate(mbHeight, 0));
  1576.         else if(rotation == 180)
  1577.             return this.concat(m.rotate(2.0 * -Math.asin(1.0)).translate(mbWidth, mbHeight));
  1578.         else if(rotation == 270)
  1579.             return this.concat(m.rotate(-Math.asin(1.0)).translate(0, mbWidth));
  1580.         return this.concat(m);
  1581.     }
  1582.     this.transform = function(pts)
  1583.     {
  1584.         var result = new Array(pts.length);
  1585.  
  1586.         if(typeof pts[0] == "object")
  1587.             for(var n = 0; n < pts.length; n++)
  1588.                 result[n] = this.transform(pts[n]);
  1589.         else
  1590.             for(var n = 0; n + 1 < pts.length; n += 2)
  1591.             {
  1592.                 result[n] = this.a * pts[n] + this.c * pts[n + 1] + this.h;
  1593.                 result[n + 1] = this.b * pts[n] + this.d * pts[n + 1] + this.v;
  1594.             }
  1595.         return result;
  1596.     }
  1597.     this.concat = function(m)
  1598.     {
  1599.         return new Matrix2D(
  1600.             (this.a * m.a) + (this.b * m.c),
  1601.             (this.a * m.b) + (this.b * m.d),
  1602.             (this.c * m.a) + (this.d * m.c),
  1603.             (this.c * m.b) + (this.d * m.d),
  1604.             (this.h * m.a) + (this.v * m.c) + m.h,
  1605.             (this.h * m.b) + (this.v * m.d) + m.v);
  1606.     }
  1607.     this.invert = function()
  1608.     {
  1609.         var result = new Matrix2D;
  1610.         var q = this.b * this.c - this.a * this.d;
  1611.  
  1612.         if (q)
  1613.         {
  1614.             result.a = - this.d / q;
  1615.             result.b = this.b / q;
  1616.             result.c = this.c / q;
  1617.             result.d = - this.a / q;
  1618.             result.h = -(this.h * result.a + this.v * result.c);
  1619.             result.v = -(this.h * result.b + this.v * result.d);
  1620.         }
  1621.         return result;
  1622.     }
  1623.     this.translate = function(dx, dy)
  1624.     {
  1625.         return this.concat(new Matrix2D(1, 0, 0, 1, CBdef(dx, 0), CBdef(dy, 0)));
  1626.     }
  1627.     this.scale = function(sx, sy)
  1628.     {
  1629.         return this.concat(new Matrix2D(CBdef(sx, 1), 0, 0, CBdef(sy, 1), 0, 0));
  1630.     }
  1631.     this.rotate = function(t)
  1632.     {
  1633.         t = CBdef(t, 0);
  1634.         return this.concat(new Matrix2D(Math.cos(t), Math.sin(t), -Math.sin(t), Math.cos(t), 0, 0));
  1635.     }
  1636. }
  1637.