first file moving
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/3150bb11 Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/3150bb11 Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/3150bb11 Branch: refs/heads/fauxton-file-reorder Commit: 3150bb1164675be6dd4817bd24ee77983b1a7fa7 Parents: d121e09 Author: Garren Smith <[email protected]> Authored: Wed Jan 8 15:26:12 2014 +0200 Committer: Garren Smith <[email protected]> Committed: Mon Jan 27 08:41:27 2014 +0200 ---------------------------------------------------------------------- src/fauxton/app/addons/fauxton/components.js | 15 +- src/fauxton/app/core/api.js | 520 ++++++++++++++++++++++ src/fauxton/app/core/base.js | 52 +++ 3 files changed, 585 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb/blob/3150bb11/src/fauxton/app/addons/fauxton/components.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js index cda61c5..bc011e3 100644 --- a/src/fauxton/app/addons/fauxton/components.js +++ b/src/fauxton/app/addons/fauxton/components.js @@ -297,7 +297,7 @@ function(app, FauxtonAPI, ace) { var annotations = editor.getSession().getAnnotations(); var newAnnotations = _.reduce(annotations, function (annotations, error) { - if (!FauxtonAPI.isIgnorableError(error.raw)) { + if (!this.isIgnorableError(error.raw)) { annotations.push(error); } return annotations; @@ -330,8 +330,19 @@ function(app, FauxtonAPI, ace) { var errors = this.getAnnotations(); // By default CouchDB view functions don't pass lint return _.every(errors, function(error) { - return FauxtonAPI.isIgnorableError(error.raw); + return this.isIgnorableError(error.raw); },this); + }, + + // List of JSHINT errors to ignore + // Gets around problem of anonymous functions not being a valid statement + excludedViewErrors: [ + "Missing name in function declaration.", + "['{a}'] is better written in dot notation." + ], + + isIgnorableError: function(msg) { + return _.contains(this.excludedViewErrors, msg); } }); http://git-wip-us.apache.org/repos/asf/couchdb/blob/3150bb11/src/fauxton/app/core/api.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/core/api.js b/src/fauxton/app/core/api.js new file mode 100644 index 0000000..0e4e730 --- /dev/null +++ b/src/fauxton/app/core/api.js @@ -0,0 +1,520 @@ +// Licensed 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. + +define([ + "app", + "core/base", + // Modules + "modules/fauxton/base", + "spin" +], + +function(app, FauxtonAPI, Fauxton) { + + FauxtonAPI.moduleExtensions = { + Routes: { + } + }; + + FauxtonAPI.addonExtensions = { + initialize: function() {} + }; + + FauxtonAPI.module = function(extra) { + return app.module(_.extend(FauxtonAPI.moduleExtensions, extra)); + }; + + FauxtonAPI.addon = function(extra) { + return FauxtonAPI.module(FauxtonAPI.addonExtensions, extra); + }; + + FauxtonAPI.navigate = function(url, _opts) { + var options = _.extend({trigger: true}, _opts ); + app.router.navigate(url,options); + }; + + FauxtonAPI.beforeUnload = function () { + app.router.beforeUnload.apply(app.router, arguments); + }; + + FauxtonAPI.removeBeforeUnload = function () { + app.router.removeBeforeUnload.apply(app.router, arguments); + }; + + FauxtonAPI.addHeaderLink = function(link) { + app.masterLayout.navBar.addLink(link); + }; + + FauxtonAPI.removeHeaderLink = function(link) { + app.masterLayout.navBar.removeLink(link); + }; + + FauxtonAPI.addRoute = function(route) { + app.router.route(route.route, route.name, route.callback); + }; + + FauxtonAPI.triggerRouteEvent = function (routeEvent, args) { + app.router.triggerRouteEvent("route:"+routeEvent, args); + }; + + FauxtonAPI.addNotification = function(options) { + options = _.extend({ + msg: "Notification Event Triggered!", + type: "info", + selector: "#global-notifications" + }, options); + var view = new Fauxton.Notification(options); + + return view.renderNotification(); + }; + + FauxtonAPI.UUID = FauxtonAPI.Model.extend({ + initialize: function(options) { + options = _.extend({count: 1}, options); + this.count = options.count; + }, + + url: function() { + return app.host + "/_uuids?count=" + this.count; + }, + + next: function() { + return this.get("uuids").pop(); + } + }); + + FauxtonAPI.Session = Backbone.Model.extend({ + url: '/_session', + + user: function () { + var userCtx = this.get('userCtx'); + + if (!userCtx || !userCtx.name) { return null; } + + return { + name: userCtx.name, + roles: userCtx.roles + }; + }, + + fetchOnce: function (opt) { + var options = _.extend({}, opt); + + if (!this._deferred || this._deferred.state() === "rejected" || options.forceFetch ) { + this._deferred = this.fetch(); + } + + return this._deferred; + }, + + fetchUser: function (opt) { + var that = this, + currentUser = this.user(); + + return this.fetchOnce(opt).then(function () { + var user = that.user(); + + // Notify anyone listening on these events that either a user has changed + // or current user is the same + if (currentUser !== user) { + that.trigger('session:userChanged'); + } else { + that.trigger('session:userFetched'); + } + + // this will return the user as a value to all function that calls done on this + // eg. session.fetchUser().done(user) { .. do something with user ..} + return user; + }); + } + }); + + FauxtonAPI.setSession = function (newSession) { + app.session = FauxtonAPI.session = newSession; + return FauxtonAPI.session.fetchUser(); + }; + + FauxtonAPI.setSession(new FauxtonAPI.Session()); + + // This is not exposed externally as it should not need to be accessed or overridden + var Auth = function (options) { + this._options = options; + this.initialize.apply(this, arguments); + }; + + // Piggy-back on Backbone's self-propagating extend function, + Auth.extend = Backbone.Model.extend; + + _.extend(Auth.prototype, Backbone.Events, { + authDeniedCb: function() {}, + + initialize: function() { + var that = this; + }, + + authHandlerCb : function (roles) { + var deferred = $.Deferred(); + deferred.resolve(); + return deferred; + }, + + registerAuth: function (authHandlerCb) { + this.authHandlerCb = authHandlerCb; + }, + + registerAuthDenied: function (authDeniedCb) { + this.authDeniedCb = authDeniedCb; + }, + + checkAccess: function (roles) { + var requiredRoles = roles || [], + that = this; + + return FauxtonAPI.session.fetchUser().then(function (user) { + return FauxtonAPI.when(that.authHandlerCb(FauxtonAPI.session, requiredRoles)); + }); + } + }); + + FauxtonAPI.auth = new Auth(); + + FauxtonAPI.RouteObject = function(options) { + this._options = options; + + this._configure(options || {}); + this.initialize.apply(this, arguments); + this.addEvents(); + }; + + var broadcaster = {}; + _.extend(broadcaster, Backbone.Events); + + FauxtonAPI.RouteObject.on = function (eventName, fn) { + broadcaster.on(eventName, fn); + }; + + /* How Route Object events work + To listen to a specific route objects events: + + myRouteObject = FauxtonAPI.RouteObject.extend({ + events: { + "beforeRender": "beforeRenderEvent" + }, + + beforeRenderEvent: function (view, selector) { + console.log('Hey, beforeRenderEvent triggered',arguments); + }, + }); + + It is also possible to listen to events triggered from all Routeobjects. + This is great for more general things like adding loaders, hooks. + + FauxtonAPI.RouteObject.on('beforeRender', function (routeObject, view, selector) { + console.log('hey, this will trigger when any routeobject renders a view'); + }); + + Current Events to subscribe to: + * beforeFullRender -- before a full render is being done + * beforeEstablish -- before the routeobject calls establish + * AfterEstablish -- after the routeobject has run establish + * beforeRender -- before a view is rendered + * afterRender -- a view is finished being rendered + * renderComplete -- all rendering is complete + + */ + + // Piggy-back on Backbone's self-propagating extend function + FauxtonAPI.RouteObject.extend = Backbone.Model.extend; + + var routeObjectOptions = ["views", "routes", "events", "roles", "crumbs", "layout", "apiUrl", "establish"]; + + _.extend(FauxtonAPI.RouteObject.prototype, Backbone.Events, { + // Should these be default vals or empty funcs? + views: {}, + routes: {}, + events: {}, + crumbs: [], + layout: "with_sidebar", + apiUrl: null, + disableLoader: false, + loaderClassname: 'loader', + renderedState: false, + establish: function() {}, + route: function() {}, + roles: [], + initialize: function() {} + }, { + + renderWith: function(route, masterLayout, args) { + var routeObject = this, + triggerBroadcast = _.bind(this.triggerBroadcast, this); + + // Only want to redo the template if its a full render + if (!this.renderedState) { + masterLayout.setTemplate(this.layout); + triggerBroadcast('beforeFullRender'); + $('#primary-navbar li').removeClass('active'); + + if (this.selectedHeader) { + app.selectedHeader = this.selectedHeader; + $('#primary-navbar li[data-nav-name="' + this.selectedHeader + '"]').addClass('active'); + } + } + + masterLayout.clearBreadcrumbs(); + var crumbs = this.get('crumbs'); + + if (crumbs.length) { + masterLayout.setBreadcrumbs(new Fauxton.Breadcrumbs({ + crumbs: crumbs + })); + } + + triggerBroadcast('beforeEstablish'); + FauxtonAPI.when(this.establish()).then(function(resp) { + triggerBroadcast('afterEstablish'); + _.each(routeObject.getViews(), function(view, selector) { + if(view.hasRendered) { + triggerBroadcast('viewHasRendered', view, selector); + return; + } + + triggerBroadcast('beforeRender', view, selector); + FauxtonAPI.when(view.establish()).then(function(resp) { + masterLayout.setView(selector, view); + + masterLayout.renderView(selector); + triggerBroadcast('afterRender', view, selector); + }, function(resp) { + view.establishError = { + error: true, + reason: resp + }; + + if (resp) { + var errorText = JSON.parse(resp.responseText).reason; + FauxtonAPI.addNotification({ + msg: 'An Error occurred: ' + errorText, + type: 'error', + clear: true + }); + } + + masterLayout.renderView(selector); + }); + + }); + }.bind(this), function (resp) { + if (!resp) { return; } + FauxtonAPI.addNotification({ + msg: 'An Error occurred' + JSON.parse(resp.responseText).reason, + type: 'error', + clear: true + }); + }); + + if (this.get('apiUrl')){ + masterLayout.apiBar.update(this.get('apiUrl')); + } else { + masterLayout.apiBar.hide(); + } + + // Track that we've done a full initial render + this.renderedState = true; + triggerBroadcast('renderComplete'); + }, + + triggerBroadcast: function (eventName) { + var args = Array.prototype.slice.call(arguments); + this.trigger.apply(this, args); + + args.splice(0,1, eventName, this); + broadcaster.trigger.apply(broadcaster, args); + }, + + get: function(key) { + return _.isFunction(this[key]) ? this[key]() : this[key]; + }, + + addEvents: function(events) { + events = events || this.get('events'); + _.each(events, function(method, event) { + if (!_.isFunction(method) && !_.isFunction(this[method])) { + throw new Error("Invalid method: "+method); + } + method = _.isFunction(method) ? method : this[method]; + + this.on(event, method); + }, this); + }, + + _configure: function(options) { + _.each(_.intersection(_.keys(options), routeObjectOptions), function(key) { + this[key] = options[key]; + }, this); + }, + + getView: function(selector) { + return this.views[selector]; + }, + + setView: function(selector, view) { + this.views[selector] = view; + return view; + }, + + getViews: function() { + return this.views; + }, + + removeViews: function () { + _.each(this.views, function (view, selector) { + view.remove(); + delete this.views[selector]; + }, this); + }, + + getRouteUrls: function () { + return _.keys(this.get('routes')); + }, + + hasRoute: function (route) { + if (this.get('routes')[route]) { + return true; + } + return false; + }, + + routeCallback: function (route, args) { + var routes = this.get('routes'), + routeObj = routes[route], + routeCallback; + + if (typeof routeObj === 'object') { + routeCallback = this[routeObj.route]; + } else { + routeCallback = this[routeObj]; + } + + routeCallback.apply(this, args); + }, + + getRouteRoles: function (routeUrl) { + var route = this.get('routes')[routeUrl]; + + if ((typeof route === 'object') && route.roles) { + return route.roles; + } + + return this.roles; + } + + }); + + // We could look at moving the spinner code out to its own module + var routeObjectSpinner; + FauxtonAPI.RouteObject.on('beforeEstablish', function (routeObject) { + if (!routeObject.disableLoader){ + var opts = { + lines: 16, // The number of lines to draw + length: 8, // The length of each line + width: 4, // The line thickness + radius: 12, // The radius of the inner circle + color: '#333', // #rbg or #rrggbb + speed: 1, // Rounds per second + trail: 10, // Afterglow percentage + shadow: false // Whether to render a shadow + }; + + if (!$('.spinner').length) { + $('<div class="spinner"></div>') + .appendTo('#app-container'); + } + + routeObjectSpinner = new Spinner(opts).spin(); + $('.spinner').append(routeObjectSpinner.el); + } + }); + + var removeRouteObjectSpinner = function () { + if (routeObjectSpinner) { + routeObjectSpinner.stop(); + $('.spinner').remove(); + } + }; + + var removeViewSpinner = function () { + if (viewSpinner){ + viewSpinner.stop(); + $('.spinner').remove(); + } + }; + + var viewSpinner; + FauxtonAPI.RouteObject.on('beforeRender', function (routeObject, view, selector) { + removeRouteObjectSpinner(); + + if (!view.disableLoader){ + var opts = { + lines: 16, // The number of lines to draw + length: 8, // The length of each line + width: 4, // The line thickness + radius: 12, // The radius of the inner circle + color: '#333', // #rbg or #rrggbb + speed: 1, // Rounds per second + trail: 10, // Afterglow percentage + shadow: false // Whether to render a shadow + }; + + viewSpinner = new Spinner(opts).spin(); + $('<div class="spinner"></div>') + .appendTo(selector) + .append(viewSpinner.el); + } + }); + + FauxtonAPI.RouteObject.on('afterRender', function (routeObject, view, selector) { + removeViewSpinner(); + }); + + FauxtonAPI.RouteObject.on('viewHasRendered', function () { + removeViewSpinner(); + removeRouteObjectSpinner(); + }); + + var extensions = _.extend({}, Backbone.Events); + // Can look at a remove function later. + FauxtonAPI.registerExtension = function (name, view) { + if (!extensions[name]) { + extensions[name] = []; + } + + extensions.trigger('add:' + name, view); + extensions[name].push(view); + }; + + FauxtonAPI.getExtensions = function (name) { + var views = extensions[name]; + + if (!views) { + views = []; + } + + return views; + }; + + FauxtonAPI.extensions = extensions; + + app.fauxtonAPI = FauxtonAPI; + return app.fauxtonAPI; +}); + http://git-wip-us.apache.org/repos/asf/couchdb/blob/3150bb11/src/fauxton/app/core/base.js ---------------------------------------------------------------------- diff --git a/src/fauxton/app/core/base.js b/src/fauxton/app/core/base.js new file mode 100644 index 0000000..20dea73 --- /dev/null +++ b/src/fauxton/app/core/base.js @@ -0,0 +1,52 @@ +// Licensed 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. + +define([ +], + +function() { + var FauxtonAPI = {}; + + FauxtonAPI.Deferred = function() { + return $.Deferred(); + }; + + FauxtonAPI.when = function (deferreds) { + if (deferreds instanceof Array) { + return $.when.apply(null, deferreds); + } + + return $.when(deferreds); + }; + + FauxtonAPI.View = Backbone.View.extend({ + // This should return an array of promises, an empty array, or null + establish: function() { + return null; + }, + + loaderClassname: 'loader', + + disableLoader: false, + + forceRender: function () { + this.hasRendered = false; + } + }); + + FauxtonAPI.Model = Backbone.Model.extend({ + //add fetchOnce + }); + + return FauxtonAPI; +}); +
