1 /*jslint devel: true */
2 /*global $, app, TemplateManager, Helpers */
11 (function () { // strict mode wrapper
24 lockedFolders: ['ringtones'],
33 * @type {bool} block taps until the page change is completed
38 * @type {TemplateManager}
40 templateManager: null,
55 SELECT_ALL_HEIGHT: 32,
58 * @const {number} header height, set on domReady
63 * name of row gradient class
65 CSS_GRADIENT_CLASS: 'gradientBackground',
68 * Standard tabbar actions
71 STD_TABBAR_EDIT_ACTION: 0,
72 STD_TABBAR_MORE_ACTION: 1,
73 STD_TABBAR_EXIT_ACTION: 2,
79 EDIT_TABBAR_DELETE_ACTION: 0,
80 EDIT_TABBAR_MOVE_ACTION: 1,
81 EDIT_TABBAR_COPY_ACTION: 2,
82 EDIT_TABBAR_CANCEL_ACTION: 3,
84 currentHeaderHeight: null,
85 currentScrollPosition: null,
90 init: function Ui_init(storages) {
91 this.templateManager = new TemplateManager();
92 this.helpers = new Helpers();
93 // Disable text selection
94 $.mobile.tizen.disableSelection(document);
95 $(document).ready(this.initDom.bind(this, storages));
98 initDom: function Ui_initDom(storages) {
101 this.templateManager.loadToCache(['main', 'fileRow', 'folderRow', 'levelUpRow', 'emptyFolder'], function () {
102 $('#main').append($(self.templateManager.get('main')).children()).trigger('pagecreate');
104 self.displayStorages(storages);
111 addEvents: function Ui_addEvents() {
114 document.addEventListener('webkitvisibilitychange', function () {
115 if (document.webkitVisibilityState === 'visible') {
116 app.ui.editMode = false;
117 app.refreshCurrentPage(true);
121 window.addEventListener('tizenhwkey', function(e) {
122 var uri = $('#navbar span+span').attr('uri');
123 if (e.keyName == "back") {
125 if ( app.ui.root === false ) {
126 $('#fileList').empty();
127 app.ui.prepareFolderRow(0, "root");
130 tizen.application.getCurrentApplication().exit();
132 } else if ($.mobile.popup.active !== undefined) {
133 $(".ui-popup").popup('close');
138 if (self.editMode === true) {
139 self.handleCancelEditAction();
144 $(window).resize( function () {
145 $.mobile.activePage.page('refresh')
148 // touch events for all nodes
150 .on('tap', 'li.levelUp', function () {
151 if (self.editMode === true) {
152 self.handleCancelEditAction();
156 .on('tap', 'li.node', function (e) {
157 self.handleNodeClick($(this), true);
159 .on('change', 'input[type=checkbox]', function (e) {
160 self.handleNodeClick($(this).closest('li.node'), false);
162 .on('touchstart', 'li', function (event) {
163 $(this).addClass(self.CSS_GRADIENT_CLASS);
165 .on('touchend touchmove', 'li', function (event) {
166 $(this).removeClass(self.CSS_GRADIENT_CLASS);
169 $('.selectAll input').on('change', this.handleSelectAllChange.bind(this));
172 $('#navbar').on('tap', 'span', function () {
173 var uri = $(this).attr('uri');
174 if (uri === 'home') {
175 if (app.currentPath !== '') {
176 app.displayStorages();
178 } else if (uri === app.model.currentPath) {
179 app.displayFolder(uri,true);
181 if (self.editMode === true) {
182 self.handleCancelEditAction();
184 app.displayFolder(uri);
189 $('#levelUpBtn').on('tap', function () {
190 if (self.editMode === true) {
191 self.handleCancelEditAction();
196 $('#homeBtn').on('tap', app.displayStorages.bind(app));
199 $('#editActionBtn').on('tap', this.handleEditAction.bind(this));
202 $('#deleteActionBtn').on('tap', this.handleDeleteAction.bind(this));
205 $('#cancelActionBtn').on('tap', function (e) {
208 self.handleCancelEditAction();
212 $('#copyActionBtn').on('tap', this.handleCopyAction.bind(this));
215 $('#moveActionBtn').on('tap', this.handleMoveAction.bind(this));
218 $('a#pasteActionBtn').on('tap', function () {
219 app.pasteClipboard.bind(app)();
220 $("#morePopup").popup('close');
223 // remove active class
224 $('[data-role = "tabbar"] li > a').on('click', function () {
225 $(this).removeClass('ui-focus, ui-btn-active');
228 $('.ui-myExit').on('tap', app.exit);
230 // add folder popup actions
231 $('#addFolderPopup').on("popupafterclose", function () {
233 $('#newFolderName').val('New folder');
236 $('#newFolderName').on('tap', function () {
237 if ($(this).attr('value') === 'New folder') {
238 $(this).attr('value', '');
242 $('#saveNewFolder').on('tap', this.saveNewFolder.bind(this));
243 $('#newFolderForm').on('submit', this.saveNewFolder.bind(this));
246 saveNewFolder: function Ui_saveNewFolder(e) {
247 var folderName = $('#newFolderName').val().trim(), status = true,
249 $("#addFolderPopup").popup('open', {
255 $("#addFolderPopup").one("popupafterclose", function () {
256 if (folderName === '') {
257 self.alertPopup("Empty folder name", open);
259 } else if (folderName.match(/[\*\.\/\\\?\"\'\:<>|]/)) {
260 self.alertPopup("The following special characters "
261 +"are not allowed: *./\\?:<>|'\"", open);
264 status = app.createDir(folderName, open);
267 $("#addFolderPopup").popup('close');
271 alertPopup: function (text, callback) {
272 $("#alertPopup .text").text(text);
273 $("#alertPopup").popup('open', {'positionTo': 'window'})
274 if (callback instanceof Function) {
275 $("#alertPopup").one("popupafterclose", function () {
281 confirmPopup: function (text, confirmCallback, completeCallback) {
282 $("#confirmPopup .text").text(text);
283 setTimeout(function () {
284 $("#confirmPopup").popup('close');
285 setTimeout(function () {
286 $("#confirmPopup").popup('open');
287 $("#confirmPopup .confirm").one("tap", function () {
288 $("#confirmPopup").popup('close');
289 if (confirmCallback instanceof Function) {
293 if (completeCallback instanceof Function) {
294 $("#confirmPopup").one('popupafterclose', function () {
302 clearTabbars: function Ui_clearTabbars() {
303 $('[data-role = "tabbar"] li > a').removeClass('ui-focus, ui-btn-active');
307 * Handler for node click
309 * @param {boolean} toggleCheckbox
311 handleNodeClick: function Ui_handleNodeClick(node, toggleCheckbox) {
313 app.model.loadInternalStorages(function () { app.displayStorages(); });
315 } else if (this.editMode === true) {
316 //if edit mode is on toggle checkbox state
317 if (toggleCheckbox === true) {
318 this.toggleCheckBoxState(node); // select the checkbox
321 this.refreshSelectAllStatus();
322 this.refreshEditMenu();
323 } else if (node.hasClass('folder')) {
324 // otherwise display folder
325 app.displayFolder(node.attr('uri'));
328 app.openFile(node.attr('uri'), node.attr('fullUri'));
333 * Handler for edit action
335 handleEditAction: function Ui_handleEditAction() {
336 this.editMode = true;
338 $('.standardTabbar').hide();
339 $('div.editTabbar').show();
340 this.disableControlBarButtons($('div.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]);
341 $('#fileList .folder .nodename').animate({'width': '70%'});
342 this.showEditCheckBoxes();
346 * Handler for cancel edit action
348 handleCancelEditAction: function Ui_handleCancelEditAction() {
349 this.editMode = false;
351 $('div.editTabbar').hide();
352 $('.standardTabbar').show();
353 $('#fileList .folder .nodename').animate({'width': '75%'});
354 this.hideEditCheckBoxes();
355 if (this.isFileListEmpty()) {
356 $('#editActionBtn').addClass('vhidden');
361 * Handler for delete action
363 handleDeleteAction: function Ui_handleDeleteAction(e) {
364 var nodesToDelete = [],
372 this.confirmPopup('Selected nodes will be deleted. Are you sure?',
374 $('ul#fileList input:checkbox:checked').each(function (index) {
375 $rowElement = $(this).closest('li');
377 id: $rowElement.attr('id'),
378 uri: $rowElement.attr('uri'),
379 name: $rowElement.attr('label'),
380 folder: $rowElement.hasClass('folder')
383 if (nodesToDelete.length > 0) {
384 app.deleteNodes(nodesToDelete);
385 self.scrollContentTo(0);
386 $('ul#fileList input:checkbox:checked').remove();
387 self.refreshEditMenu();
397 * Handler for copy action
399 handleCopyAction: function Ui_handleCopyAction(e) {
405 if (this.editMode === true) {
406 $('ul#fileList input:checkbox:checked').each(function (index) {
407 paths.push($(this).closest('li').attr('uri'));
409 app.saveToClipboard(paths, app.clipboard.COPY_MODE_ID);
414 * Handler for move action
416 handleMoveAction: function Ui_handleMoveAction(e) {
422 if (this.editMode === true) {
423 $('ul#fileList input:checkbox:checked').each(function (index) {
424 paths.push($(this).closest('li').attr('uri'));
426 app.saveToClipboard(paths, app.clipboard.MOVE_MODE_ID);
431 * Handler for paste action
433 handlePasteAction: function Ui_handlePasteAction() {
437 * Scrolls content to the specified position
439 scrollContentTo: function scrollContentTo(value) {
440 $('#main [data-role="content"]').scrollview('scrollTo', 0, value);
444 * @param {FileSystemStorage[]} nodes Storage elements
446 displayStorages: function Ui_displayStorages(nodes) {
447 var len = nodes.length, nodeName, i;
449 this.updateNavbar('');
450 $('#fileList').empty();
452 for (i = 0; i < len; i = i + 1) {
453 nodeName = nodes[i].label.trim();
455 && (nodes[i].type === 0 || nodes[i].type === 'INTERNAL')
456 && nodeName.indexOf('wgt-') === -1
457 && $.inArray(nodeName, this.lockedFolders) === -1
460 app.model.isStorageExists(nodeName,
461 app.ui.prepareFolderRow.bind(app.ui, i, nodeName), null);
463 this.prepareFolderRow(i, nodeName);
468 $('#levelUpBtn').addClass('vhidden');
469 $('#homeBtn').addClass('vhidden');
471 $('#editActionBtn').addClass('vhidden');
472 $('#moreActionBtn').addClass('vhidden');
473 $('h1#mainTitle').html('Media');
476 this.scrollContentTo(0);
479 this.resetDefaultCheckBoxLabelEvents();
480 this.hideSelectAllArea();
481 this.handleCancelEditAction();
484 prepareFolderRow: function (id, name) {
485 $(this.templateManager.get('folderRow', {
490 })).appendTo('#fileList');
494 * File comparison function using their names (case insensitive)
500 fileComparison: function fileComparison(x, y) {
501 var a = x.name.toLowerCase(),
502 b = y.name.toLowerCase();
514 * renders node list for folder
515 * @param {string} folderName
516 * @param {File[]} nodes
517 * @param {bool} [refresh=false]
519 displayFolder: function Ui_displayFolder(folderName, nodes, refresh) {
520 var len = nodes.length,
521 listElements = [this.templateManager.get('levelUpRow')],
526 refresh = refresh || false;
529 this.updateTitle(this.templateManager.modifiers.escape(folderName));
531 this.updateNavbar(this.templateManager.modifiers.escape(folderName));
532 this.refreshPasteActionBtn();
534 nodes.sort(this.fileComparison);
537 for (i = 0; i < len; i = i + 1) {
538 nodeName = nodes[i].name.trim();
539 if (nodeName !== '') {
540 if (nodes[i].isDirectory) {
542 listElements.push(this.templateManager.get('folderRow', {
545 uri: nodes[i].fullPath,
546 fullUri: nodes[i].toURI()
550 listElements.push(this.templateManager.get('fileRow', {
553 uri: nodes[i].fullPath,
554 fullUri: nodes[i].toURI(),
555 thumbnailURI: this.helpers.getThumbnailURI(nodeName, nodes[i])
561 if (listElements.length === 1) {
562 // set content for empty folder
563 listElements.push(this.templateManager.get('emptyFolder'));
564 // hide edit button for empty content
565 $('#editActionBtn').addClass('vhidden');
567 $('#editActionBtn').removeClass('vhidden');
570 // scroll to top of list
571 this.scrollContentTo(0);
573 $('#levelUpBtn').removeClass('vhidden');
574 $('#homeBtn').removeClass('vhidden');
575 $('#moreActionBtn').removeClass('vhidden');
577 if (refresh === true && this.editMode === true) {
578 $.each($('#fileList .ui-checkbox input:checked'), function () {
579 checkedRows.push($(this).closest('li').attr('id'));
584 $('#fileList').html(listElements.join(''))
589 if (this.editMode === true) {
590 $('.selectAll').show();
591 $('ul#fileList > li').css('paddingLeft', '2rem');
592 $('.my-ui-checkbox').removeClass('hidden');
594 if (refresh === true) {
595 // restore checked checkboxes
596 for (i = 0; i < checkedRows.length; i += 1) {
597 $('#' + checkedRows[i] + ' input:checkbox')
598 .attr('checked', 'checked')
599 .data('checkboxradio').refresh();
603 $('.selectAll').hide();
604 $('ul#fileList > li').css('paddingLeft', '0');
605 $('.my-ui-checkbox').addClass('hidden');
607 if (!refresh) this.hideSelectAllArea();
611 * Toggle a checkbox associated with a given list element
612 * @param {jQuery} listElement
614 toggleCheckBoxState: function Ui_toggleCheckBoxState(listElement) {
616 var checkboxInput = null;
618 checkboxInput = listElement.find('form > div.ui-checkbox input');
620 .attr('checked', !checkboxInput.attr('checked'))
621 .data('checkboxradio').refresh();
625 * Shows item checkboxes and topbar with select all option
627 showEditCheckBoxes: function Ui_showEditCheckBoxes() {
630 this.showSelectAllArea();
632 $('ul#fileList > li').animate({paddingLeft: '2rem'}, 500, 'swing', function () {
633 self.editMode = true;
634 $('.my-ui-checkbox').removeClass('hidden');
639 * Hides item checkboxes and topbar with select all option
640 * All checkboxes are auto uncheked
642 hideEditCheckBoxes: function Ui_hideEditCheckBoxes() {
645 this.hideSelectAllArea(); // hide select all option topbar
647 $('ul#fileList > li').animate({paddingLeft: '0'}, 200, 'swing', function () {
648 $('.my-ui-checkbox').addClass('hidden');
649 $.mobile.activePage.page('refresh');
652 // uncheck all checkboxes
653 $('ul#fileList input[type=checkbox]').each(function (index) {
654 var checkboxradio = $(this).data('checkboxradio');
656 $(this).attr('checked', false);
659 checkboxradio.refresh();
663 //uncheck select all input
664 $('.ui-header .selectAll .ui-checkbox input')
665 .attr('checked', false)
666 .data('checkboxradio')
671 * Save current header and content height
673 saveHeights: function Ui_saveHeights() {
674 this.currentHeaderHeight = $('#main div[data-role="header"]').height();
675 this.currentScrollPosition = $('#main div[data-role="content"]').scrollview('getScrollPosition').y;
679 * Changes content scroll position after showing/hiding selectAllArea
681 changeContentScrollPosition: function Ui_changeContentScrollPosition() {
683 if (this.currentScrollPosition !== 0) {
684 diff = $('#main div[data-role="header"]').height() - this.currentHeaderHeight;
685 $('#main div[data-role="content"]').scrollview('scrollTo', 0, -(this.currentScrollPosition + diff));
690 * Shows topbar with select all option
692 showSelectAllArea: function Ui_showSelectAllArea() {
694 $('.selectAll').show();
695 $.mobile.activePage.page('refresh');
696 this.changeContentScrollPosition();
700 * Hides topbar with select all option
702 hideSelectAllArea: function Ui_hideSelectAllArea() {
704 $('.selectAll').hide();
705 $.mobile.activePage.page('refresh');
706 this.changeContentScrollPosition();
710 * Enable specified options for tabbar
711 * @param {object} tabbar
712 * @param {array} enableOptions options to enable
714 enableControlBarButtons: function Ui_enableControlBarButtons(tabbar, enableOptions) {
716 len = enableOptions.length;
718 for (i = 0; i < len; i += 1) {
719 tabbar.tabbar('enable', enableOptions[i]);
724 * Disable specified options for tabbar
725 * @param {object} tabbar controlbar
726 * @param {array} disableOptions options to enable
728 disableControlBarButtons: function Ui_disableControlBarButtons(tabbar, disableOptions) {
730 len = disableOptions.length;
732 for (i = 0; i < len; i += 1) {
733 tabbar.tabbar('disable', disableOptions[i]);
738 * @param {string} path
740 updateTitle: function Ui_updateTitle(path) {
741 var regexp = new RegExp('([^\/])+$', 'g'),
742 match = path.match(regexp),
743 lastDir = match[0] || '(dir)';
744 $('h1#mainTitle').html(lastDir);
748 * @param {string} path
750 updateNavbar: function Ui_updateNavbar(path) {
751 var html = ['<span uri="home">Media</span>'],
756 if (typeof path === 'string' && path !== '') {
757 splitted = path.split('/');
758 len = splitted.length;
760 for (i = 0; i < len; i = i + 1) {
761 html.push('<span uri="' + splitted.slice(0, i + 1).join('/') + '">' + splitted[i] + '</span>');
764 $('#navbar').html(html.join(' > '));
767 handleSelectAllChange: function Ui_handleSelectAllChange() {
768 var $selectAllInput = $('.ui-header .selectAll .ui-checkbox input');
769 $selectAllInput.data('checkboxradio').refresh();
771 if ($selectAllInput.is(':checked')) {
772 // check all checkboxes
773 $('ul#fileList input[type=checkbox]').each(function (index) {
774 $(this).attr('checked', true);
775 $(this).data('checkboxradio').refresh();
778 this.enableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]);
780 $('ul#fileList input[type=checkbox]').each(function (index) {
781 $(this).attr('checked', false);
782 $(this).data('checkboxradio').refresh();
785 this.disableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]);
792 refreshSelectAllStatus: function Ui_refreshSelectAllStatus() {
793 var $selectAllInput = $('.ui-header .selectAll .ui-checkbox input');
794 // update status of select all checkbox
795 if ($('ul#fileList input:checkbox:not(:checked)').length === 0) {
797 $selectAllInput.attr('checked', true).data('checkboxradio').refresh();
799 // some node is not checked
800 $selectAllInput.attr('checked', false).data('checkboxradio').refresh();
805 * Refresh activity of edit menu
807 refreshEditMenu: function () {
808 if ($('ul#fileList input:checkbox:checked').length > 0) {
809 this.enableControlBarButtons($('.editTabbar'),
810 [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]);
812 this.disableControlBarButtons($('.editTabbar'),
813 [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]);
818 * Unbinds default events for checkbox labels
820 resetDefaultCheckBoxLabelEvents: function Ui_resetDefaultCheckBoxLabelEvents() {
821 $('div.ui-checkbox > label')
822 .unbind('vmousedown')
824 .unbind('vmouseover')
829 * Remove html node element from list
830 * @param {string} nodeId node id
832 removeNodeFromList: function Ui_removeNodeFromList(nodeId) {
833 $('ul#fileList > li#' + nodeId).remove();
835 // hide select All checkbox if removed all elements;
836 if ($('ul#fileList > li.node').length === 0) {
837 this.hideSelectAllArea();
844 refreshPasteActionBtn: function Ui_refreshPasteActionBtn() {
845 if (app.emptyClipboard()) {
846 $('#pasteActionBtnRow').addClass('hidden');
848 $('#pasteActionBtnRow').removeClass('hidden');
852 isFileListEmpty: function Ui_isFileListEmpty() {
853 return ($('ul#fileList').children('.node').length < 1);