From b89a7ee8b956c96a1dcee995ea840feddc5d4b27 Mon Sep 17 00:00:00 2001 From: Pierre Frisch Date: Thu, 22 Dec 2011 07:25:50 -0800 Subject: First commit of Ninja to ninja-internal Signed-off-by: Valerio Virgillito --- .../ui/FilePicker/file-picker-controller.js | 497 ++++++++++ js/components/ui/FilePicker/file-picker-model.js | 122 +++ .../pickerNavigator.reel/pickerNavigator.css | 201 ++++ .../pickerNavigator.reel/pickerNavigator.html | 81 ++ .../pickerNavigator.reel/pickerNavigator.js | 1019 ++++++++++++++++++++ js/components/ui/color-chip.reel/color-chip.css | 31 + js/components/ui/color-chip.reel/color-chip.html | 36 + js/components/ui/color-chip.reel/color-chip.js | 41 + js/components/ui/file-input.reel/file-input.css | 34 + js/components/ui/file-input.reel/file-input.html | 31 + js/components/ui/file-input.reel/file-input.js | 65 ++ .../ui/icon-list-basic/icon.reel/icon.css | 42 + .../ui/icon-list-basic/icon.reel/icon.html | 31 + js/components/ui/icon-list-basic/icon.reel/icon.js | 192 ++++ .../icon-list-basic/iconsList.reel/iconsList.css | 9 + .../icon-list-basic/iconsList.reel/iconsList.html | 62 ++ .../ui/icon-list-basic/iconsList.reel/iconsList.js | 42 + js/components/ui/input-group.reel/input-group.css | 33 + js/components/ui/input-group.reel/input-group.html | 78 ++ js/components/ui/input-group.reel/input-group.js | 85 ++ .../ui/modalDialog/modal-dialog-manager.js | 123 +++ js/components/ui/modalDialog/modalDialogHeader.js | 82 ++ .../modalDialogHeader.reel/modalDialogHeader.css | 30 + .../modalDialogHeader.reel/modalDialogHeader.html | 33 + .../ui/property-control.reel/property-control.css | 27 + .../ui/property-control.reel/property-control.html | 40 + .../ui/property-control.reel/property-control.js | 238 +++++ .../ui/radio-button.reel/radio-button.html | 32 + js/components/ui/radio-button.reel/radio-button.js | 44 + .../ui/row-renderer.reel/row-renderer.html | 37 + js/components/ui/row-renderer.reel/row-renderer.js | 18 + js/components/ui/tree-basic/tree.reel/tree.css | 9 + js/components/ui/tree-basic/tree.reel/tree.html | 67 ++ js/components/ui/tree-basic/tree.reel/tree.js | 43 + .../ui/tree-basic/treeItem.reel/treeItem.css | 53 + .../ui/tree-basic/treeItem.reel/treeItem.html | 35 + .../ui/tree-basic/treeItem.reel/treeItem.js | 269 ++++++ 37 files changed, 3912 insertions(+) create mode 100644 js/components/ui/FilePicker/file-picker-controller.js create mode 100644 js/components/ui/FilePicker/file-picker-model.js create mode 100644 js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.css create mode 100644 js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.html create mode 100644 js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.js create mode 100644 js/components/ui/color-chip.reel/color-chip.css create mode 100644 js/components/ui/color-chip.reel/color-chip.html create mode 100644 js/components/ui/color-chip.reel/color-chip.js create mode 100644 js/components/ui/file-input.reel/file-input.css create mode 100644 js/components/ui/file-input.reel/file-input.html create mode 100644 js/components/ui/file-input.reel/file-input.js create mode 100644 js/components/ui/icon-list-basic/icon.reel/icon.css create mode 100644 js/components/ui/icon-list-basic/icon.reel/icon.html create mode 100644 js/components/ui/icon-list-basic/icon.reel/icon.js create mode 100644 js/components/ui/icon-list-basic/iconsList.reel/iconsList.css create mode 100644 js/components/ui/icon-list-basic/iconsList.reel/iconsList.html create mode 100644 js/components/ui/icon-list-basic/iconsList.reel/iconsList.js create mode 100644 js/components/ui/input-group.reel/input-group.css create mode 100644 js/components/ui/input-group.reel/input-group.html create mode 100644 js/components/ui/input-group.reel/input-group.js create mode 100644 js/components/ui/modalDialog/modal-dialog-manager.js create mode 100644 js/components/ui/modalDialog/modalDialogHeader.js create mode 100644 js/components/ui/modalDialog/modalDialogHeader.reel/modalDialogHeader.css create mode 100644 js/components/ui/modalDialog/modalDialogHeader.reel/modalDialogHeader.html create mode 100644 js/components/ui/property-control.reel/property-control.css create mode 100644 js/components/ui/property-control.reel/property-control.html create mode 100644 js/components/ui/property-control.reel/property-control.js create mode 100644 js/components/ui/radio-button.reel/radio-button.html create mode 100644 js/components/ui/radio-button.reel/radio-button.js create mode 100644 js/components/ui/row-renderer.reel/row-renderer.html create mode 100644 js/components/ui/row-renderer.reel/row-renderer.js create mode 100644 js/components/ui/tree-basic/tree.reel/tree.css create mode 100644 js/components/ui/tree-basic/tree.reel/tree.html create mode 100644 js/components/ui/tree-basic/tree.reel/tree.js create mode 100644 js/components/ui/tree-basic/treeItem.reel/treeItem.css create mode 100644 js/components/ui/tree-basic/treeItem.reel/treeItem.html create mode 100644 js/components/ui/tree-basic/treeItem.reel/treeItem.js (limited to 'js/components/ui') diff --git a/js/components/ui/FilePicker/file-picker-controller.js b/js/components/ui/FilePicker/file-picker-controller.js new file mode 100644 index 00000000..526578d1 --- /dev/null +++ b/js/components/ui/FilePicker/file-picker-controller.js @@ -0,0 +1,497 @@ +/* +This file contains proprietary software owned by Motorola Mobility, Inc.
+No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ + + +var Montage = require("montage/core/core").Montage, + pickerNavigatorReel = require("js/components/ui/FilePicker/pickerNavigator.reel").PickerNavigator, + filePickerModelModule = require("js/components/ui/FilePicker/file-picker-model"), + fileSystem = require("js/io/system/filesystem").FileSystem, + Popup = require("montage/ui/popup/popup.reel").Popup; + +//singleton with functions to create a new file picker instance and utilities to format or filter the model data +var FilePickerController = exports.FilePickerController = Montage.create(require("montage/ui/component").Component, { + /** + * Register a listener for file open event + */ + deserializedFromTemplate:{ + writable:false, + enumerable:true, + value:function(){ + var that = this; + this.eventManager.addEventListener("executeFileOpen", function(evt){ + + var callback, pickerMode, currentFilter, allFileFilters,inFileMode, allowNewFileCreation, allowMultipleSelections; + + if(!!evt.callback){ + callback = evt.callback; + } + if(!!evt.pickerMode){ + pickerMode = evt.pickerMode; + } + if(!!evt.currentFilter){ + currentFilter = evt.currentFilter; + } + if(!!evt.inFileMode){ + inFileMode = evt.inFileMode; + } + if(!!evt.allFileFilters){ + allFileFilters = evt.allFileFilters; + } + if(!!evt.allowNewFileCreation){ + allowNewFileCreation = evt.allowNewFileCreation; + } + if(!!evt.allowMultipleSelections){ + allowMultipleSelections = evt.allowMultipleSelections; + } + + that.showFilePicker(callback, pickerMode, currentFilter, allFileFilters,inFileMode, allowNewFileCreation, allowMultipleSelections); + + }, false); + } + }, + + /** + * this will be stored in the local storage and in the cloud may be, for the cloud one. + */ + _lastOpenedFolderURI:{ + writable:true, + enumerable:true, + value:{ + lastFolderUri_local:null, + lastFolderUri_cloud:null + } + }, + + /** + * this will be stored in the local storage and in the cloud may be, for the cloud one. + */ + _lastSavedFolderURI:{ + writable:true, + enumerable:true, + value:{ + lastSavedFolderUri_local:null, + lastSavedFolderUri_cloud:null + } + }, + + /** + *this function is used to create an instance of a file picker + * + * parameters: + * callback: the call back function which will be used to send the selected URIs back + * pickerMode: ["read", "write"] : specifies if the file picker is opened to read a file/folder or to save a file + * currentFilter: if a current filter needs to be applied [ex: .psd] + * allFileFilters: list of filters that user can use to filter the view + * inFileMode: true => allow file selection , false => allow directory selection + * allowNewFileCreation: flag to specify whether or not it should return URI(s) to item(s) that do not exist. i.e. a user can type a filename to a new file that doesn't yet exist in the file system. + * allowMultipleSelections: allowMultipleSelections + *rootDirectories: invoker of this function can mention a subset of the allowed root directories to show in the file picker + * + * return: none + */ + + showFilePicker:{ + writable:false, + enumerable:true, + value:function(callback, pickerMode, currentFilter, allFileFilters,inFileMode, allowNewFileCreation, allowMultipleSelections){ + + var aModel = filePickerModelModule.FilePickerModel.create(); + + var topLevelDirectories = null; + var driveData = fileSystem.shellApiHandler.getDirectoryContents({uri:"", recursive:false, returnType:"all"}); + if(driveData.success){ + topLevelDirectories = (JSON.parse(driveData.content)).children; + }else{ + var errorCause = ""; + if(driveData.status === null){ + errorCause = "Service Unavailable" + }else{ + errorCause = driveData.status; + } + aModel.fatalError = " ** Unable to get files [Error: "+ errorCause +"]"; + } + + aModel.currentFilter = currentFilter; + aModel.inFileMode = inFileMode; + aModel.topLevelDirectories = topLevelDirectories; + + if(!!topLevelDirectories && !!topLevelDirectories[0]){ + aModel.currentRoot = topLevelDirectories[0].uri; + } + + //populate the last opened folder first, if none then populate default root + var sessionStorage = window.sessionStorage; + var storedUri = null; + + if(pickerMode === "write"){ + storedUri = sessionStorage.getItem("lastSavedFolderURI"); + }else{ + storedUri = sessionStorage.getItem("lastOpenedFolderURI"); + } + + if(!!storedUri){ + aModel.currentRoot = unescape(storedUri); + } + + aModel.fileFilters = allFileFilters; + aModel.callback = callback; + aModel.pickerMode = pickerMode; + + //dummy data - TODO:remove after testing + //aModel.currentFilter = "*.html, *.png"; + //aModel.currentFilter = "*.jpg"; + aModel.currentFilter = "*.*"; + aModel.inFileMode = true; + aModel.fileFilters = [".html, .htm", ".jpg, .jpeg, .png, .gif", ".js, .json", ".css", ".txt, .rtf", ".doc, .docx", ".pdf", ".avi, .mov, .mpeg, .ogg, .webm", "*.*"]; + //-end - dummy data + + //logic: get file content data onDemand from the REST api for the default or last opened root. Cache the data in page [in local cache ? dirty fs? ]. Filter on client side to reduce network calls. + this.openFilePickerAsModal(callback, aModel); + + + //to open this on another modal dialog, make it a popup instead above the modal dialog container layer + + } + }, + + openFilePickerAsModal:{ + writable:false, + enumerable:true, + value:function(callback, aModel){ + //render modal dialog + var pickerNavContent = document.createElement("div"); + pickerNavContent.id = "filePicker"; + + pickerNavContent.style.color = "#fff"; + + //hack (elements needs to be on DOM to be drawn) + document.getElementById('modalContainer').appendChild(pickerNavContent); + + var pickerNavChoices = Montage.create(pickerNavigatorReel); + var initUri = aModel.currentRoot; + pickerNavChoices.mainContentData = this.prepareContentList(initUri, aModel); + pickerNavChoices.pickerModel = aModel; + pickerNavChoices.element = pickerNavContent; + + //hack - remove after rendering and add in modal dialog + document.getElementById('modalContainer').removeChild(pickerNavContent); + + var popup = Popup.create(); + popup.content = pickerNavChoices; + popup.modal = true; + popup.show(); + + pickerNavChoices.popup = popup;//handle to be used for hiding the popup + } + }, + openFilePickerAsPopup:{ + writable:false, + enumerable:true, + value:function(){} + }, + + expandDirectory:{ + writable:false, + enumerable:true, + value: function(root, currentFilter, inFileMode){ + //populate children in dom + } + }, + + refreshDirectoryCache:{ + writable:false, + enumerable:true, + value:function(directoryUri){ + if(directoryContentCache[directoryUri] !== null){ + directoryContentCache[directoryUri] = null; //invalidate the cached content + //fetch fresh content + } + } + }, + + /** + * queries the cache to build contents array. If not found queries the file system + * + * parameters: + * folderUri + * aModel: model instance per picker instance, containing + */ + + prepareContentList:{ + writable: false, + enumerable:true, + value:function(folderUri, aModel, fromCache, checkStaleness){ + var contentList = [], + childrenArray = []; + + var folderContent = null; + // query filesystem and populate cache + if(((typeof fromCache !== "undefined") && (fromCache === false)) + || !this._directoryContentCache[folderUri] + || !this._directoryContentCache[folderUri].children){ + //get data using IO api + try{ + var iodata = fileSystem.shellApiHandler.getDirectoryContents({uri:folderUri, recursive:false, returnType:"all"}); + //console.log("IO:getDirectoryContents:Response:\n"+"uri="+folderUri+"\n status="+iodata.status+"\n content= "+iodata.content); + if(iodata.success && (iodata.status === 200) && (iodata.content !== null)){ + folderContent = JSON.parse(iodata.content); + } + }catch(e){ + console.error("Error to IO uri: "+folderUri+"\n"+e.message); + } + + if(!!folderContent){ + //contentList = folderContent.children;//need to apply filters and mode + this.cacheContentForRandomAccess(folderUri, folderContent); + } + } + //now from cache - apply filters and mode + if((!!this._directoryContentCache[folderUri]) + && (this._directoryContentCache[folderUri].type === "directory") + && (typeof this._directoryContentCache[folderUri].children !== "undefined") + && (this._directoryContentCache[folderUri].children !== null)){ + + //console.log("$$$ this._directoryContentCache"); + //console.log(this._directoryContentCache); + + //check for directory staleness.... if stale query filesystem + if((typeof checkStaleness === "undefined") || (checkStaleness === true)){ + this.checkIfStale(folderUri); + } + + childrenArray = this._directoryContentCache[folderUri].children; + + //prepare content array for folder uri + childrenArray.forEach(function(item){ + if(this._directoryContentCache[item]){ + //apply mode and filtering here + if(aModel.inFileMode){// if in file selection mode, do filtering + if((this._directoryContentCache[item].type === "directory") || !aModel.currentFilter){//no filetering + contentList.push(this._directoryContentCache[item]); + } + else if(aModel.currentFilter){ + if(this.applyFilter(this._directoryContentCache[item].name, aModel.currentFilter)){ + contentList.push(this._directoryContentCache[item]); + } + } + }else{// if in folder selection mode + if(this._directoryContentCache[item].type === "directory"){ + contentList.push(this._directoryContentCache[item]); + } + } + } + }, this); + } + else if((typeof this._directoryContentCache[folderUri] !== 'undefined') && (this._directoryContentCache[folderUri].type === "file")){//if the uri is for a file + + //check for directory staleness.... if stale query filesystem + if((typeof checkStaleness === "undefined") || (checkStaleness === true)){ + this.checkIfStale(folderUri); + } + + contentList.push(this._directoryContentCache[folderUri]); + } + //end - from cache + + return contentList; + } + }, + + /** + * populates/updates cache for a uri + */ + cacheContentForRandomAccess:{ + writable:false, + enumerable:true, + value: function(directoryUri, directoryContents){ + + var that = this; + //assumption: directoryContents will have only its direct files and subfolders + //uri is the unique identifier + + + //check if the directoryUri exists in cache + //if not add uri content object, prepare children's uri array,then add/update objects for children + if(!this._directoryContentCache[directoryUri]){//uri not in cache... so add it + //add uri content object + this._directoryContentCache[directoryUri] = {"type":directoryContents.type,"name":directoryContents.name,"uri":directoryUri}; + if(!!directoryContents.size){ + this._directoryContentCache[directoryUri].size = directoryContents.size; + } + if(!!directoryContents.creationDate){ + this._directoryContentCache[directoryUri].creationDate = directoryContents.creationDate; + } + if(!!directoryContents.modifiedDate){ + this._directoryContentCache[directoryUri].modifiedDate = directoryContents.modifiedDate; + } + + //store the current queried time for refreshing cache logic + this._directoryContentCache[directoryUri].queriedTimeStamp = (new Date()).getTime(); + + if(!!directoryContents.children && directoryContents.children.length > 0){ + this._directoryContentCache[directoryUri].children = []; + + //add the uri to this._directoryContentCache[directoryUri].children, and add the child's description objects + directoryContents.children.forEach(function(obj){ + //add uri to parent's children list + that._directoryContentCache[directoryUri].children.push(obj.uri); + //add the child object + that._directoryContentCache[obj.uri] = obj; + + //store the current queried time for refreshing cache logic + that._directoryContentCache[obj.uri].queriedTimeStamp = (new Date()).getTime(); + + } ,this); + } + }else{//uri in cache... so update it AND its children + this._directoryContentCache[directoryUri].type = directoryContents.type; + this._directoryContentCache[directoryUri].name = directoryContents.name; + if(!!directoryContents.size){ + this._directoryContentCache[directoryUri].size = directoryContents.size; + } + if(!!directoryContents.creationDate){ + this._directoryContentCache[directoryUri].creationDate = directoryContents.creationDate; + } + if(!!directoryContents.modifiedDate){ + this._directoryContentCache[directoryUri].modifiedDate = directoryContents.modifiedDate; + } + + //store the current queried time for refreshing cache logic + this._directoryContentCache[directoryUri].queriedTimeStamp = (new Date()).getTime(); + + if(!!directoryContents.children && directoryContents.children.length > 0){ + + // logic to clear off objects from cache if they no longer exist in the filesystem + //better logic - use isUpdatedFlag for a folder .. then compare modified date + + //hack for now - clear up the old children and add new ones + var tempArr = this._directoryContentCache[directoryUri].children; + if( !!tempArr && Array.isArray(tempArr) && tempArr.length>0){ + tempArr.forEach(function(uriString){ + if(!!that._directoryContentCache[uriString]){ + delete that._directoryContentCache[uriString]; + } + }); + } + + this._directoryContentCache[directoryUri].children = []; + + //add the uri to this._directoryContentCache[directoryUri].children, and add the child's description objects + directoryContents.children.forEach(function(obj){ + //add uri to parent's children list + that._directoryContentCache[directoryUri].children.push(obj.uri); + //add the child object + that._directoryContentCache[obj.uri] = obj; + //store the current queried time for refreshing cache logic + that._directoryContentCache[obj.uri].queriedTimeStamp = (new Date()).getTime(); + } ,this); + }else{ + this._directoryContentCache[directoryUri].children = []; + } + } + + //console.log("$$$ "+directoryUri+" modifiedDate = "+this._directoryContentCache[directoryUri].modifiedDate); + //console.log("$$$ "+directoryUri+" queriedTimeStamp = "+this._directoryContentCache[directoryUri].queriedTimeStamp); + } + }, + + applyFilter:{ + writable: false, + enumerable:true, + value:function(fileName , filters){ + + if(filters.indexOf("*.*") !== -1){return true;} + + //console.log(""+fileName); + var filtersArr = filters.split(","); + var passed = false; + for(var i=0; i< filtersArr.length; i++){ + filtersArr[i] = filtersArr[i].trim(); + //console.log(filtersArr[i]); + var fileType = filtersArr[i].substring(filtersArr[i].indexOf(".") ); + //console.log(""+fileType); + + //ignore uppercase + fileName = fileName.toLowerCase(); + fileType = fileType.toLowerCase(); + + if(fileName.indexOf(fileType, fileName.length - fileType.length) !== -1){//ends with file type + passed = true; + break; + } + } + return passed; + } + }, + + /** + * Stale Time (ms) for each resource + * Logic: the last queried time for a resource is compared to stale time. If stale, then file system is queried + */ + cacheStaleTime:{ + writable: false, + enumerable: false, + value: 5000 + }, + + checkIfStale: { + writable: false, + enumerable: true, + value: function(folderUri){ + var wasStale = false; + var folderContent = null; + //check for directory staleness.... if stale query filesystem + if((new Date()).getTime() > (this._directoryContentCache[folderUri].queriedTimeStamp + this.cacheStaleTime)){ + try{ + var ifModifiedResponse = fileSystem.shellApiHandler.isDirectoryModified({uri:folderUri, recursive:false, returnType:"all"}, this._directoryContentCache[folderUri].queriedTimeStamp); + //console.log("ifModifiedResponse"); + //console.log(ifModifiedResponse); + }catch(e){ + console.error("Error to IO uri with isDirectoryModified: "+folderUri+"\n"+e.message); + } + if(ifModifiedResponse && ifModifiedResponse.status === 304){ + //do nothing since the uri has not changed + }else if(ifModifiedResponse && (ifModifiedResponse.status === 200)){ + wasStale = true; + //uri has changed. so update cache + try{ + var iodata = fileSystem.shellApiHandler.getDirectoryContents({uri:folderUri, recursive:false, returnType:"all"}); + //console.log("IO:getDirectoryContents:Response:\n"+"uri="+folderUri+"\n status="+iodata.status+"\n content= "+iodata.content); + if(iodata.success && (iodata.status === 200) && (iodata.content !== null)){ + folderContent = JSON.parse(iodata.content); + } + }catch(e){ + console.error("Error to IO uri: "+folderUri+"\n"+e.message); + } + + if(!!folderContent){ + this.cacheContentForRandomAccess(folderUri, folderContent); + } + } + } + + return wasStale; + } + }, + + /** + * This will store the directory content per session + * check session storage for this + */ + _directoryContentCache:{ + writable:true, + enumerable:false, + value:{} + }, + + clearCache:{ + writable:false, + enumerable: true, + value: function(){ + this._directoryContentCache = {}; + } + } +}); \ No newline at end of file diff --git a/js/components/ui/FilePicker/file-picker-model.js b/js/components/ui/FilePicker/file-picker-model.js new file mode 100644 index 00000000..b1df3b4e --- /dev/null +++ b/js/components/ui/FilePicker/file-picker-model.js @@ -0,0 +1,122 @@ +/* +This file contains proprietary software owned by Motorola Mobility, Inc.
+No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ + +var filePickerControllerModule = require("js/components/ui/FilePicker/file-picker-controller"); + +//this is per file picker instance +exports.FilePickerModel = (require("montage/core/core").Montage).create(require("montage/ui/component").Component, { + + fatalError:{ + writable:true, + enumerable:true, + value:null + }, + + _fileFilters:{ + writable:true, + enumerable:false, + value:null + }, + fileFilters:{ + get:function(){ + return this._fileFilters; + }, + set:function(value){ + this._fileFilters = value; + } + }, + _currentFilter:{ + writable:true, + enumerable:false, + value:null + }, + currentFilter:{ + get:function(){ + return this._currentFilter; + }, + set:function(value){ + this._currentFilter = value; + } + }, + + /** + * true -> file selection mode + * false -> directory selection mode + */ + inFileMode:{ + writable:true, + enumerable:false, + value:null + }, + + /** + * pickerMode: ["read", "write"] : specifies if the file picker is opened to read a file/folder or to save a file + */ + pickerMode:{ + writable:true, + enumerable:false, + value:null + }, + + topLevelDirectories:{ + writable:true, + enumerable:true, + value:[] + }, + + currentRoot:{ + writable:true, + enumerable:true, + value:"" + }, + + callback:{ + writable:true, + enumerable:true, + value:null + }, + + /** + * stores the previously viewed directory uris in an array [FILO] per file picker instance + */ + _history:{ + writable:true, + enumerable:true, + value:[]//array of visited URIs for the back/forward arrows + }, + + /** + * store history of folders navigated if it was already not visited last + */ + storeHistory:{ + writable:false, + enumerable:true, + value:function(uri){ + //remove redundant / at end +// uri = new String(uri); +// if((uri.charAt(uri.length - 1) === "/") || (uri.charAt(uri.length - 1) === "\\")){ +// uri = uri.substring(0, (uri.length - 1)); +// } + //console.log("storeHistory: "+uri); + if(uri && (uri !== this._history[this._history.length -1]) && (!!filePickerControllerModule.FilePickerController._directoryContentCache[uri]) && (filePickerControllerModule.FilePickerController._directoryContentCache[uri].type === "directory")){ + //remove history after current pointer + if(this._history.length >0){ + this._history.splice((this.currentHistoryPointer+1), (this._history.length - this.currentHistoryPointer - 1)); + } + //now add the new state + this._history.push(uri); + this.currentHistoryPointer = this._history.length -1; + //console.log("### stored: "+uri+" : pointer="+this.currentHistoryPointer); + } + } + }, + + currentHistoryPointer:{ + writable:true, + enumerable:true, + value:-1 + } +}); \ No newline at end of file diff --git a/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.css b/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.css new file mode 100644 index 00000000..2d9f1189 --- /dev/null +++ b/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.css @@ -0,0 +1,201 @@ +/* + This file contains proprietary software owned by Motorola Mobility, Inc.
+ No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+ (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ + +.picker{ + font-size:12px; + width:510px; + height:500px; + padding-top:15px; + padding-left:15px; + padding-bottom:15px; + color:#ffffff; + background-color:#313131; +} + +.picker .pickerHeader{ + margin-bottom: 10px; +} + +.picker .container{ + border: 1px groove #000000; + overflow: auto; + background-color:#5f5f5f; +} + +.picker .left-nav{ + float:left; + width:20%; + height:86%; + padding-top: 10px; + overflow-x:hidden; + overflow-y:auto +} + +.picker .right-nav{ + float:left; + width:75%; + height:88%; + overflow: hidden; +} + +.picker .right-top{ + border: 1px groove #000000; + overflow: auto; + min-height:150px; + height:75%; + width:auto; +} + +.picker .right-bottom{ + border: 1px groove #000000; + overflow: auto; + height:20%; + padding: 10px; +} + +.picker .driversList{ + padding: 3px; + text-align: center; +} + +.picker .driversList:hover{ + cursor:pointer; +} + +.picker .highlighted{ + background-color: #343434; +} + + +.picker .buttons{ + float:right; + margin-top: 7px; +} + +.picker .historyArrows{ + width:20px; + display:inline; + border-radius: 5px; + border: 1px solid #d1d1d1; +} + +.picker .historyArrows span{ + font-size: 0.7em; + cursor: pointer; +} + +.picker .addressbar{ + display:inline; + margin-left: 2px; + border-radius: 5px; + border: 1px solid #d1d1d1; + padding: 3px; + position:relative; +} + +.picker .addressbar input{ + width:320px; +} + +.picker .dropdownArrow{ + font-size: 0.7em; + cursor: pointer; +} + +.picker .dropdown{ + position:absolute; + z-index:10000; + border: 1px solid #d1d1d1; + background-color: #444; + opacity:0.9; +} + +.picker .dropdown div{ + padding: 3px; +} + +.picker .dropdown div:hover{ + cursor: pointer; + background-color: #343434; +} + +.picker .addressbar .dropdown{ + width:331px; + min-height:50px; +} + +.picker .go{ + margin-left: 5px; + cursor: pointer; +} + +.picker .viewSwitcher{ + display:inline; + margin-left: 30px; +} + +.picker .viewSwitcher span{ + margin-left: 5px; + border-radius: 5px; + border: 1px solid #d1d1d1; + padding: 3px; + cursor: pointer; +} + +.picker .reload{ + position:relative; + margin-left: 15px; + top:4px; + cursor: pointer; +} + +.picker .filters{ + display:inline; + float:left; + margin-left: 12px; + margin-top: 7px; + border-radius: 5px; + border: 1px solid #d1d1d1; + padding: 3px; + /*width:150px;*/ + position:relative; + max-width: 65%; +} + +.picker .filters .dropdown{ + min-height:25px; + /*width:154px;*/ + bottom:21px; + left:-1px; + white-space: nowrap; +} + +.picker .filterVal{ + width:120px; + margin-left: 5px; + background-color: #444; +} + +.picker .disable{ + color:#5f5f5f; + cursor:auto; +} + +.picker .hide{ + display: none; +} + +.picker .viewSelected{ + background-color:#6f6f6f; + pointer:auto; +} + +.picker .okButton{ + margin-right:25px; +} + + + diff --git a/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.html b/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.html new file mode 100644 index 00000000..bf421247 --- /dev/null +++ b/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.js b/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.js new file mode 100644 index 00000000..299d44f5 --- /dev/null +++ b/js/components/ui/FilePicker/pickerNavigator.reel/pickerNavigator.js @@ -0,0 +1,1019 @@ +/* +This file contains proprietary software owned by Motorola Mobility, Inc.
+No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ + +var Montage = require("montage/core/core").Montage, + Component = require("montage/ui/component").Component, + iconsListModule = require("js/components/ui/icon-list-basic/iconsList.reel"), + filePickerControllerModule = require("js/components/ui/FilePicker/file-picker-controller"), + treeModule = require("js/components/ui/tree-basic/tree.reel"); + +var PickerNavigator = exports.PickerNavigator = Montage.create(Component, { + + popup:{ + enumerable: false, + writable: true, + value: null + }, + + mainContentData:{ + enumerable:false, + writable:true, + value:null + }, + + _firstTime: { + enumerable: false, + value: true + }, + + firstTime:{ + get: function(){}, + set: function(){} + }, + + pickerCallback:{ + enumerable:false, + writable:true, + value:null + }, + + selectedItems:{//passed to pickerCallback on click of OK + enumerable:false, + writable:true, + value:[] + }, + + /** + * store a reference to the currently selected node... for single selection + */ + currentSelectedNode:{ + enumerable:false, + writable:true, + value:null + }, + + currentURI:{ + enumerable:false, + writable:true, + value:null + }, + + iconsViewDrawnOnce:{ + enumerable:false, + writable:true, + value:false + }, + + /** + * for tree view only + * will store folder uri vs tree instance map + */ + treeRefHash:{ + enumerable:false, + writable:true, + value:{ + + } + }, + + selectedPickerView:{ + enumerable:false, + writable:true, + value:null + }, + + /** + * Contains the different Views and their update handlers + */ + pickerViews:{ + enumerable:false, + writable:true, + value: function(){ + var that = this; + return { + "iconView":that.updateIconView, + "treeView":that.updateTreeView + } + } + }, + + pickerModel:{ + enumerable:false, + writable:true, + value:null + }, + + willDraw: { + enumerable: false, + value: function() { + + } + }, + draw: { + enumerable: false, + value: function() { + this.filterVal.innerHTML = this.pickerModel.currentFilter; + + if(this.pickerModel.fatalError !== null){ + this.error.innerHTML = this.pickerModel.fatalError; + } + + } + }, + didDraw: { + enumerable: false, + value: function() { + + var that = this; + this.iconList = null; + this.newContent = null; + this.spanEl = null; + + this.addIdentifiers(); + + var topLevelDirs = this.pickerModel.topLevelDirectories; + var leftNav = this.leftNav; + //draw left nav + if(!!topLevelDirs + && (typeof topLevelDirs === "object") + && ('splice' in topLevelDirs) + && ('join' in topLevelDirs) + &&(topLevelDirs.length > 0)){ + + topLevelDirs.forEach(function(dirObj){ + var newDiv = document.createElement("div"); + newDiv.className = "driversList"; + newDiv.innerHTML = dirObj.name; + leftNav.appendChild(newDiv); + if(dirObj.uri === this.pickerModel.currentRoot){ + newDiv.classList.add("highlighted"); + } + + newDiv.addEventListener("click", function(evt){that.handleTopLevelDirectoryClicks(evt, dirObj);}, false); + }, that); + }else{ + console.error("need at least 1 valid top level directory"); + } + + //Draw icon view list + //TODO: check the default view and draw the appropriate view + if(this.mainContentData !== null){ + + this.currentURI = this.pickerModel.currentRoot; + + //draw the IconsList if icons view container is on + if(this.iconViewContainer.style.display === "block"){ + this.iconList = iconsListModule.IconsList.create(); + //console.log(this.mainContentData); + this.iconList.iconsViewDataObject = this.mainContentData; + this.iconList.element = this.iconViewContainer; + this.iconList.needsDraw = true; + this.iconsViewDrawnOnce = true; + this.selectedPickerView = "iconView"; + + if(!this.iconView.classList.contains("viewSelected")){ + this.iconView.classList.add("viewSelected") + } + if(this.treeView.classList.contains("viewSelected")){ + this.treeView.classList.remove("viewSelected"); + } + + }else if(this.treeViewContainer.style.display === "block"){ + //else draw the Tree if tree view container is on + this.renderTree(this.treeViewContainer, this.currentURI); + this.selectedPickerView = "treeView"; + + if(!this.treeView.classList.contains("viewSelected")){ + this.treeView.classList.add("viewSelected") + } + if(this.iconView.classList.contains("viewSelected")){ + this.iconView.classList.remove("viewSelected"); + } + } + + this.updateAddressBar(this.pickerModel.currentRoot); + this.pickerModel.storeHistory(this.pickerModel.currentRoot);//populate history + + this.updateMetadata(this.currentURI); + } + + this.element.addEventListener("openFolder", function(evt){that.handlePickerNavOpenFolder(evt);}, false);//add icon double click event listener to reload iconList with new set of data + this.element.addEventListener("selectedItem", function(evt){that.handlePickerNavSelectedItem(evt);}, false);//for single selection only + this.element.addEventListener("showMetadata", function(evt){that.handlePickerNavShowMetadata(evt);}, false);//show metadata on hover of icon + this.element.addEventListener("updateMetadata", function(evt){that.handlePickerNavUpdateMetadata(evt);}, false);//show metadata on click of icon + //this.addressGo.addEventListener("click", this, false); + this.addressBarUri.addEventListener("keydown", this, false); + this.refreshButton.addEventListener("click", this, false);//refresh - gets from file system directly + this.backArrow.addEventListener("click", this, false); + this.forwardArrow.addEventListener("click", this, false); + this.okButton.addEventListener("click", this, false); + this.cancelButton.addEventListener("click", this, false); + + //populate filters + var filtersDD = this.element.querySelector(".filters .dropdown"); + if(!!this.pickerModel.fileFilters + && (typeof this.pickerModel.fileFilters === "object") + && ('splice' in this.pickerModel.fileFilters) + && ('join' in this.pickerModel.fileFilters)){ + this.pickerModel.fileFilters.forEach(function(aFilter){ + var newDiv = document.createElement("div"); + newDiv.innerHTML = aFilter; + filtersDD.appendChild(newDiv); + newDiv.addEventListener("click", function(evt){that.handleFilterClick(evt, aFilter, filtersDD)}, false); + }, this); + } + + var renderedWidth = this.getComputedWidth(filtersDD); + this.filters.style.width = "" + (parseInt((renderedWidth.substring(0, (renderedWidth.length - 2)))) + 20) + "px"; + + /** + * attach click event listeners to the addressbar dropdown arrows + */ + var dropDownArrows = this.element.getElementsByClassName("dropdownArrow"); + for(var x=0; x0){ + arr.push(temp); + }else{//for unix root / + arr.push("/"); + } + + } + //console.log(arr); + return arr; + } + }, + + renderTree:{ + writable:false, + enumerable:true, + value:function(element, uri){ + if(!!element){ + var tree = treeModule.Tree.create(); + tree.treeViewDataObject = filePickerControllerModule.FilePickerController.prepareContentList(uri, this.pickerModel); + //console.log("renderTree() for "+ uri); + //console.log(tree.treeViewDataObject); + tree.element = element; + tree.needsDraw = true; + } + } + }, + + updateIconView:{ + writable:false, + enumerable:true, + value:function(uri, fromCache){ + var status = true; + var iconViewContainer = this.element.querySelector(".iconViewContainer"); + if((typeof fromCache === 'undefined') || (fromCache === true)){ + this.newContent = filePickerControllerModule.FilePickerController.prepareContentList(uri, this.pickerModel); + } + else{ + this.newContent = filePickerControllerModule.FilePickerController.prepareContentList(uri, this.pickerModel, false); + } + if(!!this.newContent && this.newContent.length > 0){ + //clear selection + this.clearSelection(); + this.spanEl = iconViewContainer.querySelector(".noResults"); + if(!!this.spanEl){ + this.spanEl.style.display = "none"; + } + this.iconList.iconsViewDataObject = this.newContent; + }else{ + this.iconList.iconsViewDataObject = []; + this.spanEl = iconViewContainer.querySelector(".noResults"); + if(!!this.spanEl){ + this.spanEl.style.display = "block"; + }else{ + this.spanEl = document.createElement("span"); + this.spanEl.className = "noResults"; + this.spanEl.innerHTML = "no results"; + this.spanEl.style.display = "block"; + iconViewContainer.appendChild(this.spanEl); + } + status = false; //for no match + } + this.updateMetadata(uri); + + return status; + } + }, + + updateTreeView:{ + writable:false, + enumerable:true, + value:function(uri, fromCache){ + var status = true; + //need to draw every time since an expanded tree won't update with just bound data update + var treeViewContainer = this.element.querySelector(".treeViewContainer"); + //first clean up treeViewContainer + while(treeViewContainer.hasChilden){ + treeViewContainer.removeChild(treeViewContainer.lastChild); + } + //now draw the unexpanded tree with the current uri data + + if(!!treeViewContainer){ + var data = []; + if((typeof fromCache === 'undefined') || (fromCache === true)){ + data = filePickerControllerModule.FilePickerController.prepareContentList(uri, this.pickerModel); + } + else{ + data = filePickerControllerModule.FilePickerController.prepareContentList(uri, this.pickerModel, false); + } + + if(data.length > 0){ + + //clear selection + this.clearSelection(); + this.spanEl = this.element.querySelector(".treeViewContainer").querySelector(".noResults"); + if(!!this.spanEl){ + this.spanEl.style.display = "none"; + } + + var tree = treeModule.Tree.create(); + tree.treeViewDataObject = data; + tree.element = treeViewContainer; + tree.needsDraw = true; + + }else{ + + var treeUl = treeViewContainer.getElementsByTagName("ul")[0]; + if(!!treeUl){ + treeUl.style.display = "none"; + } + + this.spanEl = this.element.querySelector(".treeViewContainer").querySelector(".noResults"); + if(!!this.spanEl){ + this.spanEl.style.display = "block"; + }else{ + this.spanEl = document.createElement("span"); + this.spanEl.className = "noResults"; + this.spanEl.innerHTML = "no results"; + this.spanEl.style.display = "block"; + this.element.querySelector(".treeViewContainer").appendChild(this.spanEl); + } + + status = false; //for no match + } + + this.updateMetadata(uri); + + return status; + } + } + }, + + updateMetadata:{ + enumerable: false, + writable:false, + value:function(currentUri){ + var data = filePickerControllerModule.FilePickerController._directoryContentCache[currentUri]; + var metadata = ""; + if(!!data){ + if(data.name !== ""){ + metadata = "Name: "+data.name; + } + metadata = metadata + "
" + "Type: "+data.type; + if(data.size){metadata = metadata + "
" + "Size: "+data.size;} + if(data.creationDate){metadata = metadata + "
" + "Creation date: "+ this.formatTimestamp(data.creationDate);} + if(data.modifiedDate){metadata = metadata + "
" + "Modified date: "+ this.formatTimestamp(data.modifiedDate);} + } + this.element.getElementsByClassName("right-bottom")[0].innerHTML = metadata; + } + }, + + isOutside:{ + enumerable:true, + value:function(el, targetEl){ + var isOutside = true; + if(el){ + var childElements = el.getElementsByTagName("*");//get a flat NodeList of all the child elements + if(childElements != null){ + for(var i=0; i< childElements.length; i++){ + if(childElements[i] === targetEl){//targetEl matches with an element inside the menu + isOutside = false; + break; + } + } + } + } + return isOutside; + } + }, + /** + * convert timestamp to human readable form + * + * @param: timestamp - UTC milliseconds + */ + formatTimestamp:{ + writable:false, + enumerable: false, + value: function(timestamp) { + var aDate = new Date(); + timestamp = timestamp - (aDate.getTimezoneOffset()*60*1000);//convert from GMT to local timestamp + aDate = new Date(timestamp); + return aDate.toLocaleString(); + } + }, + + /** + * Event Listners + */ + + addIdentifiers:{ + value: function(){ + this.element.identifier = "pickerNav"; + //this.addressGo.identifier = "addressGo"; + this.addressBarUri.identifier = "addressBarUri"; + this.refreshButton.identifier = "refreshButton"; + this.backArrow.identifier = "backArrow"; + this.forwardArrow.identifier = "forwardArrow"; + this.okButton.identifier = "okButton"; + this.cancelButton.identifier = "cancelButton"; + this.iconView.identifier = "iconView"; + this.treeView.identifier = "treeView"; + this.resultsArea.identifier = "resultsArea"; + } + }, + + handleTopLevelDirectoryClicks : { + enumerable: true, + writable: false, + value : function(evt, dirObj){ + this.currentURI = dirObj.uri; + + var status = this.pickerViews()[this.selectedPickerView].call(this, dirObj.uri);//dynamically calls the update function of the current picker view + + this.updateAddressBar(dirObj.uri); + this.pickerModel.currentRoot = dirObj.uri; + + //populate history + this.pickerModel.storeHistory(dirObj.uri); + //disable forward button for explicit new urls + if(!this.forwardArrow.classList.contains("disable")){ + this.forwardArrow.classList.add("disable"); + } + //enable back button if required + if((this.pickerModel.currentHistoryPointer === 1) && this.backArrow.classList.contains("disable")){ + this.backArrow.classList.remove("disable"); + } + + //dehighlight current selection and highlight new selection + var currentHighlighted = evt.target.parentNode.querySelector(".highlighted"); + if(!!currentHighlighted){ + currentHighlighted.classList.remove("highlighted"); + } + if(!evt.target.classList.contains("highlighted")){ + evt.target.classList.add("highlighted"); +