From: HyungKyu Song Date: Fri, 15 Feb 2013 06:11:57 +0000 (+0900) Subject: Tizen 2.0 Release X-Git-Tag: 2.0_release^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4dbcd4fc8f43e4f3c07b4b944caeb0632660cb64;p=samples%2Fweb%2FFileManager.git Tizen 2.0 Release --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a447a9f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Pawel Sierszen +Piotr Wronski +Dariusz Paziewski +Tomasz Lukawski +Tomasz Paciorek +Aniela Rudy-Gawecka diff --git a/LICENSE.Flora b/LICENSE.Flora new file mode 100644 index 0000000..9c95663 --- /dev/null +++ b/LICENSE.Flora @@ -0,0 +1,206 @@ +Flora License + +Version 1.0, May, 2012 + +http://floralicense.org/license/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and +all other entities that control, are controlled by, or are +under common control with that entity. For the purposes of +this definition, "control" means (i) the power, direct or indirect, +to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice +that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, +or merely link (or bind by name) to the interfaces of, the Work and +Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the copyright owner or by an individual or +Legal Entity authorized to submit on behalf of the copyright owner. +For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or +its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue +tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding +communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +"Tizen Certified Platform" shall mean a software platform that complies +with the standards set forth in the Compatibility Definition Document +and passes the Compatibility Test Suite as defined from time to time +by the Tizen Technical Steering Group and certified by the Tizen +Association or its designated agent. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work +solely as incorporated into a Tizen Certified Platform, where such +license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work solely +as incorporated into a Tizen Certified Platform to which such +Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent infringement, +then any patent licenses granted to You under this License for that +Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof pursuant to the copyright license +above, in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + 3. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: + within a NOTICE text file distributed as part of the Derivative Works; + within the Source form or documentation, if provided along with the + Derivative Works; or, within a display generated by the Derivative Works, + if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only + and do not modify the License. + +You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text +from the Work, provided that such additional attribution notices +cannot be construed as modifying the License. You may add Your own +copyright statement to Your modifications and may provide additional or +different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works +as a whole, provided Your use, reproduction, and distribution of +the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Flora License to your work + +To apply the Flora License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Flora License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://floralicense.org/license/ + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..85044e4 --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Flora License, Version 1. +Please, see the LICENSE.Flora file for Flora License terms and conditions. + diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..87d62ba --- /dev/null +++ b/config.xml @@ -0,0 +1,14 @@ + + + + + FileManager + + + + + + + + + diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..50aca9e --- /dev/null +++ b/css/style.css @@ -0,0 +1,174 @@ +* { + margin: 0px; + padding: 0px; +} + +body { + overflow: hidden; +} + +#fileList { + margin: 0; +} + +#mainTitle { + width: 260px; +} + +#fileList > li { + padding-top: 0.3rem; + padding-bottom: 0.3rem; + border-top: solid 1px #ddd; +} + +#fileList > li > span.nodename { + display: inline-block; + position: absolute; + line-height: 32px; + white-space: nowrap; + text-overflow: ellipsis; + width: 75%; + overflow: hidden; + margin-top: 5px; +} + +#fileList > li.gradientBackground > span.nodename { + color: #fff !important; +} + +#fileList > li.file img { + width: 32px; + height: 32px; +} + +#fileList > li.folder img { + margin-top: 0.1rem; +} + +#fileList > li.levelUp { + padding-left: 47px !important; + height: 32px; +} + +.selectAll { + padding-left: 10px; + display: inline-block; +} + +.selectAll span.ui-icon { + top: 40% !important; +} + +.selectAll span.ui-btn-text { + padding-left: 1.5rem !important; +} + +#navbar { + height: 16px; + padding: 2px 10px; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background-color: #EEE; + border-top: solid 1px #DDD; + direction: rtl; + text-align: left; +} + +.ui-pathDiv { + position: absolute; + top: 49px; + left: 0px; + right: 0px; + bottom: 0px; +} + +#pathDiv { + padding: 2px 0px 0px 5px; + border: 0px; +} + +#pathDiv .ui-li-text-main { + font-size: 18px; +} + +#morePopup td.text { + padding: 5px; +} + +.ui-header.ui-bar-s .ui-btn.standard { + width: 100%; + height: 100%; +} + +.ui-li-1line-bigicon1.ui-li.ui-li-static.ui-body-s.ui-li-has-thumb { + padding-left: 0.7rem; + padding-right: 0rem; +} + +.my-ui-checkbox { + display: inline-block; + margin-top: 0rem; + margin-right: 0rem; + position: relative !important; + top: -0.7rem; + left: -1.5rem; +} + +ul.ui-listview > li.ui-li-1line-bigicon1 img.ui-li-bigicon { + display: inline-block; + margin-top: 0rem; + margin-right: 0.7rem; + position: relative; + left: 5px; +} + +.ui-checkbox .ui-btn.ui-btn-icon-left .ui-btn-inner { + line-height: 1.1rem; + padding: 0 0 0 0rem; + width: 30px; +} + +.ui-checkbox .ui-btn.ui-btn-icon-left .ui-btn-inner.ui-btn-hastxt { + width: 100%; +} + +.ui-btn-corner-all { + -webkit-border-radius: 0px; + bordert-radius: 0px; +} + +.ui-content.ui-scrollview-clip > div.ui-scrollview-view { + padding: 0px; +} + +input.ui-input-text.new_folder { + width: 100%; + height: 50px; + padding: 0 0 0 .4em; +} + +.gradientBackground { + background: -webkit-linear-gradient(top, #5A99BA 0%, #205473 100%) !important; /* from tizen-white */ +} + +.hidden { + display: none !important; +} + +.vhidden { + visibility: hidden !important; +} + +.ui-tabbar a { + color: #999 !important; +} + +#addFolderPopup span.ui-btn-inner { + width: 100px; +} + +#newFolderName { + margin:0.4em; +} \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100755 index 0000000..983c883 Binary files /dev/null and b/icon.png differ diff --git a/images/00_winset_Back.png b/images/00_winset_Back.png new file mode 100644 index 0000000..780e24e Binary files /dev/null and b/images/00_winset_Back.png differ diff --git a/images/etc.png b/images/etc.png new file mode 100755 index 0000000..26748d8 Binary files /dev/null and b/images/etc.png differ diff --git a/images/folder.png b/images/folder.png new file mode 100755 index 0000000..c8395f5 Binary files /dev/null and b/images/folder.png differ diff --git a/images/img.png b/images/img.png new file mode 100755 index 0000000..4dd3be3 Binary files /dev/null and b/images/img.png differ diff --git a/images/music.png b/images/music.png new file mode 100755 index 0000000..449d29d Binary files /dev/null and b/images/music.png differ diff --git a/images/pdf.png b/images/pdf.png new file mode 100755 index 0000000..2480d81 Binary files /dev/null and b/images/pdf.png differ diff --git a/images/ppt.png b/images/ppt.png new file mode 100755 index 0000000..42c1100 Binary files /dev/null and b/images/ppt.png differ diff --git a/images/text.png b/images/text.png new file mode 100755 index 0000000..c937ef8 Binary files /dev/null and b/images/text.png differ diff --git a/images/video.png b/images/video.png new file mode 100755 index 0000000..9786439 Binary files /dev/null and b/images/video.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..9bd228a --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + file manager + + + + + + + + +
+ + \ No newline at end of file diff --git a/js/app.clipboard.js b/js/app.clipboard.js new file mode 100644 index 0000000..8b150f7 --- /dev/null +++ b/js/app.clipboard.js @@ -0,0 +1,113 @@ +/*jslint devel: true*/ +/*global $*/ + +/** + * @class Config + */ +function Clipboard() { + 'use strict'; + this.mode = this.INACTIVE_MODE; +} + +(function () { // strict mode wrapper + 'use strict'; + Clipboard.prototype = { + /** + * Clipboard mode for copying + */ + COPY_MODE_ID: 0, + + /** + * Clipboard mode for moving + */ + MOVE_MODE_ID: 1, + + /** + * Clipbboard inactive mode + */ + INACTIVE_MODE: -1, + + /** + * Clipboard data + */ + data: [], + + /** + * Clipboard mode: [copy | move | inactive] + */ + mode: undefined, + + /** + * Returns all paths in clipboard + * @returns {array} + */ + get: function Clipboard_get() { + return this.data; + }, + + /** + * Add new path to clipboard + * @param {array} paths aray of full paths + * @returns {number} current length of clipboard objects + */ + add: function Clipboard_add(paths) { + var len = paths.length, + i; + + // clear clipboard + this.clear(); + for (i = 0; i < len; i += 1) { + if (this.has(paths[i]) === false) { + this.data.push(paths[i]); + } + } + + return this.data.length; + }, + + /** + * Checks if specified path is already in clipboard + * @param {string} path full path + * @returns {boolean} + */ + has: function Clipboard_has(path) { + return $.inArray(path, this.data) === -1 ? false : true; + }, + + /** + * Clears all clipboard data and resets clipboard mode + */ + clear: function Clipboard_clear() { + this.data = []; + this.mode = this.INACTIVE_MODE; + }, + + /** + * Sets clipboard mode + * @param {number} mode + * @returns {boolean} + */ + setMode: function Clipboard_setMode(mode) { + if ($.inArray(mode, [this.MOVE_MODE_ID, this.COPY_MODE_ID]) === false) { + console.error('Incorrect clipboard mode'); + return false; + } + this.mode = mode; + return true; + }, + + /** + * @returns {number} mode Clipboard mode + */ + getMode: function Clipboard_getMode() { + return this.mode; + }, + + /** + * @returns {boolean} + */ + isEmpty: function Clipboard_isEmpty() { + return this.data.length === 0; + } + }; +}()); diff --git a/js/app.config.js b/js/app.config.js new file mode 100644 index 0000000..e0332c3 --- /dev/null +++ b/js/app.config.js @@ -0,0 +1,28 @@ +/** + * @class Config + */ +function Config() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Config.prototype = { + + properties: { + 'templateDir': 'templates', + 'templateExtension': '.tpl' + }, + + /** + * Returns config value + */ + get: function (value, defaultValue) { + + if (this.properties.hasOwnProperty(value)) { + return this.properties[value]; + } + return defaultValue; + } + }; +}()); diff --git a/js/app.helpers.js b/js/app.helpers.js new file mode 100644 index 0000000..6998571 --- /dev/null +++ b/js/app.helpers.js @@ -0,0 +1,123 @@ +/*jslint devel: true*/ +/*global $ */ + +/** + * @class Helpers + */ +function Helpers() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Helpers.prototype = { + + /** + * Capitalise the first letter + * + * @param {string} text + * @returns {string} + */ + UCFirst: function Helpers_UCFirst(text) { + return text.charAt(0).toUpperCase() + text.slice(1); + }, + + /** + * @param {string} fileName + * @returns {string} extension for specified file name + */ + getFileExtension: function Helpers_getFileExtension(fileName) { + var splittedFileName = fileName.split('.'), + ext = ''; + + if (splittedFileName.length > 1) { + ext = '.' + splittedFileName.pop(); + } + return ext; + }, + + /** + * Return icon filename for the given extension. + * For example, for '.mp3' returns 'music.png' + * + * @param {string} ext + * @return {string} + */ + resolveFileIcon: function Helpers_resolveFileIcon(ext) { + + ext = ext.toLowerCase(); + + switch (ext) { + case '.jpg': + return 'img.png'; + case '.png': + return 'img.png'; + case '.gif': + return 'img.png'; + case '.pdf': + return 'pdf.png'; + case '.mp3': + return 'music.png'; + case '.avi': + return 'video.png'; + case '.mp4': + return 'video.png'; + case '.ppt': + return 'ppt.png'; + case '.txt': + return 'text.png'; + case '.doc': + return 'text.png'; + case '.xls': + return 'text.png'; + case '.directory': + return 'folder.png'; + default: + return 'etc.png'; + } + }, + + /** + * Resolve file extension to MIME type + * + * @param {string} ext File extension + * @returns {string} + */ + resolveMimeType: function Helpers_resolveMimeType(ext) { + var mime = ''; + + ext = ext.toLowerCase(); + + if (ext === '.jpg' || ext === '.png' || ext === '.gif') { + mime = 'image/*'; + } else if (ext === '.mp4' || ext === '.ogv' || ext === '.avi') { + mime = 'video/*'; + } else if (ext === '.mp3') { + mime = 'audio/mp3'; + } else if (ext === '.txt' || ext === '.doc' || ext === '.html' || ext === '.ppt' || ext === '.xls' || ext === '.pdf') { + mime = 'text/*'; + } + + return mime; + }, + + /** + * Returns thumbnail URI for specified file + * @param {string} fileName + * @param {File} node + * @returns {string} + */ + getThumbnailURI: function Helpers_getThumbnailURI(fileName, node) { + var ext = this.getFileExtension(fileName), + thumbnailURI = ''; + + if (!node.thumbnailURIs) { + thumbnailURI = 'images/' + this.resolveFileIcon(ext); + } else if (node.thumbnailURIs[0] && $.inArray(ext, ['.mp4', '.jpg', '.png', '.gif'])) { + thumbnailURI = node.thumbnailURIs[0]; + } + + return thumbnailURI; + } + }; +}()); diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..690489c --- /dev/null +++ b/js/app.js @@ -0,0 +1,239 @@ +/*jslint devel: true*/ +/*global tizen, $, app, Ui, Model, Helpers, Config, Clipboard*/ + +var App = null; + +(function () { // strict mode wrapper + 'use strict'; + + /** + * Creates a new application object + * + * @class Application + * @constructor + */ + App = function App() { + }; + + App.prototype = { + /** + * @type Array + */ + requires: ['js/app.config.js', 'js/app.model.js', 'js/app.ui.js', 'js/app.ui.templateManager.js', 'js/app.systemIO.js', 'js/app.helpers.js', 'js/app.clipboard.js'], + + /** + * @type Model + */ + model: null, + + /** + * @type Ui + */ + ui: null, + + /** + * @type Config + */ + config: null, + + /** + * @type SystemIO + */ + systemIO: null, + + /** + * @type Helpers + */ + helpers: null, + + /** + * @type {string} + */ + currentPath: '', + + /** + * + */ + currentDirHandle: null, + + /** + * @type {Clipboard} + */ + clipboard: null, + + /** + * Initialization + */ + init: function App_init() { + this.config = new Config(); + this.model = new Model(); + this.ui = new Ui(); + this.helpers = new Helpers(); + this.clipboard = new Clipboard(); + + this.model.loadInternalStorages(this.initUi.bind(this)); + this.addEvents(); + }, + + /** + * UI initialization + */ + initUi: function App_initUi() { + this.ui.init(this.model.getInternalStorages()); + }, + + /** + * Add pages events + */ + addEvents: function App_addEvents() { + var self = this; + document.addEventListener('webkitvisibilitychange', function () { self.refreshCurrentPage(); }); + }, + + /** + * Displays media storages + */ + displayStorages: function App_displayStorages() { + this.ui.scrollContentTo(0); + this.ui.displayStorages(this.model.getInternalStorages()); + }, + + /** + * Displays specified folder + * @param {string} path + */ + displayFolder: function App_displayFolder(path, refresh) { + var self = this; + + // get folder data and push into rendering method + this.model.getFolderData(path, function (dir, nodes) { + // on success + + // update current path + self.currentPath = path; + + // update current dir handle + self.currentDirHandle = dir; + + // display folder UI + if (refresh === undefined) { + self.ui.scrollContentTo(0); + } + self.ui.displayFolder(path, nodes); + }); + }, + + /** + * Opens specified file + * @params {string} uri File URI + */ + openFile: function App_openFile(uri, fullUri) { + var ext = this.helpers.getFileExtension(uri), + mime = this.helpers.resolveMimeType(ext); + + if (mime !== '') { + this.model.openFile(fullUri, mime); + } else { + console.error('Unsupported mime type for extension ' + ext); + } + }, + + /** + * Displays parent location + */ + goLevelUp: function App_goLevelUp() { + // split current path and get proper path for parent location + var newPath = this.currentPath.split('/').slice(0, -1).join('/'); + + if (newPath !== '') { + this.displayFolder(newPath); + } else { + this.displayStorages(); + } + }, + + /** + * creates new dir in currently viewed dir + * @param {string} dirName + */ + createDir: function App_createDir(dirName) { + + if (this.currentDirPath !== '') { + try { + this.currentDirHandle.createDirectory(dirName); + } catch (e) { + alert(e.message); + } + this.refreshCurrentPage(); + } else { + alert("You can't create new nodes in the main view"); + } + }, + + /** + * Triggers refresh current page + */ + refreshCurrentPage: function App_refreshCurrentPage() { + if (this.currentPath !== '') { + this.displayFolder(this.currentPath, true); + } else { + this.displayStorages(); + } + }, + + /** + * Deletes nodes with specified paths + * @param {string[]} nodes nodePaths + */ + deleteNodes: function App_deleteNodes(nodes) { + this.model.deleteNodes(nodes, this.currentDirHandle, this.ui.removeNodeFromList.bind(this.ui)); + }, + + /** + * @param {string[]} paths filepaths + * @param {number} mode clipboard mode + */ + saveToClipboard: function App_saveToClipboard(paths, mode) { + this.clipboard.add(paths); + this.clipboard.setMode(mode); + this.ui.refreshPasteActionBtn(this.clipboard.isEmpty()); + }, + + /** + * Paste nodes from clipboard to current dir + */ + pasteClipboard: function App_pasteClipboard() { + var clipboardData = this.clipboard.get(); + + if (clipboardData.length === 0) { + alert('Clipboard is empty'); + return false; + } + + if (this.clipboard.getMode() === this.clipboard.COPY_MODE_ID) { + this.model.copyNodes(this.currentDirHandle, clipboardData, this.currentPath, this.onPasteClipboardSuccess.bind(this)); + } else { + this.model.moveNodes(this.currentDirHandle, clipboardData, this.currentPath, this.onPasteClipboardSuccess.bind(this)); + } + + this.ui.refreshPasteActionBtn(this.clipboard.isEmpty()); + + return true; + }, + + /** + * Handler for paste clipboard success + */ + onPasteClipboardSuccess: function App_onPasteClipboardSuccess() { + this.clipboard.clear(); + this.refreshCurrentPage(); + }, + + /** + * App exit + */ + exit: function App_exit() { + tizen.application.getCurrentApplication().exit(); + } + }; +}()); diff --git a/js/app.model.js b/js/app.model.js new file mode 100644 index 0000000..16d6c13 --- /dev/null +++ b/js/app.model.js @@ -0,0 +1,255 @@ +/*jslint devel: true*/ +/*global tizen, SystemIO, $ */ + +/** + * @class Model + */ +function Model() { + 'use strict'; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + Model.prototype = { + + /** + * @type SystemIO + */ + systemIO: null, + + /** + * @type Array + */ + storages: [], + + /** + * API module initialisation + */ + init: function Model_init() { + this.systemIO = new SystemIO(); + }, + + /** + * @returns {FileSystemStorage[]} storages + */ + getInternalStorages: function Model_getInternalStorages() { + return this.storages; + }, + + /** + * Saves storages + * @param {function} onSuccess callback + */ + loadInternalStorages: function Model_loadInternalStorages(onSuccess) { + var self = this; + + this.systemIO.getStorages('INTERNAL', function (storages) { + self.storages = storages; + if (typeof onSuccess === 'function') { + onSuccess(); + } + }, 'internal0'); + }, + + /** + * Returns folder data + * @param {string} path Node path + * @param {function} onSuccess Success callback + * @param {function} onError Error callback + */ + getFolderData: function Model_getFolderData(path, onSuccess, onError) { + + var onOpenSuccess = function (dir) { + dir.listFiles( + function (files) { + onSuccess(dir, files); + }, + function (e) { + console.error('Model_getFolderData listFiles error', e); + } + ); + }, + onOpenError = function (e) { + console.error('Model_getFolderData openDir error', e); + }; + + this.systemIO.openDir(path, onOpenSuccess, onOpenError); + }, + + /** + * Launch a service associated with 'ext' to launch the 'uri' + * @param {string} ext + * @param {string} uri + * @returns {ApplicationSevice} + */ + openFile: function Model_openFile(fullUri, mime) { + var serviceReplyCB = { + onsuccess: function (reply) { + var num = 0; + for (num = 0; num < reply.data.length; num += 1) { + } + }, + onfailure: function () { + console.error('Launch service failed'); + } + }; + + try { + tizen.application.launchAppControl(new tizen.ApplicationControl( + 'http://tizen.org/appcontrol/operation/view', + fullUri, + mime + ), + null, + function () { }, + function (e) { + alert('launch sevice failed. reason :' + e.message); + }, + serviceReplyCB + ); + } catch (e) { + console.error('openFile error:', e); + } + }, + + /** + * @param {File[]} nodes Collection of node objects + * @param {File} dir Directory handle + * @param {function} onSuccess + * @param {function} onError + */ + deleteNodes: function Model_deleteNodes(nodes, dir, onSuccess, onError) { + var len = nodes.length, + onDeleteNodeSuccess = function (nodeId, isDir) { + if (typeof onSuccess === 'function') { + onSuccess(nodeId); + } + }, + onDeleteNodeError = function (e) { + console.error('Folder delete error', e); + if (typeof onError === 'function') { + onError(); + } + }, + i; + + for (i = 0; i < len; i = i + 1) { + if (nodes[i].folder) { + dir.deleteDirectory( + nodes[i].uri, + true, + onDeleteNodeSuccess.bind(this, nodes[i].id, true), + onDeleteNodeError + ); + } else { + dir.deleteFile( + nodes[i].uri, + onDeleteNodeSuccess.bind(this, nodes[i].id, false), + onDeleteNodeError + ); + } + } + }, + + /** + * Copy specified files to destination path + * Overwrites existing files + * + * @param {File} dir Directory handle + * @param {string[]} paths Array with absolute virtual file paths + * @param {string} destinationPath + * @param {function} onSuccess callback + */ + copyNodes: function Model_copyNodes(dir, paths, destinationPath, onSuccess) { + var len = paths.length, + copied = 0, + onCopyNodeSuccess = function () { + copied += 1; + if (copied === len) { + onSuccess(); + } + }, + onCopyNodeFailure = function () { + alert('Copying error'); + }, + i, + sourceName, + decision; + + this.systemIO.getFilesList(dir, function (filesList) { + for (i = 0; i < len; i = i + 1) { + if (destinationPath.indexOf(paths[i]) !== -1) { + alert('Copying error'); + return; + } + } + + for (i = 0; i < len; i = i + 1) { + decision = true; + sourceName = paths[i].split('/').pop(); + + if ($.inArray(sourceName, filesList) !== -1) { + decision = confirm('A file with (' + sourceName + ') name already exists.\nDo you want to overwrite it?'); + } + + if (decision) { + try { + dir.copyTo(paths[i], destinationPath + '/' + sourceName, true, onCopyNodeSuccess, onCopyNodeFailure); + } catch (e) { + console.error(e); + } + } + } + }); + }, + + /** + * Move specified files to destination path + * Overwrites existing files + * + * @param {File} dir Directory handle + * @param {string[]} paths Array with absolute virtual file paths + * @param {string} destinationPath + * @param {function} onSuccess callback + */ + moveNodes: function Model_moveNodes(dir, paths, destinationPath, onSuccess) { + var len = paths.length, + moved = 0, + onMoveNodeSuccess = function () { + moved += 1; + if (moved === len) { + onSuccess(); + } + }, + onMoveNodeFailure = function () { + alert('Moving error'); + }, + i, + sourceName, + decision; + + this.systemIO.getFilesList(dir, function (filesList) { + for (i = 0; i < len; i = i + 1) { + if (destinationPath.indexOf(paths[i]) !== -1) { + alert('Moving error'); + return; + } + } + + for (i = 0; i < len; i = i + 1) { + decision = true; + sourceName = paths[i].split('/').pop(); + + if ($.inArray(sourceName, filesList) !== -1) { + decision = confirm('A file with (' + sourceName + ') name already exists.\nDo you want to overwrite it?'); + } + + if (decision) { + dir.moveTo(paths[i], destinationPath + '/' + sourceName, true, onMoveNodeSuccess, onMoveNodeFailure); + } + } + }); + } + }; +}()); diff --git a/js/app.systemIO.js b/js/app.systemIO.js new file mode 100644 index 0000000..a3df78b --- /dev/null +++ b/js/app.systemIO.js @@ -0,0 +1,263 @@ +/*jslint devel: true*/ +/*global tizen, localStorage */ + +/** + * @class SystemIO + */ +function SystemIO() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + SystemIO.prototype = { + /** + * Creates new empty file in specified location + * + * @param {File} directoryHandle + * @param {string} fileName + */ + createFile: function SystemIO_createFile(directoryHandle, fileName) { + + try { + return directoryHandle.createFile(fileName); + } catch (e) { + console.error('SystemIO_createFile error:' + e.message); + return false; + } + }, + + /** + * Writes content to file stream + * + * @param {File} fileHandle file handler + * @param {string} fileContent file content + * @param {function} onSuccess on success callback + * @param {function} onError on error callback + * @param {string} content encoding + */ + writeFile: function SystemIO_writeFile(fileHandle, fileContent, onSuccess, onError, contentEncoding) { + onError = onError || function () {}; + + fileHandle.openStream('w', function (fileStream) { + if (contentEncoding === 'base64') { + fileStream.writeBase64(fileContent); + } else { + fileStream.write(fileContent); + } + + fileStream.close(); + + // launch onSuccess callback + if (typeof onSuccess === 'function') { + onSuccess(); + } + }, onError, 'UTF-8'); + }, + + /** + * Opens specified location + * + * @param {string} directory path + * @param {function} on success callback + * @param {function} on error callback + * @param {string} mode + */ + openDir: function SystemIO_openDir(directoryPath, onSuccess, onError, openMode) { + openMode = openMode || 'rw'; + onSuccess = onSuccess || function () {}; + + try { + tizen.filesystem.resolve(directoryPath, onSuccess, onError, openMode); + } catch (e) { + } + }, + + /** + * Parse specified filepath and returns data parts + * + * @param {string} filePath + * @returns {array} + */ + getPathData: function SystemIO_getPathData(filePath) { + var path = { + originalPath: filePath, + fileName: '', + dirName: '' + }, + splittedPath = filePath.split('/'); + + path.fileName = splittedPath.pop(); + path.dirName = splittedPath.join('/') || '/'; + + return path; + }, + + /** + * Save specified content to file + * + * @param {string} file path + * @param {string} file content + * @param {string} file encoding + */ + saveFileContent: function SystemIO_saveFileContent(filePath, fileContent, onSaveSuccess, fileEncoding) { + var pathData = this.getPathData(filePath), + self = this, + fileHandle; + + function onOpenDirSuccess(dir) { + // create new file + fileHandle = self.createFile(dir, pathData.fileName); + if (fileHandle !== false) { + // save data into this file + self.writeFile(fileHandle, fileContent, onSaveSuccess, false, fileEncoding); + } + } + + // open directory + this.openDir(pathData.dirName, onOpenDirSuccess); + }, + + /** + * Deletes node with specified path + * + * @param {string} node path + * @param {function} success callback + */ + deleteNode: function SystemIO_deleteNode(nodePath, onSuccess) { + var pathData = this.getPathData(nodePath), + self = this; + + function onDeleteSuccess() { + onSuccess(); + } + + function onDeleteError(e) { + console.error('SystemIO_deleteNode:_onDeleteError', e); + } + + function onOpenDirSuccess(dir) { + var onListFiles = function (files) { + if (files.length > 0) { + // file exists; + if (files[0].isDirectory) { + self.deleteDir(dir, files[0].fullPath, onDeleteSuccess, onDeleteError); + } else { + self.deleteFile(dir, files[0].fullPath, onDeleteSuccess, onDeleteError); + } + } else { + onDeleteSuccess(); + } + }; + + // check file exists; + dir.listFiles(onListFiles, function (e) { + console.error(e); + }, { + name: pathData.fileName + }); + } + + this.openDir(pathData.dirName, onOpenDirSuccess, function (e) { + console.error('openDir error:' + e.message); + }); + }, + + /** + * Deletes specified file + * + * @param {File} dir + * @param {string} file path + * @param {function} delete success callback + * @param {function} delete error callback + */ + deleteFile: function SystemIO_deleteFile(dir, filePath, onDeleteSuccess, onDeleteError) { + try { + dir.deleteFile(filePath, onDeleteSuccess, onDeleteError); + } catch (e) { + console.error('SystemIO_deleteFile error: ' + e.message); + return false; + } + }, + + /** + * Deletes specified directory + * + * @param {File} dir + * @param {string} dir path + * @param {function} delete success callback + * @param {function} delete error callback + * @returns {boolean} + */ + deleteDir: function SystemIO_deleteDir(dir, dirPath, onDeleteSuccess, onDeleteError) { + try { + dir.deleteDirectory(dirPath, false, onDeleteSuccess, onDeleteError); + } catch (e) { + console.error('SystemIO_deleteDir error:' + e.message); + return false; + } + + return true; + }, + + /** + * @param {string} type storage type + * @param {function} onSuccess on success callback + * @param {string} excluded Excluded storage + */ + getStorages: function SystemIO_getStorages(type, onSuccess, excluded) { + try { + tizen.filesystem.listStorages(function (storages) { + var tmp = [], + len = storages.length, + i; + + if (type !== undefined) { + for (i = 0; i < len; i += 1) { + if (storages[i].label === excluded) { + continue; + } + + if (storages[i].type === 0 || storages[i].type === type) { + tmp.push(storages[i]); + } + } + } else { + tmp = storages; + } + + if (typeof onSuccess === 'function') { + onSuccess(tmp); + } + }); + } catch (e) { + console.error('SystemIO_getStorages error:' + e.message); + } + }, + + getFilesList: function SystemIO_getFilesList(dir, onSuccess) { + try { + dir.listFiles( + function (files) { + var tmp = [], + len = files.length, + i; + + for (i = 0; i < len; i += 1) { + tmp.push(files[i].name); + } + + if (typeof onSuccess === 'function') { + onSuccess(tmp); + } + }, + function (e) { + console.error('SystemIO_getFilesList dir.listFiles() error:', e); + } + ); + } catch (e) { + console.error('SystemIO_getFilesList error:', e.message); + } + } + }; +}()); \ No newline at end of file diff --git a/js/app.ui.js b/js/app.ui.js new file mode 100644 index 0000000..5f8f73e --- /dev/null +++ b/js/app.ui.js @@ -0,0 +1,659 @@ +/*jslint devel: true */ +/*global $, app, TemplateManager, Helpers */ + +/** + * @class Ui + */ +function Ui() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Ui.prototype = { + /** + * UI edit mode + * @type {boolean} + */ + editMode: false, + + /** + * @type {bool} block taps until the page change is completed + */ + nodeTapBlock: false, + + /** + * @type {TemplateManager} + */ + templateManager: null, + + /** + * @type Helpers + */ + helpers: null, + + /** + * @const {number} + */ + PATH_DIV_HEIGHT: 20, + + /** + * @const {number} + */ + SELECT_ALL_HEIGHT: 32, + + /** + * @const {number} header height, set on domReady + */ + HEADER_HEIGHT: 53, + + /** + * name of row gradient class + */ + CSS_GRADIENT_CLASS: 'gradientBackground', + + /** + * Standard tabbar actions + * @type {number} + */ + STD_TABBAR_EDIT_ACTION: 0, + STD_TABBAR_MORE_ACTION: 1, + STD_TABBAR_EXIT_ACTION: 2, + + /** + * Edit tabbar actions + * @type {number} + */ + EDIT_TABBAR_DELETE_ACTION: 0, + EDIT_TABBAR_MOVE_ACTION: 1, + EDIT_TABBAR_COPY_ACTION: 2, + EDIT_TABBAR_CANCEL_ACTION: 3, + + currentHeaderHeight: null, + currentScrollPosition: null, + + /** + * UI Initialization + */ + init: function Ui_init(storages) { + this.templateManager = new TemplateManager(); + this.helpers = new Helpers(); + // Disable text selection + $.mobile.tizen.disableSelection(document); + $(document).ready(this.initDom.bind(this, storages)); + }, + + initDom: function Ui_initDom(storages) { + var self = this; + + this.templateManager.loadToCache(['main', 'fileRow', 'folderRow', 'levelUpRow', 'emptyFolder'], function () { + $('#main').append($(self.templateManager.get('main')).children()).trigger('pagecreate'); + self.addEvents(); + self.displayStorages(storages); + }); + }, + + /** + * Add UI events + */ + addEvents: function Ui_addEvents() { + var self = this; + // touch events for all nodes + $('ul#fileList') + .on('tap', 'li.levelUp', function () { + if (self.editMode === true) { + self.handleCancelEditAction(); + } + app.goLevelUp(); + }) + .on('tap', 'li.node', function (e) { + self.handleNodeClick($(this), true); + }) + .on('change', 'input[type=checkbox]', function (e) { + self.handleNodeClick($(this).closest('li.node'), false); + }) + .on('touchstart', 'li', function (event) { + $(this).addClass(self.CSS_GRADIENT_CLASS); + }) + .on('touchend touchmove taphold', 'li', function (event) { + $(this).removeClass(self.CSS_GRADIENT_CLASS); + }); + + $('.selectAll input').on('change', this.handleSelectAllChange.bind(this)); + + // navbar + $('#navbar').on('tap', 'span', function () { + var uri = $(this).attr('uri'); + if (uri === 'home') { + app.displayStorages(); + } else { + app.displayFolder(uri); + } + }); + + // level up + $('#levelUpBtn').on('tap', function () { + if (self.editMode === true) { + self.handleCancelEditAction(); + } + app.goLevelUp(); + }); + + $('#homeBtn').on('tap', app.displayStorages.bind(app)); + + // edit action + $('#editActionBtn').on('tap', this.handleEditAction.bind(this)); + + // delete action + $('#deleteActionBtn').on('tap', this.handleDeleteAction.bind(this)); + + // cancel edit + $('#cancelActionBtn').on('tap', this.handleCancelEditAction.bind(this)); + + // copy action + $('#copyActionBtn').on('tap', this.handleCopyAction.bind(this)); + + // move action + $('#moveActionBtn').on('tap', this.handleMoveAction.bind(this)); + + // paste action + $('a#pasteActionBtn').on('tap', app.pasteClipboard.bind(app)); + + // exit + $('.ui-myExit').on('tap', app.exit); + + // add folder popup actions + $('#addFolderPopup').on("popupafterclose", function () { + // clear input value + $('#newFolderName').val('New folder'); + }); + + $('#newFolderName').on('tap', function () { + if ($(this).attr('value') === 'New folder') { + $(this).attr('value', ''); + } + }); + + $('#cancelNewFolder').on('tap', function () { + $('#addFolderPopup').popup('close'); + $('#morePopup').popupwindow('close'); + }); + + $('#saveNewFolder').on('tap taphold', function () { + var folderName = $('#newFolderName').val().trim(); + $('#addFolderPopup').popup('close'); + $('#morePopup').popupwindow('close'); + + if (folderName === '') { + alert("Empty folder name"); + } else if (folderName.match(/[\*\.\/\\\?\"\'\:<>|]/)) { + alert("The following special characters are not allowed: *./\\?:<>|'\""); + } else { + app.createDir(folderName); + } + $(this).parent().removeClass('ui-btn-down-s'); + }); + + $('#newFolderActionBtn, #pasteActionBtn').on('tap', function (e) { + setTimeout(function () { + $('#morePopup').popupwindow('close'); + }, 700); + }); + + /* workaround for UIFW & webkit scroll*/ + $('.ui-page').css('min-height', 0); + }, + + /** + * Handler for node click + * @param {File} node + * @param {boolean} toggleCheckbox + */ + handleNodeClick: function Ui_handleNodeClick(node, toggleCheckbox) { + if (this.editMode === true) { + //if edit mode is on toggle checkbox state + if (toggleCheckbox === true) { + this.toggleCheckBoxState(node); // select the checkbox + } + + this.refreshSelectAllStatus(); + + if ($('ul#fileList input:checkbox:checked').length > 0) { + this.enableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } else { + this.disableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } + } else if (node.hasClass('folder')) { + // otherwise display folder + app.displayFolder(node.attr('uri')); + } else { + // file + app.openFile(node.attr('uri'), node.attr('fullUri')); + } + }, + + /** + * Handler for edit action + */ + handleEditAction: function Ui_handleEditAction() { + this.editMode = true; + $('.standardTabbar').hide(); + $('div.editTabbar').show(); + this.disableControlBarButtons($('div.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + this.showEditCheckBoxes(); + }, + + /** + * Handler for cancel edit action + */ + handleCancelEditAction: function Ui_handleCancelEditAction() { + this.editMode = false; + $('div.editTabbar').hide(); + $('.standardTabbar').show(); + this.hideEditCheckBoxes(); + }, + + /** + * Handler for delete action + */ + handleDeleteAction: function Ui_handleDeleteAction() { + var nodesToDelete = [], + $rowElement; + + $('ul#fileList input:checkbox:checked').each(function (index) { + $rowElement = $(this).closest('li'); + nodesToDelete.push({ + id: $rowElement.attr('id'), + uri: $rowElement.attr('uri'), + name: $rowElement.attr('label'), + folder: $rowElement.hasClass('folder') + }); + }); + + if (nodesToDelete.length > 0 && confirm('Selected nodes will be deleted. Are you sure?')) { + app.deleteNodes(nodesToDelete); + this.scrollContentTo(0); + } + }, + + /** + * Handler for copy action + */ + handleCopyAction: function Ui_handleCopyAction() { + var paths = []; + if (this.editMode === true) { + $('ul#fileList input:checkbox:checked').each(function (index) { + paths.push($(this).closest('li').attr('uri')); + }); + app.saveToClipboard(paths, app.clipboard.COPY_MODE_ID); + } + }, + + /** + * Handler for move action + */ + handleMoveAction: function Ui_handleMoveAction() { + var paths = []; + if (this.editMode === true) { + $('ul#fileList input:checkbox:checked').each(function (index) { + paths.push($(this).closest('li').attr('uri')); + }); + app.saveToClipboard(paths, app.clipboard.MOVE_MODE_ID); + } + }, + + /** + * Handler for paste action + */ + handlePasteAction: function Ui_handlePasteAction() { + }, + + /** + * Scrolls content to the specified position + */ + scrollContentTo: function scrollContentTo(value) { + $('#main [data-role="content"]').scrollview('scrollTo', 0, value); + }, + + /** + * @param {FileSystemStorage[]} nodes Storage elements + */ + displayStorages: function Ui_displayStorages(nodes) { + var len = nodes.length, + listElements = [], + nodeName, + listTemplate = '', + i; + + this.updateNavbar(''); + + for (i = 0; i < len; i = i + 1) { + nodeName = nodes[i].label.trim(); + if (nodeName !== '' && (nodes[i].type === 0 || nodes[i].type === 'INTERNAL') && nodeName.indexOf('wgt-') === -1) { + listElements.push(this.templateManager.get('folderRow', { + id: i, + name: nodeName, + uri: nodeName, + fullUri: nodeName + })); + } + } + + $('#levelUpBtn').addClass('vhidden'); + $('#homeBtn').addClass('vhidden'); + + $('#editActionBtn').addClass('vhidden'); + $('#moreActionBtn').addClass('vhidden'); + $('h1#mainTitle').html('Media'); + + // update file list + $('#fileList').empty(); + listTemplate = listElements.join(''); + $(listTemplate).appendTo('#fileList'); + // reset scrollview position + //$('#main .ui-scrollview-view').css('-webkit-transform', 'none'); + $('#fileList') + .trigger("refresh"); + + this.resetDefaultCheckBoxLabelEvents(); + this.hideSelectAllArea(); + this.handleCancelEditAction(); + }, + + /** + * renders node list for folder + * @param {string} folderName + * @param {File[]} nodes + */ + displayFolder: function Ui_displayFolder(folderName, nodes) { + var len = nodes.length, + listElements = [this.templateManager.get('levelUpRow')], + nodeName, + i; + + // update title + this.updateTitle(folderName); + // update navbar + this.updateNavbar(folderName); + + // render nodes + for (i = 0; i < len; i = i + 1) { + nodeName = nodes[i].name.trim(); + if (nodeName !== '') { + if (nodes[i].isDirectory) { + // folder + listElements.push(this.templateManager.get('folderRow', { + id: i, + name: nodeName, + uri: nodes[i].fullPath, + fullUri: nodes[i].toURI() + })); + } else { + // file + listElements.push(this.templateManager.get('fileRow', { + id: i, + name: nodeName, + uri: nodes[i].fullPath, + fullUri: nodes[i].toURI(), + thumbnailURI: this.helpers.getThumbnailURI(nodeName, nodes[i]) + })); + } + } + } + + if (listElements.length === 1) { + // set content for empty folder + listElements.push(this.templateManager.get('emptyFolder')); + } + + $('#levelUpBtn').removeClass('vhidden'); + $('#homeBtn').removeClass('vhidden'); + $('#editActionBtn').removeClass('vhidden'); + $('#moreActionBtn').removeClass('vhidden'); + + // update file list + $('#fileList').html(listElements.join('')) + .trigger('refresh') + .trigger('create'); + + if (this.editMode === true) { + $('.selectAll').show(); + $('ul#fileList > li').css('paddingLeft', '2rem'); + $('.my-ui-checkbox').removeClass('hidden'); + } else { + $('.selectAll').hide(); + $('ul#fileList > li').css('paddingLeft', '0'); + $('.my-ui-checkbox').addClass('hidden'); + } + }, + + /** + * Toggle a checkbox associated with a given list element + * @param {jQuery} listElement + */ + toggleCheckBoxState: function Ui_toggleCheckBoxState(listElement) { + + var checkboxInput = null; + + checkboxInput = listElement.find('form > div.ui-checkbox input'); + checkboxInput + .attr('checked', !checkboxInput.attr('checked')) + .data('checkboxradio').refresh(); + }, + + /** + * Shows item checkboxes and topbar with select all option + */ + showEditCheckBoxes: function Ui_showEditCheckBoxes() { + var self = this; + + this.showSelectAllArea(); + + $('ul#fileList > li').animate({paddingLeft: '2rem'}, 500, 'swing', function () { + self.editMode = true; + $('.my-ui-checkbox').removeClass('hidden'); + }); + }, + + /** + * Hides item checkboxes and topbar with select all option + * All checkboxes are auto uncheked + */ + hideEditCheckBoxes: function Ui_hideEditCheckBoxes() { + var self = this; + + this.hideSelectAllArea(); // hide select all option topbar + + $('ul#fileList > li').animate({paddingLeft: '0'}, 200, 'swing', function () { + $('.my-ui-checkbox').addClass('hidden'); + $.mobile.activePage.page('refresh'); + }); + + // uncheck all checkboxes + $('ul#fileList input[type=checkbox]').each(function (index) { + var checkboxradio = $(this).data('checkboxradio'); + + $(this).attr('checked', false); + + if (checkboxradio) { + checkboxradio.refresh(); + } + }); + + //uncheck select all input + $('.ui-header .selectAll .ui-checkbox input') + .attr('checked', false) + .data('checkboxradio') + .refresh(); + }, + + /** + * Save current header and content height + */ + saveHeights: function Ui_saveHeights() { + this.currentHeaderHeight = $('#main div[data-role="header"]').height(); + this.currentScrollPosition = $('#main div[data-role="content"]').scrollview('getScrollPosition').y; + }, + + /** + * Changes content scroll position after showing/hiding selectAllArea + */ + changeContentScrollPosition: function Ui_changeContentScrollPosition() { + var diff; + if (this.currentScrollPosition !== 0) { + diff = $('#main div[data-role="header"]').height() - this.currentHeaderHeight; + $('#main div[data-role="content"]').scrollview('scrollTo', 0, -(this.currentScrollPosition + diff)); + } + }, + + /** + * Shows topbar with select all option + */ + showSelectAllArea: function Ui_showSelectAllArea() { + this.saveHeights(); + $('.selectAll').show(); + $.mobile.activePage.page('refresh'); + this.changeContentScrollPosition(); + }, + + /** + * Hides topbar with select all option + */ + hideSelectAllArea: function Ui_hideSelectAllArea() { + this.saveHeights(); + $('.selectAll').hide(); + $.mobile.activePage.page('refresh'); + this.changeContentScrollPosition(); + }, + + /** + * Enable specified options for tabbar + * @param {object} tabbar + * @param {array} enableOptions options to enable + */ + enableControlBarButtons: function Ui_enableControlBarButtons(tabbar, enableOptions) { + var i = 0, + len = enableOptions.length; + + for (i = 0; i < len; i += 1) { + tabbar.tabbar('enable', enableOptions[i]); + } + }, + + /** + * Disable specified options for tabbar + * @param {object} tabbar controlbar + * @param {array} disableOptions options to enable + */ + disableControlBarButtons: function Ui_disableControlBarButtons(tabbar, disableOptions) { + var i = 0, + len = disableOptions.length; + + for (i = 0; i < len; i += 1) { + tabbar.tabbar('disable', disableOptions[i]); + } + }, + + /** + * @param {string} path + */ + updateTitle: function Ui_updateTitle(path) { + var regexp = new RegExp('([^\/])+$', 'g'), + match = path.match(regexp), + lastDir = match[0] || '(dir)'; + $('h1#mainTitle').html(lastDir); + }, + + /** + * @param {string} path + */ + updateNavbar: function Ui_updateNavbar(path) { + var html = ['Media'], + splitted, + len, + i; + + if (typeof path === 'string' && path !== '') { + splitted = path.split('/'); + len = splitted.length; + + for (i = 0; i < len; i = i + 1) { + html.push('' + splitted[i] + ''); + } + } + $('#navbar').html(html.join(' > ')); + }, + + handleSelectAllChange: function Ui_handleSelectAllChange() { + var $selectAllInput = $('.ui-header .selectAll .ui-checkbox input'); + $selectAllInput.data('checkboxradio').refresh(); + + if ($selectAllInput.is(':checked')) { + // check all checkboxes + $('ul#fileList input[type=checkbox]').each(function (index) { + $(this).attr('checked', true); + $(this).data('checkboxradio').refresh(); + }); + + this.enableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } else { + $('ul#fileList input[type=checkbox]').each(function (index) { + $(this).attr('checked', false); + $(this).data('checkboxradio').refresh(); + }); + + this.disableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } + }, + + /** + * + */ + refreshSelectAllStatus: function Ui_refreshSelectAllStatus() { + var $selectAllInput = $('.ui-header .selectAll .ui-checkbox input'); + // update status of select all checkbox + if ($('ul#fileList input:checkbox:not(:checked)').length === 0) { + // all nodes checked + $selectAllInput.attr('checked', true).data('checkboxradio').refresh(); + } else { + // some node is not checked + $selectAllInput.attr('checked', false).data('checkboxradio').refresh(); + } + }, + + /** + * Unbinds default events for checkbox labels + */ + resetDefaultCheckBoxLabelEvents: function Ui_resetDefaultCheckBoxLabelEvents() { + $('div.ui-checkbox > label') + .unbind('vmousedown') + .unbind('vmouseup') + .unbind('vmouseover') + .unbind('vclick'); + }, + + /** + * Remove html node element from list + * @param {string} nodeId node id + */ + removeNodeFromList: function Ui_removeNodeFromList(nodeId) { + $('ul#fileList > li#' + nodeId).remove(); + + // hide select All checkbox if removed all elements; + if ($('ul#fileList > li.node').length === 0) { + this.hideSelectAllArea(); + } + }, + + /** + * Enable/Disable + */ + refreshPasteActionBtn: function Ui_refreshPasteActionBtn(clipboardEmpty) { + if (clipboardEmpty === true) { + $('#pasteActionBtnRow').addClass('hidden'); + } else { + $('#pasteActionBtnRow').removeClass('hidden'); + } + } + }; +}()); diff --git a/js/app.ui.templateManager.js b/js/app.ui.templateManager.js new file mode 100644 index 0000000..8d3de5d --- /dev/null +++ b/js/app.ui.templateManager.js @@ -0,0 +1,108 @@ +/*jslint devel: true*/ +/*global $, app */ +/** + * @class TemplateManager + */ +function TemplateManager() { + 'use strict'; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + TemplateManager.prototype = { + + /** + * Template cache + */ + cache: {}, + + /** + * UI module initialisation + */ + init: function init() { + + }, + + /** + * Returns template html (from cache) + */ + get: function TemplateManager_get(tplName, tplParams) { + + if (this.cache[tplName] !== undefined) { + return this.getCompleted(this.cache[tplName], tplParams); + } + return ''; + }, + + /** + * Load templates to cache + */ + loadToCache: function TemplateManager_loadToCache(tplNames, onSuccess) { + var self = this, + cachedTemplates = 0, + tplName, + tplPath; + + if ($.isArray(tplNames)) { + + // for each template + $.each(tplNames, function (index, fileName) { + + // cache template html + if (self.cache[fileName] === undefined) { + tplName = [fileName, app.config.get('templateExtension')].join(''); + tplPath = [app.config.get('templateDir'), tplName].join('/'); + + $.ajax({ + url: tplPath, + cache: true, + dataType: 'html', + async: true, + success: function (data) { + // increase counter + cachedTemplates += 1; + + // save to cache + self.cache[fileName] = data; + + // if all templates are cached launch callback + if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { + onSuccess(); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + alert(errorThrown); + } + }); + } else { + // template is already cached + cachedTemplates += 1; + // if all templates are cached launch callback + if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { + onSuccess(); + } + } + }); + + } + }, + + /** + * Returns template completed by specified params + */ + getCompleted: function TemplateManager_getCompleted(tplHtml, tplParams) { + var tplParam, replaceRegExp; + + for (tplParam in tplParams) { + if (tplParams.hasOwnProperty(tplParam)) { + replaceRegExp = new RegExp(['%', tplParam, '%'].join(''), 'g'); + tplHtml = tplHtml.replace(replaceRegExp, tplParams[tplParam]); + } + } + + return tplHtml; + } + }; + +}()); \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..45d59e6 --- /dev/null +++ b/js/main.js @@ -0,0 +1,78 @@ +/* + * Copyright 2012 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*jslint devel: true*/ +/*global $, tizen, App */ + +/** + * This file acts as a loader for the application and its dependencies + * + * First, the 'app.js' script is loaded . + * Then, scripts defined in 'app.requires' are loaded. + * Finally, the app is initialised - the app is instantiated ('app = new App()') + * and 'app.init()' is called. + */ + + +var app = null; + +(function () { // strict mode wrapper + 'use strict'; + + ({ + /** + * Loader init - load the App constructor + */ + init: function init() { + var self = this; + $.getScript('js/app.js') + .done(function () { + // once the app is loaded, create the app object + // and load the libraries + app = new App(); + self.loadLibs(); + }) + .fail(this.onGetScriptError); + }, + + /** + * Load dependencies + */ + loadLibs: function loadLibs() { + var loadedLibs = 0; + if ($.isArray(app.requires)) { + $.each(app.requires, function (index, filename) { + $.getScript(filename) + .done(function () { + loadedLibs += 1; + if (loadedLibs >= app.requires.length) { + // All dependencies are loaded - initialise the app + app.init(); + } + }) + .fail(this.onGetScriptError); + }); + } + }, + + /** + * Handle ajax errors + */ + onGetScriptError: function onGetScriptError(e, jqxhr, setting, exception) { + alert('An error occurred: ' + e.message); + } + }).init(); // run the loader +}()); \ No newline at end of file diff --git a/templates/emptyFolder.tpl b/templates/emptyFolder.tpl new file mode 100644 index 0000000..da09356 --- /dev/null +++ b/templates/emptyFolder.tpl @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/templates/fileRow.tpl b/templates/fileRow.tpl new file mode 100644 index 0000000..47e30f2 --- /dev/null +++ b/templates/fileRow.tpl @@ -0,0 +1,5 @@ +
  • + + + %name% +
  • \ No newline at end of file diff --git a/templates/folderRow.tpl b/templates/folderRow.tpl new file mode 100644 index 0000000..c4e91e3 --- /dev/null +++ b/templates/folderRow.tpl @@ -0,0 +1,6 @@ +
  • + + + %name% + +
  • \ No newline at end of file diff --git a/templates/levelUpRow.tpl b/templates/levelUpRow.tpl new file mode 100644 index 0000000..ddbc7cf --- /dev/null +++ b/templates/levelUpRow.tpl @@ -0,0 +1,3 @@ +
  • + .. +
  • \ No newline at end of file diff --git a/templates/main.tpl b/templates/main.tpl new file mode 100644 index 0000000..68dde67 --- /dev/null +++ b/templates/main.tpl @@ -0,0 +1,64 @@ +
    +
    +

    + Home + Up + + +
    + +
    +
      +
      + +
      +
      +
      + +
      + +
      + + + + + + + +
      + New folder +
      + Paste to folder +
      +
      +
      +

      Add new folder

      +

      + +

      +

      + + +

      +
      +
      +
      +
      \ No newline at end of file