/** * h5ile JavaScript helper utility library for the HTML5 File API * * Version: 0.1 * * Copyright (c) 2010 Marat Nepomnyashy maratbn@gmail * All rights reserved. * * Module: h5ile.js * * Description: JavaScript helper utility library for the HTML5 File API * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * */ window.h5ile = { /** * Creates a visible HTML form field file input tag, prepares it to load * files, and appends it as a child to the DOM element with the DOM id * specified. * * @param parent Either the parent DOM element to which * to append the new HTML form field file * input tag, or its DOM id. * * @param params JSON structure with file processing * parameters. * * params.loadtext Sub-field for text loading settings. * * params.loadtext.onloadend * Callback function for when a file has * been loaded as regular text. * 1st parameter: 'File' object read. * 2nd parameter: 'FileReader' object. * * params.loadtext.onprogress * Callback function for the HTML5 File * API 'onprogress' event. * 1st parameter: 'ProgressEvent' * object. * * @returns the created tag. */ createVisibleFileInput: function(parent, params) { if (!parent) throw new Error("h5ile: Expected parent element or id."); if (!(parent instanceof HTMLElement)) parent = document.getElementById(parent); if (!parent) throw new Error("h5ile: Invalid parent element"); var elInput = document.createElement('input'); elInput.setAttribute('type', 'file'); this.prepareFileInput(elInput, params); parent.appendChild(elInput); return elInput; }, /** * @returns Boolean true if the platform supports HTML 5 File API. */ isFileAPISupported: function() { return window.File && window.FileReader ? true : false; }, /** * Prepares the HTML form field file input tag element specified to load * files. * * @param elInput Reference to the form field file input * tag element to prepare. * * @param params JSON structure with file processing * parameters. * * params.loadtext Sub-field for text loading settings. * * params.loadtext.onloadend * Callback function for when a file has * been loaded as regular text. * 1st parameter: 'File' object read. * 2nd parameter: 'FileReader' object. * * params.loadtext.onprogress * Callback function for the HTML5 File * API 'onprogress' event. * 1st parameter: 'ProgressEvent' * object. */ prepareFileInput: function(elInput, params) { // Based on: // https://developer.mozilla.org/en/using_files_from_web_applications var _processChange = function() { if (!this.files) { throw new Error("h5ile: Expected 'FileList' object, but" + " did not get any"); } for (var i = 0; i < this.files.length; i++) { var file = this.files[i]; if (!file) continue; if (params && params.loadtext && params.loadtext.onloadend) { function _onError() { alert("h5ile: Encountered error loading text file '" + file.name + "'."); } var reader = new FileReader(); reader.onabort = function() { alert("h5ile: Aborted text loading of file '" + file.name + "'."); } reader.onerror = function() { _onError(); } reader.onloadend = function() { if (params.loadtext.onloadend) { params.loadtext.onloadend(file, reader); } } reader.onprogress = function(progress_event) { if (params.loadtext.onprogress) { params.loadtext.onprogress( progress_event); } } try { reader.readAsText(file); } catch (e) { _onError(); if (params.loadtext.onloadend) { params.loadtext.onloadend(null, null); } } } } } if (elInput.addEventListener) { elInput.addEventListener('change', _processChange, false); } else if (elInput.attachEvent) { elInput.attachEvent('change', _processChange); } else { throw new Error("h5ile: Expected either 'addEventListener' or 'attachEvent'."); } }, /** * Reads text from the file specified, and splits it into lines. * * @returns Object with info on lines. */ splitIntoLines: function(file_reader) { var arrSplit = file_reader && file_reader.result && file_reader.result.split(/(\r\n|\r|\n)/); // The 'arrSplit' variable is an array where the line text and the // line delimiters are at separate indices. Need to process that into // another array where the line delimiters are appended to the ends of // their associated lines. var arrLines = []; var strLine = ""; for (var i = 0; i < arrSplit.length; i ++) { strLine += arrSplit[i]; if (strLine.match(/\r\n|\r|\n/)) { arrLines.push(strLine); strLine = ""; } } if (strLine) arrLines.push(strLine); function _getLine(indexLine) { return arrLines && arrLines.length > indexLine && arrLines[indexLine] || ""; } function _getLineStripped(indexLine) { var strLine = _getLine(indexLine); var arrBreakdownForLine = strLine && strLine.match(/^\s*(.+\S+)\s*$/); return arrBreakdownForLine && arrBreakdownForLine.length == 2 && arrBreakdownForLine[1] || null; } var _arrLineTokens = []; function _insureTokenLineExists(indexLine) { if (_arrLineTokens[indexLine]) return; var strLineStripped = _getLineStripped(indexLine); _arrLineTokens[indexLine] = strLineStripped && strLineStripped.split(/\s/) || []; } return { /** * Total number of characters in the text file. */ totalChars: file_reader && file_reader.result && file_reader.result.length || 0, /** * Total number of lines in the text file. * * The text file is stored line by line rather than in a * single string, as otherwise a huge file in a huge string * would overwhelm the browser's JavaScript engine. */ totalLines: arrLines && arrLines.length || 0, /** * Returns a single line out of the text file, including the * line termination carriage return character sequence. * * @param indexLine Number 0-based index of the line. */ getLine: function(indexLine) { return _getLine(indexLine); }, /** * Returns a single line out of the text file, stripped of * the whitespace bounding it. * * @param indexLine Number 0-based index of the line. */ getLineStripped: function(indexLine) { return _getLineStripped(indexLine); }, /** * Returns the total number of non-whitespace tokens on the * line with the index specified. * * @param indexLine Number 0-based index of the line. */ getTotalTokensOnLine: function(indexLine) { _insureTokenLineExists(indexLine); return _arrLineTokens[indexLine].length; }, /** * Returns a single non-whitespace token with the index * specified on the line with the index specified. * * @param indexToken Number 0-based index of the token on the line. * @param indexLine Number 0-based index of the line. * * Note that the token index is specified before the line * index. */ getTokenOnLine: function(indexToken, indexLine) { _insureTokenLineExists(indexLine); return _arrLineTokens[indexLine][indexToken]; } } } }