Gmail - dynamic Add Attachment - Reverse Engineering

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • JavaScriptRocks

    Gmail - dynamic Add Attachment - Reverse Engineering

    I've been trying to imitate / reverse engineer the add attachment
    feature in gmail composer. I managed to do it to say about 80% but its
    giving me trouble in IE on WinXP-Sp2. I am using PHP to do the upload.
    It works well on Firefox/DeerPark, but in IE, the file selected just
    vanishes. You can verify it by commenting the lines marked "//IE
    Trouble". Commenting those lines will remove IE specific code, except
    the el.click() whick sends the Click event to the newly added file
    upload input element.


    Please someone help me.


    _______________ __________
    Here is the code
    -------------------------
    <?
    include_once('. ./modules/file_­upload/lib_upload.php' );
    ?>

    <script language="JavaS cript">

    function Table_AddRow(tb lName, pos)
    {
    if(typeof(pos) == 'undefined')
    pos = '_LAST_';


    var tbl = document.getEle mentById(tblNam ­e);
    switch(pos)
    {
    case '_LAST_':
    pos = tbl.rows.length ;
    default:
    pos = parseInt(pos, 10);
    if(isNaN(pos))
    {
    pos = 0;
    }
    else if (pos > tbl.rows.length )
    {
    pos = tbl.rows.length ;
    }
    }


    var today = new Date();
    var rowID = tblName + today.getTime() + tbl.rows.length ;


    var row = tbl.insertRow(p os);
    if(document.all ) row.style.displ ay = 'none';
    //IE Trouble
    var cellRight = row.insertCell( row.length);
    cellRight.setAt tribute('id', rowID);


    var el = document.create Element('input' ­);
    el.setAttribute ('type', 'FILE');
    el.setAttribute ('name', 'upload_files[]');
    if(document.all ) el.style.displa y = 'none';
    //IE Trouble
    cellRight.appen dChild(el);


    if(document.all )
    {
    //IE Trouble
    el.click();
    if(el.value == '')
    //IE Trouble
    {
    //IE Trouble
    DeleteCurRow(ro w);
    //IE Trouble
    }
    //IE Trouble
    else
    //IE Trouble
    {
    //IE Trouble
    row.style.displ ay = '';
    //IE Trouble
    var fn = document.create Element("strong ­");
    //IE Trouble
    fn.innerHTML = el.value;
    //IE Trouble
    cellRight.appen dChild(fn);
    //IE Trouble
    }
    //IE Trouble
    }
    var spc = document.create Element("span") ­;
    spc.innerHTML = "&nbsp;";
    cellRight.appen dChild(spc);


    var aa = document.create Element("a");
    aa.setAttribute ('href', 'javascript:;') ;
    var clickName = new
    Function("Table _DeleteRowByID( ­'"+tblName+"', '"+rowID+"') ");
    aa.onclick = clickName;
    aa.innerHTML = "remove";
    cellRight.appen dChild(aa);



    }


    function Table_DeleteRow (x)
    {
    while (x.tagName.toLo werCase() !='tr')
    {
    if(x.parentElem ent)
    x=x.parentEleme nt;
    else if(x.parentNode )
    x=x.parentNode;
    else
    return;
    }
    var rowNum=x.rowInd ex;
    while (x.tagName.toLo werCase() !='table')
    {
    if(x.parentElem ent)
    x=x.parentEleme nt;
    else if(x.parentNode )
    x=x.parentNode;
    else
    return;
    }
    x.deleteRow(row Num);


    }


    function Table_DeleteRow ByIndex(tblName ­, row)
    {
    var tbl = document.getEle mentById(tblNam ­e);
    x.deleteRow(row );


    }


    function Table_DeleteRow ByID(tblName, rowID)
    {
    var tbl = document.getEle mentById(tblNam ­e);
    var row = document.getEle mentById(rowID) ­;
    while (row.tagName.to LowerCase() !='tr')
    {
    if(row.parentEl ement)
    row = row.parentEleme nt;
    else if(row.parentNo de)
    row = row.parentNode;
    else
    return;
    }
    var rowNum = row.rowIndex;
    tbl.deleteRow(r owNum);


    }


    </script>

    <form method="post" enctype="multip art/form-data">
    <table id="tblSample" border="0" cellspacing="2"
    cellpadding="2" >
    <tr id="tblSample_r ow0">
    <td><a href="javascrip t:;"
    onClick="Table_ AddRow('tblSamp ­le', 0); return false;">Add
    File</a></td>
    </tr></table><input type="hidden" name="MAX_FILE_ SIZE"
    value="512000"> <input type=submit>
    </form>


    <?php
    if($REQUEST_MET HOD == "POST")
    {
    $newNames = array();
    if(($newFile = UploadFile('upl oad_files', '.', 1, $newNames,
    array('applicat ion/x-zip-compr­essed', 'application/zip'))) === false)

    {
    ?><font color=red><b>Up load of <?= sizeof($APP_ERR )?>
    file(s) failed.</font><br><stron g>Erro­r: </strong><?
    print_r($APP_ER R)?><?
    }
    else
    {
    ?><font color=green><b> <?= sizeof($newName s)?> file(s)
    uploaded.<br></font>&nbsp;&nbs ­p;&nbsp;New filename: <?
    print_r($newNam es) ?><?
    }
    }


    ?>

    _______________ __________
    lib_upload.php
    -------------------------
    sorry for the un-pro coding, one of my
    early creations, not well maintained
    -------------------------
    function UploadFile($nam e, $path, $mandatory = 0, &$newName,
    $mimeAllowed = array())
    {
    global $APP_ERR;
    global $HTTP_POST_FILE S;
    global $PATH_TRANSLATE D;
    global $HTTP_POST_VARS ;
    global $UPLOAD_MAX_FIL ESIZE;

    $UPLOAD_MAX_FIL ESIZE = get_cfg_var('up load_max_filesi ze');
    $x = substr($UPLOAD_ MAX_FILESIZE, -1);
    switch($x)
    {
    case 'T':
    $x = 1099511627776;
    break;
    case 'G':
    $x = 1073741824;
    break;
    case 'M':
    $x = 1048576;
    break;
    case 'K':
    $x = 1024;
    break;
    default:
    $x = 1;
    break;
    }
    $UPLOAD_MAX_FIL ESIZE = intval($UPLOAD_ MAX_FILESIZE) * $x;
    $scrMaxSize = intval($HTTP_PO ST_VARS['MAX_FILE_SIZE']);
    $UPLOAD_MAX_FIL ESIZE = $scrMaxSize < $UPLOAD_MAX_FIL ESIZE?
    $scrMaxSize: $UPLOAD_MAX_FIL ESIZE;
    unset($x, $scrMaxSize);

    if(isset($name) && isset($path))
    {
    $APP_ERR = NULL;

    set_time_limit( 60);
    $MimeType = array(
    'application/x-drm' => 'dnp',
    'image/x-tiff' => 'tif',
    'image/x-targa' => 'tga',
    'image/x-quicktime' => 'qti',
    'image/x-png' => 'png',
    'image/x-pict' => 'pic',
    'image/pict' => 'pic',
    'image/x-macpaint' => 'mac',
    'image/x-photoshop' => 'psd',
    'image/x-sgi' => 'sgi',
    'application/x-rtsp' => 'rts',
    'audio/vnd.qcelp' => 'qcp',
    'video/flc' => 'flc',
    'application/asx' => 'asx',
    'audio/x-rmf' => false,
    'audio/rmf' => false,
    'audio/midi' => 'mid',
    'audio/nspaudio' => 'lma',
    'audio/x-nspaudio' => 'lma',
    'text/xml' => 'xml',
    'audio/wav64' => 'w64',
    'application/x-cnet-vsl' => 'vsl',
    'text/iuls' => 'uls',
    'application/x-shockwave-flash' => 'swf',
    'application/vnd.ms-pki.stl' => 'stl',
    'application/vnd.ms-pki.certstore' => 'sst',
    'application/futuresplash' => 'spl',
    'application/forge' => 'sfw',
    'audio/sfa' => 'sfa',
    'audio/x-sd2' => 'sd2',
    'text/scriptlet' => false,
    'application/vnd.adobe.asset-catalog' => 'sac',
    'audio/x-pn-realaudio-plugin' => 'rpm',
    'application/rat-file' => 'rat',
    'application/pics-rules' => 'prf',
    'application/vnd.ms-pki.pko' => 'pko',
    'audio/pca' => 'pca',
    'application/pkcs7-signature' => 'p7s',
    'application/x-pkcs7-certreqresp' => 'p7r',
    'application/pkcs7-mime' => 'p7m',
    'application/x-pkcs7-certificates' => 'p7b',
    'application/x-pkcs12' => 'p12',
    'application/pkcs10' => 'p10',
    'audio/ogg' => 'ogg',
    'application/x-mmxp' => 'mxp',
    'audio/mid' => 'mid',
    'application/msaccess' => 'mdb',
    'application/x-troff-man' => 'man',
    'image/pjpeg' => 'jfif',
    'application/x-iphone' => 'iii',
    'image/x-icon' => 'ico',
    'Icon Library' => 'icl',
    'text/webviewhtml' => 'htt',
    'text/x-component' => 'htc',
    'application/hta' => 'hta',
    'application/vnd.fdf' => 'fdf',
    'message/rfc822' => 'eml',
    'video/x-dv' => 'dv',
    'application/x-speedbit-daf' => 'daf',
    'application/x-ctm' => 'ctm',
    'text/css' => 'css',
    'application/pkix-crl' => 'crl',
    'application/x-x509-ca-cert' => 'cer',
    'application/x-cdf' => 'cdf',
    'application/vnd.ms-pki.seccat' => 'cat',
    'image/bmp' => false,
    'text/h323' => '323',
    'application/x-vpeg005' => 'vpg',
    'application/vnd.rn-realsystem-rjt' => 'rjt',
    'audio/blue-matter-song' => 'bmt',
    'text/blue-matter-content-ref' => 'bmr',
    'audio/blue-matter-offer' => 'bmo',
    'audio/x-liquid-file' => 'la1',
    'application/x-laplayer-reg' => 'lar',
    'audio/x-liquid-secure' => 'lavs',
    'audio/x-la-lqt' => 'lqt',
    'audio/x-la-lms' => 'lmsff',
    'audio/x-mei-aac' => 'acp',
    'text/vnd.rn-realtext3d' => 'r3t',
    'audio/x-pn-aiff' => 'aif',
    'audio/x-pn-au' => 'au',
    'audio/x-pn-windows-pcm' => 'wav',
    'audio/x-pn-windows-acm' => 'wav',
    'audio/x-pn-wav' => 'wav',
    'audio/wav' => 'wav',
    'application/vnd.rn-realsystem-rmj' => 'rmj',
    'application/vnd.rn-realmedia-vbr' => 'rmvb',
    'video/vnd.rn-realvideo-secure' => 'rms',
    'audio/x-realaudio-secure' => 'rms',
    'application/vnd.rn-realaudio-secure' => 'rms',
    'application/vnd.rn-realmedia-secure' => 'rms',
    'audio/x-musicnet-download' => 'mnd',
    'audio/x-musicnet-stream' => 'mns',
    'application/vnd.rn-realsystem-rom' => 'rom',
    'application/vnd.rn-realsystem-r1m' => 'r1m',
    'video/x-mpg' => 'mpa',
    'audio/mp2' => 'mp2',
    'audio/mp1' => 'mp1',
    'video/mpg' => 'mpg',
    'audio/rn-mpeg' => 'mpga',
    'audio/mpeg' => 'mpga',
    'application/x-sdp' => 'sdp',
    'application/sdp' => 'sdp',
    'image/vnd.rn-realpix' => 'rp',
    'text/vnd.rn-realtext' => 'rt',
    'image/vnd.rn-realflash' => 'rf',
    'video/vnd.rn-realvideo' => 'rv',
    'application/vnd.rn-realmedia' => 'rm',
    'audio/x-realaudio' => 'ra',
    'audio/vnd.rn-realaudio' => 'ra',
    'application/vnd.rn-realsystem-rjs' => 'rjs',
    'application/vnd.rn-realsystem-rmx' => 'rmx',
    'application/vnd.rn-rn_music_packag e' => 'rmp',
    'application/vnd.rn-realplayer' => 'rnx',
    'application/vnd.rn-rsml' => 'rsml',
    'application/streamingmedia' => 'ssm',
    'application/smil' => 'smi',
    'audio/mpegurl' => 'm3u',
    'audio/mpg' => 'mpg',
    'audio/mp3' => 'mp3',
    'audio/x-mpg' => 'mpg',
    'audio/x-mp3' => 'mp3',
    'audio/scpls' => 'pls',
    'audio/x-scpls' => 'pls',
    'video/x-ms-wvx' => 'wvx',
    'video/x-ms-wmv' => 'wmv',
    'video/x-ms-wm' => 'wm',
    'video/x-ms-asf' => false,
    'video/x-mpeg2a' => 'mp2',
    'video/x-mpeg' => 'mpe',
    'video/x-ivf' => 'ivf',
    'video/msvideo' => 'avi',
    'video/avi' => 'avi',
    'midi/mid' => 'mid',
    'audio/x-ms-wma' => 'wma',
    'audio/x-ms-wax' => 'wax',
    'audio/x-mpegurl' => 'm3u',
    'audio/x-midi' => 'mid',
    'application/pps' => 'pps',
    'application/pot' => 'pot',
    'application/ppt' => 'ppt',
    'application/xlc' => 'xlc',
    'application/msexcel' => 'xls',
    'audio/aiff' => 'aif',
    'application/x-compress' => 'z',
    'application/wordperfect5.1' => 'wpd',
    'application/vnd.lotus-screencam' => 'scm',
    'application/vnd.lotus-organizer' => 'org',
    'application/vnd.lotus-freelance' => 'pre',
    'application/vnd.lotus-wordpro' => 'lwp',
    'application/vnd.ms-schedule' => 'sch',
    'application/vnd.ms-powerpoint' => false,
    'application/vnd.ms-access' => false,
    'application/vnd.ms-excel' => false,
    'application/msword' => 'doc',
    'application/x-pkcs7-crl' => 'crl',
    'application/pre-encrypted' => 'enc',
    'application/x-pkcs7-signature' => 'p7s',
    'application/x-pkcs7-mime' => 'p7m',
    'application/x-javascript-config' => 'jsc',
    'application/x-ns-proxy-autoconfig' => 'pac',
    'application/x-javascript' => 'js',
    'application/x-perl' => 'pl',
    'application/x-tcl' => 'tcl',
    'application/x-sh' => 'sh',
    'application/x-csh' => 'csh',
    'application/postscript' => false,
    'application/octet-stream' => false,
    'application/java-archive' => 'jar',
    'application/x-cpio' => 'cpio',
    'application/x-gtar' => 'gtar',
    'application/x-tar' => 'tar',
    'application/x-shar' => 'shar',
    'application/x-zip-compressed' => 'zip',
    'application/x-stuffit' => 'sit',
    'application/mac-binhex40' => 'hqx',
    'video/x-msvideo' => 'avi',
    'video/quicktime' => false,
    'video/x-mpeg2' => 'mpv2',
    'video/mpeg' => 'mpg',
    'audio/x-pn-realaudio' => 'ram',
    'audio/x-mpeg' => 'mpga',
    'audio/x-wav' => 'wav',
    'audio/x-aiff' => 'aif',
    'audio/basic' => 'snd',
    'application/fractals' => 'fif',
    'image/ief' => 'ief',
    'image/png' => 'png',
    'image/x-photo-cd' => 'pcd',
    'image/x-MS-bmp' => 'bmp',
    'image/x-rgb' => 'rgb',
    'image/x-portable-pixmap' => 'ppm',
    'image/x-portable-graymap' => 'pgm',
    'image/x-portable-bitmap' => 'pbm',
    'image/x-portable-anymap' => 'pnm',
    'image/x-xwindowdump' => 'xwd',
    'image/x-xpixmap' => 'xpm',
    'image/x-xbitmap' => 'xbm',
    'image/x-cmu-raster' => 'ras',
    'image/tiff' => 'tif',
    'image/jpeg' => 'jpg',
    'image/gif' => 'gif',
    'text/x-vcard' => 'vcf',
    'application/x-texinfo' => 'texinfo',
    'application/x-dvi' => 'dvi',
    'application/x-latex' => 'latex',
    'application/x-tex' => 'tex',
    'application/pdf' => 'pdf',
    'application/rtf' => 'rtf',
    'application/zip' => 'zip',
    'text/html' => 'htm',
    'text/plain' => 'txt'
    );

    if(substr($path , 0, 1) != '/')
    $path = dirname($PATH_T RANSLATED).'/'.$path;
    $path .= substr($path, -1) != '/'? '/': '';

    list($usec, $sec) = explode(' ', microtime());
    list(, $usec) = explode('.', $usec);
    $fnBase = $sec.$usec;

    $errStat = count($APP_ERR) ;
    for($i = 0; $i < sizeof($HTTP_PO ST_FILES[$name]['name']); $i++)
    {
    if(empty($HTTP_ POST_FILES[$name]['name'][$i]))
    {
    if(is_array($ma ndatory) && $mandatory[$i])
    $APP_ERR[$i] = 'File was not uploaded.';
    }

    if((sizeof($mim eAllowed) > 0) &&
    !in_array($HTTP _POST_FILES[$name]['type'][$i], $mimeAllowed))
    {
    $APP_ERR[$i] = 'File type not allowed.';
    }

    if(!empty($HTTP _POST_FILES[$name]['name'][$i]) &&
    isset($MimeType[$HTTP_POST_FILE S[$name]['type'][$i]]))
    {
    if(!isset($newN ame[$i])) $newName[$i] = "$fnBase$i" ;

    if(false === strrchr($newNam e[$i], '.'))
    {
    $newName[$i] .= '.';
    $newName[$i] .= $MimeType[$HTTP_POST_FILE S[$name]['type'][$i]] ===
    false
    ? substr($HTTP_PO ST_FILES[$name]['name'][$i],
    strrpos($HTTP_P OST_FILES[$name]['name'][$i], '.'))
    : $MimeType[$HTTP_POST_FILE S[$name]['type'][$i]];
    }
    continue;
    }
    else
    {
    $APP_ERR[$i] = 'Unknown file type.';
    }
    unset($newName[$i]);
    }

    if($errStat == count($APP_ERR) && (!is_array($man datory) &&
    $mandatory === count($newName) ))
    {
    for($i = 0; $i < sizeof($HTTP_PO ST_FILES[$name]['name']); $i++)
    {
    if(false ===
    move_uploaded_f ile($HTTP_POST_ FILES[$name]['tmp_name'][$i],
    $path.$newName[$i]))
    {
    $APP_ERR[$i] = true;
    unset($newName[$i]);
    continue;
    }
    }
    }

    return sizeof($APP_ERR ) > 0 ? false: $newName;
    }
    }

  • RobG

    #2
    Re: Gmail - dynamic Add Attachment - Reverse Engineering

    JavaScriptRocks wrote:[color=blue]
    > I've been trying to imitate / reverse engineer the add attachment
    > feature in gmail composer. I managed to do it to say about 80% but its
    > giving me trouble in IE on WinXP-Sp2. I am using PHP to do the upload.
    > It works well on Firefox/DeerPark, but in IE, the file selected just
    > vanishes. You can verify it by commenting the lines marked "//IE
    > Trouble". Commenting those lines will remove IE specific code, except
    > the el.click() whick sends the Click event to the newly added file
    > upload input element.
    >
    >
    > Please someone help me.
    >
    >
    > _______________ __________
    > Here is the code
    > -------------------------
    > <?
    > include_once('. ./modules/file_­upload/lib_upload.php' );
    > ?>
    >
    > <script language="JavaS cript">[/color]

    language is depreciated, type is required.
    [color=blue]
    >
    > function Table_AddRow(tb lName, pos)
    > {
    > if(typeof(pos) == 'undefined')
    > pos = '_LAST_';
    >
    >
    > var tbl = document.getEle mentById(tblNam ­e);
    > switch(pos)
    > {
    > case '_LAST_':
    > pos = tbl.rows.length ;
    > default:
    > pos = parseInt(pos, 10);
    > if(isNaN(pos))
    > {
    > pos = 0;
    > }
    > else if (pos > tbl.rows.length )
    > {
    > pos = tbl.rows.length ;
    > }
    > }[/color]

    That appears to be an inordinate amount of code just to check 'pos'.
    Given that you pass 'pos' from your own code, how about:

    function Table_AddRow(tb lName, pos) {
    var tbl = document.getEle mentById(tblNam ­e);
    var rlen = tbl.length;
    if ( pos > rlen || pos < 0 ) pos = -1;

    If 'pos' is not a valid index to the table, it will be set to -1 so that
    the new row will be appended as the last row (which is what I think
    your code is doing). I would also guess that any feature testing for
    getElementById) is done up front before the rest of the script is attempted.
    [color=blue]
    >
    > var today = new Date();
    > var rowID = tblName + today.getTime() + tbl.rows.length ;
    >
    > var row = tbl.insertRow(p os);
    > if(document.all ) row.style.displ ay = 'none';
    > //IE Trouble[/color]

    What is the point of testing document.all? If you want to see if the
    style object is supported, test that:

    if ( row.style) row.style.displ ay = 'none';

    It would also be better to do it once and deal with it once.
    [color=blue]
    > var cellRight = row.insertCell( row.length);
    > cellRight.setAt tribute('id', rowID);[/color]

    Simpler to access the cell attributes directly. And since you know that
    the row has a length of zero (you just created it):

    var cellRight = row.insertCell( 0);
    cellRight.id = rowID;

    Which makes me think 'rowID' should be 'cellID'.
    [color=blue]
    >
    >
    > var el = document.create Element('input' ­);
    > el.setAttribute ('type', 'FILE');
    > el.setAttribute ('name', 'upload_files[]');[/color]

    accessing attribute directly is simpler:

    el.type = 'file';
    el.name = 'upload_files[]';
    [color=blue]
    > if(document.all ) el.style.displa y = 'none';
    > //IE Trouble
    > cellRight.appen dChild(el);
    >
    > if(document.all )
    > {
    > //IE Trouble
    > el.click();[/color]

    el is type 'file', which does not have a 'click' method:

    <URL:http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-2651361>
    [color=blue]
    > if(el.value == '')
    > //IE Trouble
    > {
    > //IE Trouble
    > DeleteCurRow(ro w);[/color]

    Where is 'DeleteCurRow' defined?
    [color=blue]
    > //IE Trouble
    > }
    > //IE Trouble
    > else
    > //IE Trouble
    > {
    > //IE Trouble
    > row.style.displ ay = '';
    > //IE Trouble
    > var fn = document.create Element("strong ­");
    > //IE Trouble
    > fn.innerHTML = el.value;
    > //IE Trouble
    > cellRight.appen dChild(fn);[/color]

    Mixing of DOM methods with innerHTML can be unreliable - it is likely
    better to use one or the other. Since tables are involved, say stick to
    DOM:

    fn.appendChild( document.create TextNode( el.value ));
    cellRight.appen dChild(fn);
    [color=blue]
    > //IE Trouble
    > }
    > //IE Trouble
    > }
    > var spc = document.create Element("span") ­;
    > spc.innerHTML = "&nbsp;";[/color]

    Same here:

    spc.appendChild (document.creat eTextNode('\u00 A0'));

    where \u00A0 will insert a non-breaking space.
    [color=blue]
    > cellRight.appen dChild(spc);
    >
    >
    > var aa = document.create Element("a");
    > aa.setAttribute ('href', 'javascript:;') ;
    > var clickName = new
    > Function("Table _DeleteRowByID( ­'"+tblName+"', '"+rowID+"') ");
    > aa.onclick = clickName;[/color]

    You start using the javascript pseudo protocol in the HREF attribute
    then use an onclick. Why use an 'a' element at all? Why not a span
    with an onclick or an input button?

    You may also be having issues with closures here with references to
    tblName and rowID (I didn't test it but it seems likely).

    You could use (see below):

    aa.onclick = function() { Table_DeleteRow ByID( rowID ); }

    Provided the issue with closure is dealt with.
    [color=blue]
    > aa.innerHTML = "remove";[/color]

    aa.appendChild( document.create TextNode('remov e'));
    [color=blue]
    > cellRight.appen dChild(aa);
    > }
    >
    >
    > function Table_DeleteRow (x)
    > {
    > while (x.tagName.toLo werCase() !='tr')
    > {
    > if(x.parentElem ent)
    > x=x.parentEleme nt;
    > else if(x.parentNode )
    > x=x.parentNode;
    > else
    > return;[/color]

    More efficient:
    while ( x.parentNode && 'tr' != x.nodeName.toLo werCase() ) {
    x = x.parentNode;
    }
    [color=blue]
    > }
    > var rowNum=x.rowInd ex;
    > while (x.tagName.toLo werCase() !='table')
    > {
    > if(x.parentElem ent)
    > x=x.parentEleme nt;
    > else if(x.parentNode )
    > x=x.parentNode;
    > else
    > return;
    > }
    > x.deleteRow(row Num);
    >[/color]

    All of the above can be replaced with:

    if ( 'tr' == x.nodeName.toLo werCase() ) {
    x.parentNode.re moveChild( x );
    }

    But this function is not used anyway.
    [color=blue]
    >
    > }
    >
    >
    > function Table_DeleteRow ByIndex(tblName ­, row)
    > {
    > var tbl = document.getEle mentById(tblNam ­e);
    > x.deleteRow(row );
    >
    >
    > }[/color]

    Does not appear to be used.
    [color=blue]
    >
    >
    > function Table_DeleteRow ByID(tblName, rowID)
    > {
    > var tbl = document.getEle mentById(tblNam ­e);
    > var row = document.getEle mentById(rowID) ­;
    > while (row.tagName.to LowerCase() !='tr')
    > {
    > if(row.parentEl ement)
    > row = row.parentEleme nt;
    > else if(row.parentNo de)
    > row = row.parentNode;
    > else
    > return;
    > }
    > var rowNum = row.rowIndex;
    > tbl.deleteRow(r owNum);
    >
    >
    > }[/color]

    Presuming you want to delete the row with a particular ID, then the
    above can be replaced with:

    function Table_DeleteRow ByID( rowID ) {
    var row = document.getEle mentById( rowID );
    if ( row ) row.parentNode. removeChild( row );
    }

    Which can be used as a generic method to delete any element with an id:

    function deleteElementBy Id( elID ) {
    var el = document.getEle mentById( elID );
    if ( el ) el.parentNode.r emoveChild( el );
    }

    [color=blue]
    >
    >
    > </script>[/color]
    [...]


    --
    Rob

    Comment

    • Kingstonian

      #3
      Re: Gmail - dynamic Add Attachment - Reverse Engineering

      Thanks for the reply.

      I am actually a beginner in JavaScript DOM scripting. Was putting
      together some scripts found on internet and making them to work in the
      way I want (my way of expanding my knowledge of JavaScript).

      After reading your reply, I feel that I have failed to convey my aim in
      my posting. I am actually trying to mimic the gmail's attachment GUi
      element. If you look at it, in IE, it doesnt show the actual browse
      button, instead it just shows the file name that you have added, along
      with a remove link. I wanted to mimic it exactly. Thats why I had to
      put those if(document.all ) checkings.

      I need some time to put together the changes that you have suggested
      and will post the resulting code.


      RobG wrote:[color=blue]
      > JavaScriptRocks wrote:[color=green]
      > > I've been trying to imitate / reverse engineer the add attachment
      > > feature in gmail composer. I managed to do it to say about 80% but its
      > > giving me trouble in IE on WinXP-Sp2. I am using PHP to do the upload.
      > > It works well on Firefox/DeerPark, but in IE, the file selected just
      > > vanishes. You can verify it by commenting the lines marked "//IE
      > > Trouble". Commenting those lines will remove IE specific code, except
      > > the el.click() whick sends the Click event to the newly added file
      > > upload input element.
      > >
      > >
      > > Please someone help me.
      > >
      > >
      > > _______________ __________
      > > Here is the code
      > > -------------------------
      > > <?
      > > include_once('. ./modules/file_­upload/lib_upload.php' );
      > > ?>
      > >
      > > <script language="JavaS cript">[/color]
      >
      > language is depreciated, type is required.
      >[color=green]
      > >
      > > function Table_AddRow(tb lName, pos)
      > > {
      > > if(typeof(pos) == 'undefined')
      > > pos = '_LAST_';
      > >
      > >
      > > var tbl = document.getEle mentById(tblNam ­e);
      > > switch(pos)
      > > {
      > > case '_LAST_':
      > > pos = tbl.rows.length ;
      > > default:
      > > pos = parseInt(pos, 10);
      > > if(isNaN(pos))
      > > {
      > > pos = 0;
      > > }
      > > else if (pos > tbl.rows.length )
      > > {
      > > pos = tbl.rows.length ;
      > > }
      > > }[/color]
      >
      > That appears to be an inordinate amount of code just to check 'pos'.
      > Given that you pass 'pos' from your own code, how about:
      >
      > function Table_AddRow(tb lName, pos) {
      > var tbl = document.getEle mentById(tblNam ­e);
      > var rlen = tbl.length;
      > if ( pos > rlen || pos < 0 ) pos = -1;
      >
      > If 'pos' is not a valid index to the table, it will be set to -1 so that
      > the new row will be appended as the last row (which is what I think
      > your code is doing). I would also guess that any feature testing for
      > getElementById) is done up front before the rest of the script is attempted.
      >[color=green]
      > >
      > > var today = new Date();
      > > var rowID = tblName + today.getTime() + tbl.rows.length ;
      > >
      > > var row = tbl.insertRow(p os);
      > > if(document.all ) row.style.displ ay = 'none';
      > > //IE Trouble[/color]
      >
      > What is the point of testing document.all? If you want to see if the
      > style object is supported, test that:
      >
      > if ( row.style) row.style.displ ay = 'none';
      >
      > It would also be better to do it once and deal with it once.
      >[color=green]
      > > var cellRight = row.insertCell( row.length);
      > > cellRight.setAt tribute('id', rowID);[/color]
      >
      > Simpler to access the cell attributes directly. And since you know that
      > the row has a length of zero (you just created it):
      >
      > var cellRight = row.insertCell( 0);
      > cellRight.id = rowID;
      >
      > Which makes me think 'rowID' should be 'cellID'.
      >[color=green]
      > >
      > >
      > > var el = document.create Element('input' ­);
      > > el.setAttribute ('type', 'FILE');
      > > el.setAttribute ('name', 'upload_files[]');[/color]
      >
      > accessing attribute directly is simpler:
      >
      > el.type = 'file';
      > el.name = 'upload_files[]';
      >[color=green]
      > > if(document.all ) el.style.displa y = 'none';
      > > //IE Trouble
      > > cellRight.appen dChild(el);
      > >
      > > if(document.all )
      > > {
      > > //IE Trouble
      > > el.click();[/color]
      >
      > el is type 'file', which does not have a 'click' method:
      >
      > <URL:http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-2651361>
      >[color=green]
      > > if(el.value == '')
      > > //IE Trouble
      > > {
      > > //IE Trouble
      > > DeleteCurRow(ro w);[/color]
      >
      > Where is 'DeleteCurRow' defined?
      >[color=green]
      > > //IE Trouble
      > > }
      > > //IE Trouble
      > > else
      > > //IE Trouble
      > > {
      > > //IE Trouble
      > > row.style.displ ay = '';
      > > //IE Trouble
      > > var fn = document.create Element("strong ­");
      > > //IE Trouble
      > > fn.innerHTML = el.value;
      > > //IE Trouble
      > > cellRight.appen dChild(fn);[/color]
      >
      > Mixing of DOM methods with innerHTML can be unreliable - it is likely
      > better to use one or the other. Since tables are involved, say stick to
      > DOM:
      >
      > fn.appendChild( document.create TextNode( el.value ));
      > cellRight.appen dChild(fn);
      >[color=green]
      > > //IE Trouble
      > > }
      > > //IE Trouble
      > > }
      > > var spc = document.create Element("span") ­;
      > > spc.innerHTML = "&nbsp;";[/color]
      >
      > Same here:
      >
      > spc.appendChild (document.creat eTextNode('\u00 A0'));
      >
      > where \u00A0 will insert a non-breaking space.
      >[color=green]
      > > cellRight.appen dChild(spc);
      > >
      > >
      > > var aa = document.create Element("a");
      > > aa.setAttribute ('href', 'javascript:;') ;
      > > var clickName = new
      > > Function("Table _DeleteRowByID( ­'"+tblName+"', '"+rowID+"') ");
      > > aa.onclick = clickName;[/color]
      >
      > You start using the javascript pseudo protocol in the HREF attribute
      > then use an onclick. Why use an 'a' element at all? Why not a span
      > with an onclick or an input button?
      >
      > You may also be having issues with closures here with references to
      > tblName and rowID (I didn't test it but it seems likely).
      >
      > You could use (see below):
      >
      > aa.onclick = function() { Table_DeleteRow ByID( rowID ); }
      >
      > Provided the issue with closure is dealt with.
      >[color=green]
      > > aa.innerHTML = "remove";[/color]
      >
      > aa.appendChild( document.create TextNode('remov e'));
      >[color=green]
      > > cellRight.appen dChild(aa);
      > > }
      > >
      > >
      > > function Table_DeleteRow (x)
      > > {
      > > while (x.tagName.toLo werCase() !='tr')
      > > {
      > > if(x.parentElem ent)
      > > x=x.parentEleme nt;
      > > else if(x.parentNode )
      > > x=x.parentNode;
      > > else
      > > return;[/color]
      >
      > More efficient:
      > while ( x.parentNode && 'tr' != x.nodeName.toLo werCase() ) {
      > x = x.parentNode;
      > }
      >[color=green]
      > > }
      > > var rowNum=x.rowInd ex;
      > > while (x.tagName.toLo werCase() !='table')
      > > {
      > > if(x.parentElem ent)
      > > x=x.parentEleme nt;
      > > else if(x.parentNode )
      > > x=x.parentNode;
      > > else
      > > return;
      > > }
      > > x.deleteRow(row Num);
      > >[/color]
      >
      > All of the above can be replaced with:
      >
      > if ( 'tr' == x.nodeName.toLo werCase() ) {
      > x.parentNode.re moveChild( x );
      > }
      >
      > But this function is not used anyway.
      >[color=green]
      > >
      > > }
      > >
      > >
      > > function Table_DeleteRow ByIndex(tblName ­, row)
      > > {
      > > var tbl = document.getEle mentById(tblNam ­e);
      > > x.deleteRow(row );
      > >
      > >
      > > }[/color]
      >
      > Does not appear to be used.
      >[color=green]
      > >
      > >
      > > function Table_DeleteRow ByID(tblName, rowID)
      > > {
      > > var tbl = document.getEle mentById(tblNam ­e);
      > > var row = document.getEle mentById(rowID) ­;
      > > while (row.tagName.to LowerCase() !='tr')
      > > {
      > > if(row.parentEl ement)
      > > row = row.parentEleme nt;
      > > else if(row.parentNo de)
      > > row = row.parentNode;
      > > else
      > > return;
      > > }
      > > var rowNum = row.rowIndex;
      > > tbl.deleteRow(r owNum);
      > >
      > >
      > > }[/color]
      >
      > Presuming you want to delete the row with a particular ID, then the
      > above can be replaced with:
      >
      > function Table_DeleteRow ByID( rowID ) {
      > var row = document.getEle mentById( rowID );
      > if ( row ) row.parentNode. removeChild( row );
      > }
      >
      > Which can be used as a generic method to delete any element with an id:
      >
      > function deleteElementBy Id( elID ) {
      > var el = document.getEle mentById( elID );
      > if ( el ) el.parentNode.r emoveChild( el );
      > }
      >
      > [color=green]
      > >
      > >
      > > </script>[/color]
      > [...]
      >
      >
      > --
      > Rob[/color]

      Comment

      • Kingstonian

        #4
        Re: Gmail - dynamic Add Attachment - Reverse Engineering

        Many Thanks RobG

        I have updated the code with your suggestions.
        But its still not working the way I want it to.

        As you had said,[color=blue]
        > el is type 'file', which does not have a 'click' method:[/color]

        but how Gmail does it??!! Thats what I want.
        Here in this case, it accepts the click() event but it acts funny.

        _______________ _______________ ____________

        <?
        include_once('l ib_upload.php') ;
        ?>
        <SCRIPT type="text/javascript">

        function DynUploads_Add( tblName, pos)
        {
        var tbl = document.getEle mentById(tblNam e);
        var rowLen = tbl.rows.length ;

        pos = parseInt(pos, 10);
        pos = (pos >= 0 || pos < rowLen)? pos: pos = -1;

        var today = new Date();
        var uploadID = tblName + today.getTime() + tbl.rows.length ;

        var row = tbl.insertRow(p os);
        // cosmetic: specific to IE - Hide row till user selects a file
        if(document.all ) row.style.displ ay = 'none';

        var cellRight = row.insertCell( row.length);
        cellRight.id = uploadID;

        var el = document.create Element('input' );
        el.type = 'FILE';
        el.name = 'upload_files[]';
        // cosmetic: specific to IE - Hide control for ever, Gmail style
        if(document.all ) el.style.displa y = 'none';

        cellRight.appen dChild(el);

        // cosmetic: specific to IE
        // Instead of the text box and Browse button of the upload form
        element
        // hide the form element and show its value.
        if(document.all )
        {
        el.click();
        if(el.value == '')
        {
        DeleteCurRow(ro w);
        }
        else
        {
        row.style.displ ay = '';
        var fn = document.create Element("strong ");
        fn.appendChild( document.create TextNode( el.value ));
        cellRight.appen dChild(fn);
        }
        }

        // cosmetic: just for space
        cellRight.appen dChild(document .createTextNode ('\u00A0'));

        var aa = document.create Element("a");
        aa.href = 'javascript:;';
        aa.onclick = function() { DynUploads_Dele teByID(tblName, uploadID); };
        aa.appendChild( document.create TextNode('remov e'));
        cellRight.appen dChild(aa);
        }

        function DynUploads_Dele te(x)
        {
        while ( x.parentNode && 'tr' != x.nodeName.toLo werCase() ) {
        x = x.parentNode;
        }

        if ( 'tr' == x.nodeName.toLo werCase() ) {
        x.parentNode.re moveChild( x );
        }
        }

        function DynUploads_Dele teByIndex(tblNa me, row)
        {
        var tbl = document.getEle mentById(tblNam e);
        x.deleteRow(row );
        }

        function DynUploads_Dele teByID(tblName, uploadID)
        {
        var row = document.getEle mentById(upload ID);
        if(row) row.parentNode. removeChild(row );
        }

        </script>

        <form method="post" enctype="multip art/form-data">
        <table id="tblSample" border="0" cellspacing="2" cellpadding="2" >
        <tr id="tblSample_r ow0">
        <td><a href="javascrip t:;" onClick="DynUpl oads_Add('tblSa mple', 1);
        return false;">Add File</a></td>
        </tr></table><input type="hidden" name="MAX_FILE_ SIZE"
        value="512000"> <input type=submit>
        </form>

        Comment

        • Gavin
          New Member
          • Aug 2005
          • 2

          #5
          actually... much easier than you think

          Actually the code to reproduce Gmail's dynamic file attachment forms is a lot easier than you're making it out to be. I have just recently made a very small example of the code required to generate all the necessary elements. :)

          try this - A working example and sourcecode.
          Last edited by Niheel; Aug 11 '05, 03:11 AM.

          Comment

          • witmatix
            New Member
            • Aug 2005
            • 1

            #6
            Great Script, Slight Error

            Great contribution, Gavin. I've been itchin' to reverse-engineer Gmail add attachment script, but ran across this forum first :).

            There is a sligt error in your script, though. When you remove a file input from your div tags, you decrement the form_count variable and then check to see if it equals 0 to change the more link to show the appropriate text:

            Code:
            //remove file attachment form and associated elements
            function remove(remove_form_num)
            {
            	//decrease the form count
            	form_count--;
            
            	//remove < input > element attachment
            	document.getElementById('content').removeChild(document.getElementById('child_attachment_' + remove_form_num));
            	//remove < span > element text
            	document.getElementById('content').removeChild(document.getElementById('child_attachment_text_' + remove_form_num));
            	//remove < img > element image
            	document.getElementById('content').removeChild(document.getElementById('child_attachment_img_' + remove_form_num));
            
            	//if all forms are removed, change text back to "Attach a file"
            	if (form_count == 0)
            	{
                 	  	document.getElementById('more').innerHTML = 'Attach a file';
            	}
            }
            This gets you into a little bind if you remove any element other than the last one, because if you do, you end up with two input boxes with the same name! If you look at gmails code, they use an always incremented value for their input names. So a better way to do it is to check if "content" has any children with the "hasChildNo des" method:

            Code:
            //remove file attachment form and associated elements
            function remove(remove_form_num)
            {
            	//remove < input > element attachment
            	document.getElementById('content').removeChild(document.getElementById('child_attachment_' + remove_form_num));
            	//remove < span > element text
            	document.getElementById('content').removeChild(document.getElementById('child_attachment_text_' + remove_form_num));
            	//remove < img > element image
            	document.getElementById('content').removeChild(document.getElementById('child_attachment_img_' + remove_form_num));
            
            	//if all forms are removed, change text back to "Attach a file"
            	if (document.getElementById('content').hasChildNodes)
            	{
                 	  	document.getElementById('more').innerHTML = 'Attach a file';
            	}
            }
            --Jon
            -----------

            -----------


            Originally posted by Gavin
            Actually the code to reproduce Gmail's dynamic file attachment forms is a lot easier than you're making it out to be. I have just recently made a very small example of the code required to generate all the necessary elements. :)

            try this - A working example and sourcecode.
            Last edited by witmatix; Aug 17 '05, 06:56 AM.

            Comment

            • Digital
              New Member
              • Aug 2005
              • 2

              #7
              Troubles

              I'm having trouble getting this to work. For some reason PHP is not seeing the newly created form elements upon POST. To test I print out the $_POST Array to see what elements posted. Before you freakout and say, type=file are a part of the files array, I know so I've also added a text box for a description of the file. Here is my function:
              Code:
              function addVideo(form){
                currentVids = parseInt(form.addVids.value);
                var new_attachment = document.createElement('input');
                var new_description = document.createElement('input');
                var ln_break = document.createElement('br');
                new_description.setAttribute('id', 'addVid' + currentVids+ 'Description');
                new_description.setAttribute('type', 'text');
                new_description.setAttribute('size', '32');
                new_attachment.setAttribute('id', 'addVid' + currentVids);
                new_attachment.setAttribute('type', 'file');
                document.getElementById('addVids').appendChild(ln_break);
                document.getElementById('addVids').appendChild(new_description);
                document.getElementById('addVids').appendChild(ln_break);
                document.getElementById('addVids').appendChild(new_attachment);
                form.addVids.value = currentVids + 1;
              }
              I'm a little confused about this, and a little irritated, if anyone can help me figure out why the descriptions aren't being appended to the post array it would be greatly appreciated.

              Comment

              • Digital
                New Member
                • Aug 2005
                • 2

                #8
                Figured it out

                I realized my mistake, I forgot to add the name attribute to the elements. :D

                Comment

                Working...