Repository: ambari Updated Branches: refs/heads/trunk 8bb5cafba -> 623d2f2cf
http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/components/uploader.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/uploader.js b/contrib/views/files/src/main/resources/ui/app/components/uploader.js index 5837c36..919e4dd 100644 --- a/contrib/views/files/src/main/resources/ui/app/components/uploader.js +++ b/contrib/views/files/src/main/resources/ui/app/components/uploader.js @@ -40,8 +40,8 @@ App.FileUploaderComponent = Ember.Component.extend({ var label = this.get('value').replace(/\\/g, '/').replace(/.*\//, ''); var log = numFiles > 1 ? numFiles + ' files selected' : label; - this.set('parentView.controlInput.value',log) - this.set('parentView.files',files) + this.set('parentView.controlInput.value',log); + this.set('parentView.files',files); }.observes('files') }); @@ -61,10 +61,10 @@ App.FileUploaderComponent = Ember.Component.extend({ uploadBtn.start(); uploader.on('progress',function (e) { uploadBtn.setProgress(e.percent/100); - }) + }); uploader.upload(file,{path:path}).finally(Em.run.bind(this,reset)); } - }; + } }, clear:function () { this.set('fileInput.files',null); @@ -99,7 +99,7 @@ App.FileUploaderComponent = Ember.Component.extend({ if (!Ember.isEmpty(input.files)) { this.set('files', input.files); } - }, + } }), controlInput:Ember.TextField.create({ readonly:true, http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js b/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js new file mode 100644 index 0000000..88a437f --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +var App = require('app'); + +var _permissionsProp = function(n, l) { + return function (arg,val) { + if (arguments.length > 1) { + this.set('permissions', this.replaceAt(n,(val)?l:'-')); + return val; + } + return this.get('permissions')[n]===l; + }; +}; + +App.ChmodModalController = Em.ObjectController.extend({ + needs:['files'], + classNames:'chmod-row', + file:Em.computed.alias('content'), + permissions:Em.computed.alias('file.permission'), + usrR:_permissionsProp(1, 'r').property('permissions'), + usrW:_permissionsProp(2, 'w').property('permissions'), + usrE:_permissionsProp(3, 'x').property('permissions'), + grpR:_permissionsProp(4, 'r').property('permissions'), + grpW:_permissionsProp(5, 'w').property('permissions'), + grpE:_permissionsProp(6, 'x').property('permissions'), + otrR:_permissionsProp(7, 'r').property('permissions'), + otrW:_permissionsProp(8, 'w').property('permissions'), + otrE:_permissionsProp(9, 'x').property('permissions'), + replaceAt:function (index,p) { + return this.get('permissions').substr(0, index) + p + perm.substr(index + p.length);; + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/controllers/error.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/error.js b/contrib/views/files/src/main/resources/ui/app/controllers/error.js index ecf7749..efcc4de 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/error.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/error.js @@ -21,7 +21,7 @@ App.ErrorController = Ember.ObjectController.extend({ toggleStackTrace:function () { var value = this.get('isExpanded'); this.set('isExpanded', !value); - }, + } }, isExpanded: false, @@ -45,5 +45,5 @@ App.ErrorController = Ember.ObjectController.extend({ trace = json.trace; } return trace; - }.property('content'), + }.property('content') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/controllers/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/file.js b/contrib/views/files/src/main/resources/ui/app/controllers/file.js index 7992814..ce11923 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/file.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/file.js @@ -19,10 +19,6 @@ var App = require('app'); App.FileController = Ember.ObjectController.extend({ - init:function () { - this.set('content.selected', false); - this._super(); - }, actions:{ download:function (option) { if (this.get('content.readAccess')) { @@ -32,7 +28,7 @@ App.FileController = Ember.ObjectController.extend({ } }, showChmod:function () { - this.toggleProperty('chmodVisible',true); + this.send('showChmodModal',this.get('content')); }, rename:function (opt,name) { var file = this.get('content'), @@ -51,12 +47,6 @@ App.FileController = Ember.ObjectController.extend({ editName:function () { this.set('isRenaming',true); }, - chmod:function (r) { - var record = this.get('content'); - this.store - .chmod(record) - .then(null,Em.run.bind(this,this.chmodErrorCallback,record)); - }, open:function (file) { if (!this.get('content.readAccess')) { return false; @@ -88,9 +78,9 @@ App.FileController = Ember.ObjectController.extend({ var thisFile = this.get('content.id'); return movingFile === thisFile; }.property('parentController.movingFile'), - + setSelected:function (controller,observer) { - this.set('selected',this.get(observer)) + this.set('selected',this.get(observer)); }.observes('content.selected'), renameSuccessCallback:function (record,error) { @@ -98,13 +88,8 @@ App.FileController = Ember.ObjectController.extend({ this.sendAlert(error); }, - chmodErrorCallback:function (record,error) { - record.rollback(); - this.sendAlert({message:'Permissions change failed'}); - }, - deleteErrorCallback:function (record,error) { - this.parentController.model.pushRecord(record); + this.get('parentController.model').pushRecord(record); this.send('showAlert',error); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/controllers/files.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/files.js b/contrib/views/files/src/main/resources/ui/app/controllers/files.js index 8de6a4b..9c46de1 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/files.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/files.js @@ -21,10 +21,11 @@ var bind = Ember.run.bind; App.FilesController = Ember.ArrayController.extend({ actions:{ - moveFile:function (opt,file) { - var src, + moveFile:function (opt,fileArg) { + var src, title, + file = fileArg || this.get('selectedFiles.firstObject'), moving = this.get('movingFile'); - file = file || this.get('selectedFiles.firstObject'); + if (opt == 'cut') { src = file.toJSON({includeId: true}); src = Em.merge(src,{name:file.get('name'),path:file.get('path')}); @@ -45,7 +46,7 @@ App.FilesController = Ember.ArrayController.extend({ }, renameDir:function (path,newName) { var _this = this, - basedir = path.substring(0,path.lastIndexOf('/')+1), + basedir = path.substring(0,path.lastIndexOf('/')+1); newPath = basedir + newName; if (path === newPath) { @@ -108,11 +109,19 @@ App.FilesController = Ember.ArrayController.extend({ this.set('sortAscending',true); } }, + confirmChmod:function (file) { + this.store + .chmod(file) + .then(null,Em.run.bind(this,this.chmodErrorCallback,file)); + }, clearSearchField:function () { this.set('searchString',''); } }, init:function () { + if (App.testing) { + return this._super(); + } var controller = this; var adapter = controller.store.adapterFor('file'); var url = adapter.buildURL('upload'); @@ -148,7 +157,7 @@ App.FilesController = Ember.ArrayController.extend({ return this.get('content').filterBy('selected', true); }.property('[email protected]'), canConcat:function () { - return this.get('selectedFiles').filterProperty('isDirectory').get('length')==0; + return this.get('selectedFiles').filterProperty('isDirectory').get('length')===0; }.property('selectedFiles.length'), searchString:'', @@ -173,11 +182,24 @@ App.FilesController = Ember.ArrayController.extend({ deleteErrorCallback:function (record,error) { this.model.pushRecord(record); - this.send('showAlert',error); + this.throwAlert(error); + }, + + chmodErrorCallback:function (record,error) { + record.rollback(); + this.throwAlert({message:'Permissions change failed'}); }, throwAlert:function (error) { this.send('showAlert',error); + }, + + showSpinner:function () { + this.set('isLoadingFiles',true); + }, + + hideSpinner:function () { + this.set('isLoadingFiles',false); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js b/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js index f2ef6b6..100c5b3 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js @@ -20,11 +20,6 @@ App.FilesAlertController = App.ErrorController.extend({ content:null, output:function () { var error = this.get('content'),output; - if (error instanceof Em.Error) { - output = error; - } else { - output = {status:error.status, message:error.statusText||error.message}; - }; - return output; + return (error instanceof Em.Error)?error:{status:error.status, message:error.statusText||error.message}; }.property('content') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/initialize.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/initialize.js b/contrib/views/files/src/main/resources/ui/app/initialize.js index 8b79492..43300f4 100644 --- a/contrib/views/files/src/main/resources/ui/app/initialize.js +++ b/contrib/views/files/src/main/resources/ui/app/initialize.js @@ -30,6 +30,7 @@ require('templates/application'); require('templates/index'); require('templates/files'); require('templates/error'); +require('templates/modal/chmod'); require('templates/util/errorRow'); require('templates/util/fileRow'); @@ -39,7 +40,6 @@ require('templates/components/deletePopover'); require('templates/components/mkdirInput'); require('templates/components/contextMenu'); require('templates/components/deleteBulk'); -require('templates/components/chmodInput'); ////////////////////////////////// // Models @@ -55,6 +55,7 @@ require('controllers/files'); require('controllers/file'); require('controllers/error'); require('controllers/filesAlert'); +require('controllers/chmodModal'); ///////////////////////////////// // Components @@ -71,7 +72,6 @@ require('components/popoverDelete'); require('components/bulkCheckbox'); require('components/mkdirInput'); require('components/toggleContext'); -require('components/chmodInput'); ///////////////////////////////// // Views @@ -79,6 +79,7 @@ require('components/chmodInput'); require('views/file'); require('views/filesAlert'); +require('views/modalChmod'); ///////////////////////////////// // Routes http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/models/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/models/file.js b/contrib/views/files/src/main/resources/ui/app/models/file.js index 2a8c83c..0e31f71 100644 --- a/contrib/views/files/src/main/resources/ui/app/models/file.js +++ b/contrib/views/files/src/main/resources/ui/app/models/file.js @@ -45,7 +45,7 @@ App.File = DS.Model.extend({ return splitpath.get(splitpath.length-1); }.property('path'), date:function () { - return parseInt(moment(this.get('modificationTime')).format('X')) + return parseInt(moment(this.get('modificationTime')).format('X')); }.property('modificationTime'), size: Em.computed.alias('len') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/routes/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/routes/file.js b/contrib/views/files/src/main/resources/ui/app/routes/file.js index 5df1c0b..e99fc77 100644 --- a/contrib/views/files/src/main/resources/ui/app/routes/file.js +++ b/contrib/views/files/src/main/resources/ui/app/routes/file.js @@ -28,12 +28,17 @@ App.FilesRoute = Em.Route.extend({ refreshDir:function () { this.refresh(); }, + loading:function (argument) { + var target = this.controllerFor('files'); + target.showSpinner(); + this.router.one('didTransition', target, 'hideSpinner'); + }, error:function (error,transition,e) { if (this.router._lookupActiveView('files')) { this.send('showAlert',error); } else { return true; - }; + } }, dirUp: function () { var currentPath = this.controllerFor('files').get('path'); @@ -42,7 +47,27 @@ App.FilesRoute = Em.Route.extend({ return this.transitionTo('files',{queryParams: {path: target}}); }, willTransition:function (argument) { - this.send('removeAlert'); + var hasModal = this.router._lookupActiveView('modal.chmod'), + hasAlert = this.router._lookupActiveView('files.alert'); + + Em.run.next(function(){ + if (hasAlert) this.send('removeAlert'); + if (hasModal) this.send('removeChmodModal'); + }.bind(this)); + }, + showChmodModal:function (content) { + this.controllerFor('chmodModal').set('content',content); + this.render('modal.chmod',{ + into:'files', + outlet:'modal', + controller:'chmodModal' + }); + }, + removeChmodModal:function () { + this.disconnectOutlet({ + outlet: 'modal', + parentView: 'files' + }); }, showAlert:function (error) { this.controllerFor('filesAlert').set('content',error); @@ -64,16 +89,14 @@ App.FilesRoute = Em.Route.extend({ return this.store.listdir(path); }, afterModel:function (model) { - var self = this; - var model = model; this.store.filter('file',function(file) { if (!model.contains(file)) { return true; - }; + } }).then(function (files) { files.forEach(function (file) { file.store.unloadRecord(file); - }) + }); }); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/styles/application.less ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/styles/application.less b/contrib/views/files/src/main/resources/ui/app/styles/application.less index c44923e..74bcecb 100644 --- a/contrib/views/files/src/main/resources/ui/app/styles/application.less +++ b/contrib/views/files/src/main/resources/ui/app/styles/application.less @@ -155,6 +155,12 @@ } } } + .fa-spin { + -webkit-animation: spin 0.7s infinite linear; + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + } .chmod-row { &:hover > td { background-color: #fff; @@ -224,7 +230,7 @@ } } } - } + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs index aa86ed1..6758b48 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs @@ -19,7 +19,7 @@ {{#dropdown-wrap}} <div id="context-menu"> <ul class="dropdown-menu dropdown-context compressed-context" role="menu"> - {{#if view.target.content.isDirectory}} + {{#if view.target.content.isDirectory}} <li {{bind-attr class="view.target.content.readAccess::disabled"}}><a tabindex="-1" href="#" {{action 'open'}}>Open folder</a></li> {{else}} <li {{bind-attr class="view.target.content.readAccess::disabled"}}><a tabindex="-1" href="#" {{action 'download'}}>Download</a></li> http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs index 0db9240..4fd7c98 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs @@ -16,7 +16,7 @@ limitations under the License. }} -{{#unless isMkdir}} +{{#unless isMkdir}} <button type="button" {{action 'edit'}} {{bind-attr class=":btn :btn-default :btn-sm :pull-right :mkdirwrap"}}> <i class="fa fa-plus"></i> New directory </button> http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/templates/files.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/files.hbs b/contrib/views/files/src/main/resources/ui/app/templates/files.hbs index 76c439d..70e5625 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/files.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/files.hbs @@ -18,11 +18,11 @@ <div class="panel-default panel-files"> <div class="panel-heading"> - {{!-- BREADCRUMBS --}} + {{!-- BREADCRUMBS --}} {{bread-crumbs path=path}} - + <div class="um-section"> - {{!-- UPLOADER --}} + {{!-- UPLOADER --}} <div {{bind-attr class="isUploading::hide :pull-right" }}> <button {{action 'upload' 'close'}} type="button" class="close" aria-hidden="true">×</button> {{file-uploader path=path uploader=uploader class=" upload-area pull-right" alert='showAlert'}} @@ -34,7 +34,7 @@ </button> </div> - {{!-- MKDIR --}} + {{!-- MKDIR --}} {{mkdir-input create="mkdir" path=path}} </div> @@ -88,7 +88,7 @@ </ul> </div> </th> - <th class="check"> + <th class="check"> {{#dropdown-wrap}} <div id="bulkDropdown" class="btn-group"> <span class="input-group-addon"> @@ -167,7 +167,7 @@ <td > <ul class="list-inline file-actions text-right"> <li> - <a href="#" {{action 'moveFile' 'move'}} data-toggle="tooltip" data-placement="bottom" title="Paste"><i class="fa fa-clipboard fa-lg"></i></a> + <a href="#" {{action 'moveFile' 'move'}} data-toggle="tooltip" data-placement="bottom" title="Paste"><i class="fa fa-clipboard fa-lg"></i></a> </li> </ul> </td> @@ -177,17 +177,30 @@ </tr> {{/if}} {{/unless}} - {{#each fileList itemController="file"}} - {{partial 'util/fileRow'}} - {{/each}} + </tbody> + {{#unless isLoadingFiles}} + {{cloaked-collection + cloakView="file" + content=fileList + itemController="file" + loadingHTML='<td colspan="8" class="text-center"><i class="fa fa-refresh"></i></td>' + tagName='tbody' + defaultHeight='47'}} + {{else}} + <tbody> + <td colspan="8" class="text-center"><i class="fa fa-refresh fa-spin"></i></td> + </tbody> + {{/unless}} + <tbody> {{#unless fileList}} - <tr> - <td colspan="8"> - No files - </td> - </tr> + <tr {{bind-attr class="isLoadingFiles:hide"}} > + <td colspan="8"> + No files + </td> + </tr> {{/unless}} </tbody> </table> {{context-menu target=targetContextMenu}} + {{outlet modal}} </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/templates/modal/chmod.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/modal/chmod.hbs b/contrib/views/files/src/main/resources/ui/app/templates/modal/chmod.hbs new file mode 100644 index 0000000..5ef620c --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/modal/chmod.hbs @@ -0,0 +1,97 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 + + 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. +}} + + +<div class="modal chmodal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static"> + <div class="modal-dialog modal-sm"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + <h4 class="modal-title">Edit permission</h4> + </div> + <div class="modal-body"> + + <form class="form-horizontal" role="form"> + <div class="form-group"> + <label class="col-sm-2 control-label">User</label> + <div class="col-sm-10"> + <div class="btn-group" data-toggle="buttons"> + <label {{bind-attr class=":btn :btn-sm usrR:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=usrR}} <span>Read</span> + </label> + <label {{bind-attr class=":btn :btn-sm usrW:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=usrW}} <span>Write</span> + </label> + <label {{bind-attr class=":btn :btn-sm usrE:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=usrE}} <span>Execute</span> + </label> + </div> + </div> + </div> + <div class="form-group"> + <label class="col-sm-2 control-label">Group</label> + <div class="col-sm-10"> + <div class="btn-group" data-toggle="buttons"> + <label {{bind-attr class=":btn :btn-sm grpR:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=grpR}} <span>Read</span> + </label> + <label {{bind-attr class=":btn :btn-sm grpW:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=grpW}} <span>Write</span> + </label> + <label {{bind-attr class=":btn :btn-sm grpE:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=grpE}} <span>Execute</span> + </label> + </div> + </div> + </div> + <div class="form-group"> + <label class="col-sm-2 control-label">Other</label> + <div class="col-sm-10"> + <div class="btn-group" data-toggle="buttons"> + <label {{bind-attr class=":btn :btn-sm otrR:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=otrR}} <span>Read</span> + </label> + <label {{bind-attr class=":btn :btn-sm otrW:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=otrW}} <span>Write</span> + </label> + <label {{bind-attr class=":btn :btn-sm otrE:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=otrE}} <span>Execute</span> + </label> + </div> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-2 col-sm-10"> + <div class="checkbox"> + <label> + {{input type="checkbox"}} <span> Modify recursively</span> + </label> + </div> + </div> + </div> + </form> + + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" {{action 'close' target="view"}}>Close</button> + <button type="button" class="btn btn-primary" {{action 'confirm' target="view"}}>Save changes</button> + </div> + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/templates/util/errorRow.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/util/errorRow.hbs b/contrib/views/files/src/main/resources/ui/app/templates/util/errorRow.hbs index 1a1e920..45264c8 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/util/errorRow.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/util/errorRow.hbs @@ -35,5 +35,3 @@ {{/if}} {{/if}} </div> - - http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs b/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs index 7f69041..24e0aeb 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/util/fileRow.hbs @@ -28,17 +28,11 @@ {{#rename-input fileBinding='content' confirm='rename' isRenaming=isRenaming}} <div class="file-name allow-open"> <span> - {{#if content.readAccess}} <a {{action 'open'}}> <strong> {{content.name}} </strong> </a> - {{else}} - <strong> - {{content.name}} - </strong> - {{/if}} </span> <span class="help-block mod-time allow-open"> <small class='allow-open'> @@ -63,18 +57,15 @@ <li> {{#if content.readAccess}} {{#if content.isDirectory}} - <a href="#" {{action 'download' 'zip'}} data-toggle="tooltip" data-placement="bottom" title="Download ZIP"><i class="fa fa-archive fa-fw fa-lg"></i></a> + <a href="#" {{action 'download' 'zip'}} data-toggle="tooltip" data-placement="bottom" title="Download zip"><i class="fa fa-archive fa-fw fa-lg"></i></a> {{else}} - <a href="#" {{action 'download' 'browse'}} data-toggle="tooltip" data-placement="bottom" title="Download File"><i class="fa fa-download fa-fw fa-lg"></i></a> + <a href="#" {{action 'download' 'browse'}} data-toggle="tooltip" data-placement="bottom" title="Download"><i class="fa fa-download fa-fw fa-lg"></i></a> {{/if}} {{/if}} </li> <li> - <a href="#" {{action 'moveFile' 'cut' this.content}} data-toggle="tooltip" data-placement="bottom" title="Move"><i class="fa fa-level-down fa-rotate-270 fa-fw fa-lg"></i></span></a> + <a href="#" {{action 'moveFile' 'cut' this.content}} data-toggle="tooltip" data-placement="bottom" title="Move"><i class="fa fa-level-down fa-rotate-270 fa-fw fa-lg"></i></a> </li> - {{!-- <li> - <a {{action 'rename' 'edit'}} data-toggle="tooltip" data-placement="bottom" title="Rename"><i class="fa fa-edit fa-lg"></i></a> - </li> --}} <li>{{popover-delete confirm="deleteFile"}}</li> </ul> {{/unless}} @@ -88,5 +79,3 @@ {{toggle-context}} </td> </tr> -{{chmod-input chVisible=chmodVisible file=content confirm="chmod"}} - http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/views/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/views/file.js b/contrib/views/files/src/main/resources/ui/app/views/file.js index 167f70f..2a74426 100644 --- a/contrib/views/files/src/main/resources/ui/app/views/file.js +++ b/contrib/views/files/src/main/resources/ui/app/views/file.js @@ -18,32 +18,12 @@ var App = require('app'); -App.FilesView = Em.View.extend({ - templateName: 'files', - didInsertElement:function () { - this.scheduleRebind(); - }, - scheduleRebind:function () { - Em.run.scheduleOnce('render', this, this.get('reBindTooltips')); - }, - reBindTooltips:function () { - this.$().tooltip({selector:'[data-toggle=tooltip]'}); - }, - renameInputView: Em.TextField.extend({ - controller:null, - didInsertElement:function (argument) { - var element = $(this.get('element')); - element.focus().val(this.value) - }, - keyUp: function(e) { - var target = this.get('targetObject'); - if (e.keyCode==13) { - return target.send('rename', 'confirm'); - }; +Em.CloakedView.reopen({ + classNames:['file-row'], + classNameBindings:['_containedView.controller.isMoving:isMoving'] +}); - if (e.keyCode==27) { - return target.send('rename', 'cancel'); - }; - } - }) +App.FileView = Em.View.extend({ + templateName: 'util/fileRow', + tagName:'tr' }); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/views/files.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/views/files.js b/contrib/views/files/src/main/resources/ui/app/views/files.js new file mode 100644 index 0000000..1db7f61 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/views/files.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +var App = require('app'); + +App.FilesView = Em.View.extend({ + templateName: 'files', + didInsertElement:function () { + this.scheduleRebind(); + }, + scheduleRebind:function () { + Em.run.scheduleOnce('render', this, this.get('reBindTooltips')); + }, + reBindTooltips:function () { + this.$().tooltip({selector:'[data-toggle=tooltip]'}); + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/app/views/modalChmod.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/views/modalChmod.js b/contrib/views/files/src/main/resources/ui/app/views/modalChmod.js new file mode 100644 index 0000000..5696539 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/views/modalChmod.js @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +var App = require('app'); + +App.ModalChmodView = Em.View.extend({ + actions:{ + confirm:function (file) { + this.get('controller.controllers.files').send('confirmChmod',this.get('controller.file')); + this.$('.chmodal').modal('hide'); + }, + close:function () { + var file = this.get('controller.file'); + var diff = file.changedAttributes(); + if (diff.permission) { + file.set('permission',diff.permission[0]); + } + this.$('.chmodal').modal('hide'); + } + }, + didInsertElement:function (argument) { + this.$('.btn-chmod').each(function () { + $(this).toggleClass('active',$(this).children('input').is(':checked')); + }); + + this.$('.chmodal').modal(); + this.$('.chmodal').on('hidden.bs.modal',function () { + this.get('controller.controllers.files').send('removeChmodModal'); + }.bind(this)); + }, + willClearRender:function () { + this.$('.chmodal').off('hidden.bs.modal'); + this.$('.chmodal').modal('hide'); + } +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/config.coffee ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/config.coffee b/contrib/views/files/src/main/resources/ui/config.coffee index b7bc79f..e37c0f2 100644 --- a/contrib/views/files/src/main/resources/ui/config.coffee +++ b/contrib/views/files/src/main/resources/ui/config.coffee @@ -27,7 +27,8 @@ exports.config = defaultExtension: 'js' joinTo: 'javascripts/app.js': /^app/ - 'javascripts/vendor.js': /^bower_components|vendor/ + 'javascripts/vendor.js': /^bower_components|vendor/, + 'javascripts/test.js': /^test(\/|\\)(?!vendor)/ stylesheets: defaultExtension: 'css' http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/package.json ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/package.json b/contrib/views/files/src/main/resources/ui/package.json index 8ff13e5..1796414 100644 --- a/contrib/views/files/src/main/resources/ui/package.json +++ b/contrib/views/files/src/main/resources/ui/package.json @@ -12,6 +12,7 @@ "node": "~0.6.10 || 0.8 || 0.9" }, "scripts": { + "test": "node_modules/phantomjs/bin/phantomjs runner.js public/tests.html", "start": "brunch watch --server", "preinstall": "chmod +x node/npm/bin/node-gyp-bin/node-gyp", "postinstall" : "bash node/with_new_path.sh node node_modules/.bin/bower --allow-root install" @@ -27,12 +28,15 @@ "ember-precompiler-brunch": ">= 1.5.0", "less-brunch": "^1.7.2" }, - "devDependencies": {}, + "devDependencies": { + "phantomjs": "^1.9.2", + "karma": "*", + "karma-qunit": "*", + "karma-phantomjs-launcher": "~0.1.2" + }, "ignore": [ "**/.*", "node_modules", - "bower_components", - "test", - "tests" + "bower_components" ] } http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/runner.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/runner.js b/contrib/views/files/src/main/resources/ui/runner.js new file mode 100644 index 0000000..4fd7894 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/runner.js @@ -0,0 +1,136 @@ +/* + * PhantomJS Runner QUnit Plugin (List Tests) 1.2.0 + * + * PhantomJS binaries: http://phantomjs.org/download.html + * Requires PhantomJS 1.6+ (1.7+ recommended) + * + * Run with: + * phantomjs runner-list.js [url-of-your-qunit-testsuite] + * + * e.g. + * phantomjs runner-list.js http://localhost/qunit/test/index.html + */ + +/*global phantom:false, require:false, console:false, window:false, QUnit:false */ + +(function() { + 'use strict'; + + var url, page, timeout, + args = require('system').args; + + // arg[0]: scriptName, args[1...]: arguments + if (args.length < 2 || args.length > 3) { + console.error('Usage:\n phantomjs runner-list.js [url-of-your-qunit-testsuite] [timeout-in-seconds]'); + phantom.exit(1); + } + + url = args[1]; + page = require('webpage').create(); + if (args[2] !== undefined) { + timeout = parseInt(args[2], 10); + } + + // Route `console.log()` calls from within the Page context to the main Phantom context (i.e. current `this`) + page.onConsoleMessage = function(msg) { + console.log(msg); + }; + + page.onInitialized = function() { + page.evaluate(addLogging); + }; + + page.onCallback = function(message) { + var result, + failed; + + if (message) { + if (message.name === 'QUnit.done') { + result = message.data; + failed = !result || !result.total || result.failed; + + if (!result.total) { + console.error('No tests were executed. Are you loading tests asynchronously?'); + } + + phantom.exit(failed ? 1 : 0); + } + } + }; + + page.open(url, function(status) { + if (status !== 'success') { + console.error('Unable to access network: ' + status); + phantom.exit(1); + } else { + // Cannot do this verification with the 'DOMContentLoaded' handler because it + // will be too late to attach it if a page does not have any script tags. + var qunitMissing = page.evaluate(function() { return (typeof QUnit === 'undefined' || !QUnit); }); + if (qunitMissing) { + console.error('The `QUnit` object is not present on this page.'); + phantom.exit(1); + } + + // Set a timeout on the test running, otherwise tests with async problems will hang forever + if (typeof timeout === 'number') { + setTimeout(function() { + console.error('The specified timeout of ' + timeout + ' seconds has expired. Aborting...'); + phantom.exit(1); + }, timeout * 1000); + } + + // Do nothing... the callback mechanism will handle everything! + } + }); + + function addLogging() { + window.document.addEventListener('DOMContentLoaded', function() { + var currentTestAssertions = []; + + QUnit.log(function(details) { + var response; + + console.log((details.result ? "? ": "? ") + details.message); + + if (!details.result) { + response = details.message || ''; + + if (typeof details.expected !== 'undefined') { + if (response) { + response += ', '; + } + + response += 'expected: ' + details.expected + ', but was: ' + details.actual; + } + + if (details.source) { + response += '\n' + details.source; + } + + console.log(' Failed assertion: ' + response); + } + }); + + QUnit.moduleStart(function( details ) { + if (details.name) { + console.log('\n' + details.name); + } + }); + + QUnit.testStart(function(result) { + console.log('\n' + result.name); + }); + + QUnit.done(function(result) { + console.log('\n' + 'Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.'); + + if (typeof window.callPhantom === 'function') { + window.callPhantom({ + 'name': 'QUnit.done', + 'data': result + }); + } + }); + }, false); + } +})(); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/test/spec.coffee ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/test/spec.coffee b/contrib/views/files/src/main/resources/ui/test/spec.coffee deleted file mode 100644 index ecf0374..0000000 --- a/contrib/views/files/src/main/resources/ui/test/spec.coffee +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 -# -# 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. - -# Write your [mocha](http://visionmedia.github.com/mocha/) specs here. http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/test/unit/controllers/files_test.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/test/unit/controllers/files_test.js b/contrib/views/files/src/main/resources/ui/test/unit/controllers/files_test.js new file mode 100644 index 0000000..516e4df --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/test/unit/controllers/files_test.js @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + + +moduleFor('controller:files', 'App.FilesController', { + needs:['controller:file'] +}); + +test('Property "isRootDir" is true when in root directory', function () { + + var cntr = this.subject(); + Ember.run(function() { + equal(cntr.get('path')==='/', cntr.get('isRootDir') , 'isRootDir is set right'); + }); + + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/contrib/views/files/src/main/resources/ui/vendor/js/ember-cloaking.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/vendor/js/ember-cloaking.js b/contrib/views/files/src/main/resources/ui/vendor/js/ember-cloaking.js new file mode 100644 index 0000000..235d1f6 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/vendor/js/ember-cloaking.js @@ -0,0 +1,436 @@ +(function () { + + /** + Display a list of cloaked items + + @class CloakedCollectionView + @extends Ember.CollectionView + @namespace Ember + **/ + Ember.CloakedCollectionView = Ember.CollectionView.extend({ + topVisible: null, + bottomVisible: null, + offsetFixedTopElement: null, + offsetFixedBottomElement: null, +// + + + init: function() { + var cloakView = this.get('cloakView'), + idProperty = this.get('idProperty'), + uncloakDefault = !!this.get('uncloakDefault'); + + // Set the slack ratio differently to allow for more or less slack in preloading + var slackRatio = parseFloat(this.get('slackRatio')); + if (!slackRatio) { this.set('slackRatio', 1.0); } + + this.set('itemViewClass', Ember.CloakedView.extend({ + classNames: [cloakView + '-cloak'], + cloaks: cloakView, + preservesContext: this.get('preservesContext') === "true", + cloaksController: this.get('itemController'), + defaultHeight: this.get('defaultHeight'), + + init: function() { + this._super(); + + if (idProperty) { + this.set('elementId', cloakView + '-cloak-' + this.get('content.' + idProperty)); + } + if (uncloakDefault) { + this.uncloak(); + } else { + this.cloak(); + } + } + })); + + this._super(); + Ember.run.next(this, 'scrolled'); + }, + + + /** + If the topmost visible view changed, we will notify the controller if it has an appropriate hook. + + @method _topVisibleChanged + @observes topVisible + **/ + _topVisibleChanged: Ember.observer('topVisible', function() { + var controller = this.get('controller'); + if (controller.topVisibleChanged) { controller.topVisibleChanged(this.get('topVisible')); } + }), + + /** + If the bottommost visible view changed, we will notify the controller if it has an appropriate hook. + + @method _bottomVisible + @observes bottomVisible + **/ + _bottomVisible: Ember.observer('bottomVisible', function() { + var controller = this.get('controller'); + if (controller.bottomVisibleChanged) { controller.bottomVisibleChanged(this.get('bottomVisible')); } + }), + + /** + Binary search for finding the topmost view on screen. + + @method findTopView + @param {Array} childViews the childViews to search through + @param {Number} windowTop The top of the viewport to search against + @param {Number} min The minimum index to search through of the child views + @param {Number} max The max index to search through of the child views + @returns {Number} the index into childViews of the topmost view + **/ + findTopView: function(childViews, viewportTop, min, max) { + if (max < min) { return min; } + + var wrapperTop = this.get('wrapperTop')>>0; + + while(max>min){ + var mid = Math.floor((min + max) / 2), + // in case of not full-window scrolling + $view = childViews[mid].$(), + viewBottom = $view.position().top + wrapperTop + $view.height(); + + if (viewBottom > viewportTop) { + max = mid-1; + } else { + min = mid+1; + } + } + + return min; + }, + + + /** + Determine what views are onscreen and cloak/uncloak them as necessary. + + @method scrolled + **/ + scrolled: function() { + if (!this.get('scrollingEnabled')) { return; } + + var childViews = this.get('childViews'); + if ((!childViews) || (childViews.length === 0)) { return; } + + var self = this, + toUncloak = [], + onscreen = [], + onscreenCloaks = [], + // calculating viewport edges + $w = Ember.$(window), + windowHeight = this.get('wrapperHeight') || ( window.innerHeight ? window.innerHeight : $w.height() ), + windowTop = this.get('wrapperTop') || $w.scrollTop(), + slack = Math.round(windowHeight * this.get('slackRatio')), + viewportTop = windowTop - slack, + windowBottom = windowTop + windowHeight, + viewportBottom = windowBottom + slack, + topView = this.findTopView(childViews, viewportTop, 0, childViews.length-1), + bodyHeight = this.get('wrapperHeight') ? this.$().height() : Ember.$('body').height(), + bottomView = topView, + offsetFixedTopElement = this.get('offsetFixedTopElement'), + offsetFixedBottomElement = this.get('offsetFixedBottomElement'); + + if (windowBottom > bodyHeight) { windowBottom = bodyHeight; } + if (viewportBottom > bodyHeight) { viewportBottom = bodyHeight; } + + if (offsetFixedTopElement) { + windowTop += (offsetFixedTopElement.outerHeight(true) || 0); + } + + if (offsetFixedBottomElement) { + windowBottom -= (offsetFixedBottomElement.outerHeight(true) || 0); + } + + // Find the bottom view and what's onscreen + while (bottomView < childViews.length) { + var view = childViews[bottomView], + $view = view.$(), + // in case of not full-window scrolling + scrollOffset = this.get('wrapperTop') || 0, + viewTop = $view.offset().top + scrollOffset, + viewBottom = viewTop + $view.height(); + + if (viewTop > viewportBottom) { break; } + toUncloak.push(view); + + if (viewBottom > windowTop && viewTop <= windowBottom) { + onscreen.push(view.get('content')); + onscreenCloaks.push(view); + } + + bottomView++; + } + if (bottomView >= childViews.length) { bottomView = childViews.length - 1; } + + // If our controller has a `sawObjects` method, pass the on screen objects to it. + var controller = this.get('controller'); + if (onscreen.length) { + this.setProperties({topVisible: onscreen[0], bottomVisible: onscreen[onscreen.length-1]}); + if (controller && controller.sawObjects) { + Em.run.schedule('afterRender', function() { + controller.sawObjects(onscreen); + }); + } + } else { + this.setProperties({topVisible: null, bottomVisible: null}); + } + + var toCloak = childViews.slice(0, topView).concat(childViews.slice(bottomView+1)); + + this._uncloak = toUncloak; + if(this._nextUncloak){ + Em.run.cancel(this._nextUncloak); + this._nextUncloak = null; + } + + Em.run.schedule('afterRender', this, function() { + onscreenCloaks.forEach(function (v) { + if(v && v.uncloak) { + v.uncloak(); + } + }); + toCloak.forEach(function (v) { v.cloak(); }); + if (self._nextUncloak) { Em.run.cancel(self._nextUncloak); } + self._nextUncloak = Em.run.later(self, self.uncloakQueue,50); + }); + + for (var j=bottomView; j<childViews.length; j++) { + var checkView = childViews[j]; + if (!checkView._containedView) { + if (!checkView.get('loading')) { + checkView.$().html(this.get('loadingHTML') || "Loading..."); + } + return; + } + } + + }, + + uncloakQueue: function(){ + var maxPerRun = 3, delay = 50, processed = 0, self = this; + + if(this._uncloak){ + while(processed < maxPerRun && this._uncloak.length>0){ + var view = this._uncloak.shift(); + if(view && view.uncloak && !view._containedView){ + Em.run.schedule('afterRender', view, view.uncloak); + processed++; + } + } + if(this._uncloak.length === 0){ + this._uncloak = null; + } else { + Em.run.schedule('afterRender', self, function(){ + if(self._nextUncloak){ + Em.run.cancel(self._nextUncloak); + } + self._nextUncloak = Em.run.next(self, function(){ + if(self._nextUncloak){ + Em.run.cancel(self._nextUncloak); + } + self._nextUncloak = Em.run.later(self,self.uncloakQueue,delay); + }); + }); + } + } + }, + + scrollTriggered: function() { + Em.run.scheduleOnce('afterRender', this, 'scrolled'); + }, + + _startEvents: Ember.on('didInsertElement', function() { + if (this.get('offsetFixed')) { + Em.warn("Cloaked-collection's `offsetFixed` is deprecated. Use `offsetFixedTop` instead."); + } + + var self = this, + offsetFixedTop = this.get('offsetFixedTop') || this.get('offsetFixed'), + offsetFixedBottom = this.get('offsetFixedBottom'), + onScrollMethod = function() { + Ember.run.debounce(self, 'scrollTriggered', 10); + }; + + if (offsetFixedTop) { + this.set('offsetFixedTopElement', Ember.$(offsetFixedTop)); + } + + if (offsetFixedBottom) { + this.set('offsetFixedBottomElement', Ember.$(offsetFixedBottom)); + } + + Ember.$(document).bind('touchmove.ember-cloak', onScrollMethod); + Ember.$(window).bind('scroll.ember-cloak', onScrollMethod); + this.addObserver('wrapperTop', self, onScrollMethod); + this.addObserver('wrapperHeight', self, onScrollMethod); + this.addObserver('content.@each', self, onScrollMethod); + this.scrollTriggered(); + + this.set('scrollingEnabled', true); + }), + + cleanUp: function() { + Ember.$(document).unbind('touchmove.ember-cloak'); + Ember.$(window).unbind('scroll.ember-cloak'); + this.set('scrollingEnabled', false); + }, + + _endEvents: Ember.on('willDestroyElement', function() { + this.cleanUp(); + }) + }); + + + /** + A cloaked view is one that removes its content when scrolled off the screen + + @class CloakedView + @extends Ember.View + @namespace Ember + **/ + Ember.CloakedView = Ember.View.extend({ + attributeBindings: ['style'], + + /** + Triggers the set up for rendering a view that is cloaked. + + @method uncloak + */ + uncloak: function() { + var state = this._state || this.state; + if (state !== 'inDOM' && state !== 'preRender') { return; } + + if (!this._containedView) { + var model = this.get('content'), + controller = null, + container = this.get('container'); + + // Wire up the itemController if necessary + var controllerName = this.get('cloaksController'); + if (controllerName) { + var controllerFullName = 'controller:' + controllerName, + factory = container.lookupFactory(controllerFullName), + parentController = this.get('controller'); + + // let ember generate controller if needed + if (factory === undefined) { + factory = Ember.generateControllerFactory(container, controllerName, model); + + // inform developer about typo + Ember.Logger.warn('ember-cloaking: can\'t lookup controller by name "' + controllerFullName + '".'); + Ember.Logger.warn('ember-cloaking: using ' + factory.toString() + '.'); + } + + controller = factory.create({ + model: model, + parentController: parentController, + target: parentController + }); + } + var createArgs = {}, + target = controller || model; + + if (this.get('preservesContext')) { + createArgs.content = target; + } else { + createArgs.context = target; + } + if (controller) { createArgs.controller = controller; } + this.setProperties({ + style: null, + loading: false + }); + + this._containedView = this.createChildView(this.get('cloaks'), createArgs); + this.rerender(); + } + }, + + /** + Removes the view from the DOM and tears down all observers. + + @method cloak + */ + cloak: function() { + var self = this; + + if (this._containedView && (this._state || this.state) === 'inDOM') { + var style = 'height: ' + this.$().height() + 'px;'; + this.set('style', style); + this.$().prop('style', style); + + // We need to remove the container after the height of the element has taken + // effect. + Ember.run.schedule('afterRender', function() { + if(self._containedView){ + self._containedView.remove(); + self._containedView = null; + } + }); + } + }, + + _removeContainedView: Ember.on('willDestroyElement', function(){ + if(this._containedView){ + this._containedView.remove(); + this._containedView = null; + } + this._super(); + }), + + _setHeights: Ember.on('didInsertElement', function(){ + if (!this._containedView) { + // setting default height + // but do not touch if height already defined + if(!this.$().height()){ + var defaultHeight = 100; + if(this.get('defaultHeight')) { + defaultHeight = this.get('defaultHeight'); + } + + this.$().css('height', defaultHeight); + } + } + }), + + /** + Render the cloaked view if applicable. + + @method render + */ + render: function(buffer) { + var containedView = this._containedView, self = this; + + if (containedView && (containedView._state || containedView.state) !== 'inDOM') { + containedView.triggerRecursively('willInsertElement'); + containedView.renderToBuffer(buffer); + containedView._transitionTo('inDOM'); + Em.run.schedule('afterRender', function() { + if(self._containedView) { + self._containedView.triggerRecursively('didInsertElement'); + } + }); + } + } + + }); + + + + Ember.Handlebars.registerHelper('cloaked-collection', function(options) { + var hash = options.hash, + types = options.hashTypes; + + for (var prop in hash) { + if (types[prop] === 'ID') { + hash[prop + 'Binding'] = hash[prop]; + delete hash[prop]; + } + } + return Ember.Handlebars.helpers.view.call(this, Ember.CloakedCollectionView, options); + }); + +})(); http://git-wip-us.apache.org/repos/asf/ambari/blob/2c33f876/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 5d55074..af4fc01 100644 --- a/pom.xml +++ b/pom.xml @@ -223,6 +223,10 @@ <exclude>contrib/views/files/src/main/resources/ui/node/**</exclude> <exclude>contrib/views/files/src/main/resources/ui/node_modules/**</exclude> <exclude>contrib/views/files/src/main/resources/ui/public/**</exclude> + <exclude>contrib/views/files/src/main/resources/ui/vendor/**</exclude> + <exclude>contrib/views/files/src/main/resources/ui/runner.js</exclude> + <exclude>contrib/views/files/src/main/resources/ui/app/assets/stylesheets/**</exclude> + <exclude>contrib/views/files/src/main/resources/ui/app/assets/javascripts/**</exclude> <exclude>contrib/views/jobs/src/main/resources/ui/.tmp/**</exclude> <exclude>contrib/views/jobs/src/main/resources/ui/**/bower_components/**</exclude> <exclude>contrib/views/jobs/src/main/resources/ui/dist/**</exclude>
