home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 January / 01_02.iso / software / netscape62win / browser.xpi / bin / chrome / toolkit.jar / content / global / downloadProgress.js < prev    next >
Encoding:
JavaScript  |  2001-08-21  |  12.7 KB  |  401 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "License"); you may not use this file except in
  5.  * compliance with the License.  You may obtain a copy of the License at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10.  * for the specific language governing rights and limitations under the
  11.  * License.
  12.  *
  13.  * The Original Code is Mozilla Communicator client code,
  14.  * released March 31, 1998.
  15.  *
  16.  * The Initial Developer of the Original Code is Netscape Communications
  17.  * Corporation.  Portions created by Netscape are
  18.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  19.  * Reserved.
  20.  *
  21.  * Contributors:
  22.  *     William A. ("PowerGUI") Law <law@netscape.com>
  23.  */
  24. var data;   // nsIStreamTransferOperation object
  25. var dialog;
  26.  
  27. function loadDialog() {
  28.     dialog.location.setAttribute( "value", data.source.URI.spec );
  29.     dialog.fileName.setAttribute( "value", data.target.unicodePath );
  30.     dialog.error = false;
  31. }
  32.  
  33. var contractId = "@mozilla.org/appshell/component/xfer;1";
  34. var observer = {
  35.     Observe: function( subject, topic, data ) {
  36.         switch ( topic ) {
  37.             case contractId+";onProgress":
  38.                 var words = data.split( " " );
  39.                 onProgress( words[0], words[1] );
  40.                 break;
  41.             case contractId+";onStatus":
  42.                 // Ignore useless "Transferring data..." messages
  43.                 // after download has started.
  44.                 if ( !started ) {
  45.                    onStatus( data );
  46.                 }
  47.                 break;
  48.             case contractId+";onCompletion":
  49.                 onCompletion( data );
  50.                 break;
  51.             case contractId+";onError":
  52.                 onError( data );
  53.                 break;
  54.             default:
  55.                 alert( "Unknown topic: " + topic + "\nData: " + data );
  56.                 break;
  57.         }
  58.         return;
  59.     }
  60. }
  61.  
  62. function getString( stringId ) {
  63.     // Check if we've fetched this string already.
  64.     if ( !( stringId in dialog.strings ) ) {
  65.         // Try to get it.
  66.         var elem = document.getElementById( "dialog.strings."+stringId );
  67.         if ( elem
  68.            &&
  69.            elem.firstChild
  70.            &&
  71.            elem.firstChild.nodeValue ) {
  72.             dialog.strings[ stringId ] = elem.firstChild.nodeValue;
  73.         } else {
  74.             // If unable to fetch string, use an empty string.
  75.             dialog.strings[ stringId ] = "";
  76.         }
  77.     }
  78.     return dialog.strings[ stringId ];
  79. }
  80.  
  81. function replaceInsert( text, index, value ) {
  82.    var result = text;
  83.    var regExp = new RegExp( "#"+index );
  84.    result = result.replace( regExp, value );
  85.    return result;
  86. }
  87.  
  88. function onLoad() {
  89.     // Set global variables.
  90.     data = window.arguments[0];
  91.  
  92.     if ( !data ) {
  93.         window.close()
  94.         return;
  95.     }
  96.  
  97.     dialog = new Object;
  98.     dialog.strings = new Array;
  99.     dialog.location    = document.getElementById("dialog.location");
  100.     dialog.contentType = document.getElementById("dialog.contentType");
  101.     dialog.fileName    = document.getElementById("dialog.fileName");
  102.     dialog.status      = document.getElementById("dialog.status");
  103.     dialog.progress    = document.getElementById("dialog.progress");
  104.     dialog.timeLeft    = document.getElementById("dialog.timeLeft");
  105.     dialog.timeElapsed = document.getElementById("dialog.timeElapsed");
  106.     dialog.cancel      = document.getElementById("dialog.cancel");
  107.  
  108.     // Fill dialog.
  109.     loadDialog();
  110.  
  111.     // Commence transfer.
  112.     data.observer = observer;
  113.     try {
  114.         data.Start();
  115.     } catch( exception ) {
  116.         onError( exception );
  117.     }
  118. }
  119.  
  120. function onUnload() {
  121.     // Only do this one time...
  122.     if ( data ) {
  123.         // Unhook underlying xfer operation.
  124.         var op = data;
  125.         data = null;
  126.  
  127.         // Unhook observer.
  128.         op.observer = null;
  129.  
  130.         // See if we completed normally (i.e., are closing ourself).
  131.         if ( started && !completed ) {
  132.             // Terminate transfer.
  133.             try {
  134.                 op.Stop();
  135.             } catch ( exception ) {
  136.                 if ( !dialog.error ) {
  137.                     // Show error now.
  138.                     onError( exception );
  139.                 }
  140.             }
  141.             op = null;
  142.         }
  143.     }
  144. }
  145.  
  146. var started   = false;
  147. var completed = false;
  148. var startTime;
  149. var elapsed;
  150. var interval = 1000; // Update every 1000 milliseconds.
  151. var lastUpdate = -interval; // Update initially.
  152.  
  153. // These are to throttle down the updating of the download rate figure.
  154. var priorRate = 0;
  155. var rateChanges = 0;
  156. var rateChangeLimit = 2;
  157.  
  158. var warningLimit = 30000; // Warn on Cancel after 30 sec (30000 milleseconds).
  159.  
  160. function stop() {
  161.    // If too much time has elapsed, make sure it's OK to quit.
  162.    if ( started && !completed ) {
  163.       // Get current time.
  164.       var now = ( new Date() ).getTime();
  165.  
  166.       // See if sufficient time has elapsed to warrant dialog.
  167.       if ( now - startTime > warningLimit ) {
  168.          // XXX - Disabled for now since confirm call doesn't work.
  169.          if ( 0 && !window.confirm( getString( "confirmCancel" ) ) ) {
  170.             return;
  171.          }
  172.       }
  173.    }
  174.  
  175.    // Stop the transfer.
  176.    onUnload();
  177.  
  178.    // Close the window.
  179.    window.close();
  180. }
  181.  
  182. function onProgress( bytes, max ) {
  183.     // Check for first time.
  184.     if ( !started ) {
  185.         // Initialize download start time.
  186.         started = true;
  187.         startTime = ( new Date() ).getTime();
  188.     }
  189.  
  190.     // Get current time.
  191.     var now = ( new Date() ).getTime();
  192.     // If interval hasn't elapsed, ignore it.
  193.     if ( now - lastUpdate < interval
  194.          &&
  195.          max != "-1"
  196.          &&
  197.          parseInt(bytes) < parseInt(max) ) {
  198.         return;
  199.     }
  200.  
  201.     // Update this time.
  202.     lastUpdate = now;
  203.  
  204.     // Update download rate.
  205.     elapsed = now - startTime;
  206.     var rate; // bytes/sec
  207.     if ( elapsed ) {
  208.         rate = ( bytes * 1000 ) / elapsed;
  209.     } else {
  210.         rate = 0;
  211.     }
  212.  
  213.     // Update elapsed time display.
  214.     dialog.timeElapsed.setAttribute("value", formatSeconds( elapsed / 1000 ));
  215.  
  216.     // Calculate percentage.
  217.     var percent;
  218.     if ( max != "-1" ) {
  219.         percent = parseInt( (bytes*100)/max + .5 );
  220.         if ( percent > 100 ) {
  221.            percent = 100;
  222.         }
  223.  
  224.         // Advance progress meter.
  225.         dialog.progress.setAttribute( "value", percent );
  226.     } else {
  227.         percent = "??";
  228.  
  229.         // Progress meter should be barber-pole in this case.
  230.         dialog.progress.setAttribute( "mode", "undetermined" );
  231.     }
  232.  
  233.     // Check if download complete.
  234.     if ( !completed ) {
  235.         // Update status (nnK of mmK bytes at xx.xK bytes/sec)
  236.         var status = getString( "progressMsg" );
  237.  
  238.         // Insert 1 is the number of kilobytes downloaded so far.
  239.         status = replaceInsert( status, 1, parseInt( bytes/1024 + .5 ) );
  240.  
  241.         // Insert 2 is the total number of kilobytes to be downloaded (if known).
  242.         if ( max != "-1" ) {
  243.            status = replaceInsert( status, 2, parseInt( max/1024 + .5 ) );
  244.         } else {
  245.            status = replaceInsert( status, 2, "??" );
  246.         }
  247.  
  248.         if ( rate ) {
  249.            // rate is bytes/sec
  250.            var kRate = rate / 1024; // K bytes/sec;
  251.            kRate = parseInt( kRate * 10 + .5 ); // xxx (3 digits)
  252.            // Don't update too often!
  253.            if ( kRate != priorRate ) {
  254.               if ( rateChanges++ == rateChangeLimit ) {
  255.                  // Time to update download rate.
  256.                  priorRate = kRate;
  257.                  rateChanges = 0;
  258.               } else {
  259.                  // Stick with old rate for a bit longer.
  260.                  kRate = priorRate;
  261.               }
  262.            } else {
  263.               rateChanges = 0;
  264.            }
  265.            var fraction = kRate % 10;
  266.            kRate = parseInt( ( kRate - fraction ) / 10 );
  267.  
  268.            // Insert 3 is the download rate (in kilobytes/sec).
  269.            status = replaceInsert( status, 3, kRate + "." + fraction );
  270.         } else {
  271.            status = replaceInsert( status, 3, "??.?" );
  272.         }
  273.         // Update status msg.
  274.         onStatus( status );
  275.     }
  276.  
  277.     // Update percentage label on progress meter.
  278.     var percentMsg = getString( "percentMsg" );
  279.     percentMsg = replaceInsert( percentMsg, 1, percent );
  280.     dialog.progress.label = percentMsg;
  281.  
  282.     if ( !completed ) {
  283.         // Update time remaining.
  284.         if ( rate && max != "-1" ) {
  285.             var rem = ( max - bytes ) / rate;
  286.             rem = parseInt( rem + .5 );
  287.             dialog.timeLeft.setAttribute("value", formatSeconds( rem ));
  288.         } else {
  289.             dialog.timeLeft.setAttribute("value", getString( "unknownTime" ));
  290.         }
  291.     } else {
  292.         // Clear time remaining field.
  293.         dialog.timeLeft.setAttribute("value", "");
  294.     }
  295. }
  296.  
  297. function formatSeconds( secs ) {
  298.     // Round the number of seconds to remove fractions.
  299.     secs = parseInt( secs + .5 );
  300.     var hours = parseInt( secs/3600 );
  301.     secs -= hours*3600;
  302.     var mins = parseInt( secs/60 );
  303.     secs -= mins*60;
  304.     var result;
  305.     if ( hours ) {
  306.       result = getString( "longTimeFormat" );
  307.     } else {
  308.       result = getString( "shortTimeFormat" );
  309.     }
  310.     if ( hours < 10 ) {
  311.        hours = "0" + hours;
  312.     }
  313.     if ( mins < 10 ) {
  314.        mins = "0" + mins;
  315.     }
  316.     if ( secs < 10 ) {
  317.        secs = "0" + secs;
  318.     }
  319.     // Insert hours, minutes, and seconds into result string.
  320.     result = replaceInsert( result, 1, hours );
  321.     result = replaceInsert( result, 2, mins );
  322.     result = replaceInsert( result, 3, secs );
  323.  
  324.     return result;
  325. }
  326.  
  327. function onCompletion( status ) {
  328.     // Note that we're done (and can ignore subsequent progress notifications).
  329.     completed = true;
  330.     // Indicate completion in status area.
  331.     var msg = getString( "completeMsg" );
  332.     msg = replaceInsert( msg, 1, formatSeconds( elapsed/1000 ) );
  333.     onStatus( msg );
  334.  
  335.     // Put progress meter at 100%.
  336.     dialog.progress.setAttribute( "value", 100 );
  337.     dialog.progress.setAttribute( "mode", "normal" );
  338.     window.close();
  339. }
  340.  
  341. function onStatus( status ) {
  342.    // Update status text in dialog.
  343.    // Ignore if status text is less than 10 characters (we're getting some
  344.    // bogus onStatus notifications.
  345.    if ( status.length > 9 ) {
  346.       dialog.status.setAttribute("value", status);
  347.    }
  348. }
  349.  
  350. function onError( errorCode ) {
  351.     // Record fact we had an error.
  352.     dialog.error = true;
  353.  
  354.     // Errorcode has format: "operation rv <string>" where
  355.     //   operation - operation that failed; values are as defined
  356.     //               in nsIStreamTransferOperation.idl
  357.     //   rv        - actual nsresult (in "%X" format)
  358.     //   reason    - reason for failure; values are as defined
  359.     //               in nsIStreamTransferOperation.idl
  360.     var msg;
  361.     switch ( parseInt( errorCode.split(" ")[0] ) ) {
  362.         // Write errors...
  363.         case Components.interfaces.nsIStreamTransferOperation.kOpWrite :
  364.         case Components.interfaces.nsIStreamTransferOperation.kOpAsyncWrite :
  365.         case Components.interfaces.nsIStreamTransferOperation.kOpOpenOutputStream :
  366.         case Components.interfaces.nsIStreamTransferOperation.kOpCreateTransport :
  367.         case Components.interfaces.nsIStreamTransferOperation.kOpOutputClose :
  368.         case Components.interfaces.nsIStreamTransferOperation.kOpOutputCancel :
  369.             // Look for some specific reasons.
  370.             switch ( parseInt( errorCode.split(" ")[2] ) ) {
  371.                 case Components.interfaces.nsIStreamTransferOperation.kReasonAccessError :
  372.                     // Access/permission error.
  373.                     msg = getString( "accessErrorMsg" );
  374.                     break;
  375.  
  376.                 case Components.interfaces.nsIStreamTransferOperation.kReasonDiskFull :
  377.                     // Out of space error.
  378.                     msg = getString( "diskFullMsg" );
  379.                     break;
  380.  
  381.                 default :
  382.                     // Generic write error.
  383.                     msg = getString( "writeErrorMsg" );
  384.                     break;
  385.             }
  386.             break;
  387.  
  388.         // Read errors...
  389.         default :
  390.             // Presume generic read error.
  391.             msg = getString( "readErrorMsg" );
  392.             break;
  393.     }
  394.     // Tell user.
  395.     alert( msg );
  396.     // Dump error code to console.
  397.     dump( "downloadProgress.js onError: " + errorCode + "\n" );
  398.     // Terminate transfer and clean up.
  399.     stop();
  400. }
  401.