summaryrefslogtreecommitdiff
path: root/StoneIsland/www/js/lib/view
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2015-11-25 04:08:39 -0500
committerJules Laplace <jules@okfoc.us>2015-11-25 04:08:39 -0500
commitf0c551933c5e725b980014b559d757bee99d0536 (patch)
treeba8f090c42934f3ef601eb8d0e42b4398242fc68 /StoneIsland/www/js/lib/view
parente024c35a2584f5d975b3b4f8fe942bb8e35b6e47 (diff)
fix auth bugs pt 1
Diffstat (limited to 'StoneIsland/www/js/lib/view')
-rw-r--r--StoneIsland/www/js/lib/view/Router.js75
-rw-r--r--StoneIsland/www/js/lib/view/Scrollable.js20
-rw-r--r--StoneIsland/www/js/lib/view/Serializable.js129
-rw-r--r--StoneIsland/www/js/lib/view/View.js147
4 files changed, 371 insertions, 0 deletions
diff --git a/StoneIsland/www/js/lib/view/Router.js b/StoneIsland/www/js/lib/view/Router.js
new file mode 100644
index 00000000..a8ec331f
--- /dev/null
+++ b/StoneIsland/www/js/lib/view/Router.js
@@ -0,0 +1,75 @@
+var Router = View.extend({
+
+ routeByHash: false,
+
+ go: function(url){
+ this.parseRoute(url)
+ },
+
+ route: function(){
+ var path = this.routeByHash ? window.location.hash.substr(0) : window.location.pathname
+ path = path || "/"
+ this.originalPath = path
+ this.parseRoute(path)
+ },
+
+ parseRoute: function(pathname){
+
+ pathname = pathname.replace(/^#/, "")
+
+ if (pathname[0] !== "/") { pathname = "/" + pathname }
+
+ var routes = this.routes,
+ path = pathname.split("/");
+
+ for (var i = 0; i < path.length; i++) {
+ if (! path[i].length) {
+ path[i] = null
+ }
+ }
+
+ if (pathname in routes) {
+ this[this.routes[pathname]]()
+ return
+ }
+
+ if (path[path.length-1] == null) {
+ path.pop()
+ }
+
+ for (var route in routes) {
+ var routePath = route.split("/")
+ if (routePath[1] == path[1]) {
+ if (routePath[2] && routePath[2].indexOf(":") !== -1 && path[2] && (path[3] === routePath[3]) ) {
+ this[this.routes[route]](path[2])
+ return
+ }
+ else if (routePath[2] == path[2]) {
+ if (routePath[3] && path[3]) {
+ if (routePath[3].indexOf(":") !== -1) {
+ this[this.routes[route]](path[3])
+ return
+ }
+ else if (routePath[3] == path[3]) {
+ this[this.routes[route]]()
+ return
+ }
+ }
+ else if (! routePath[3] && ! path[3]) {
+ this[this.routes[route]]()
+ return
+ }
+ }
+ else if (! routePath[2] && (! path[2].length || ! path[2])) {
+ this[this.routes[route]]()
+ return
+ }
+ }
+ }
+
+ if (is_mobile) {
+ window.location.href = "/"
+ }
+ }
+
+})
diff --git a/StoneIsland/www/js/lib/view/Scrollable.js b/StoneIsland/www/js/lib/view/Scrollable.js
new file mode 100644
index 00000000..7cd96f89
--- /dev/null
+++ b/StoneIsland/www/js/lib/view/Scrollable.js
@@ -0,0 +1,20 @@
+var ScrollableView = View.extend({
+
+ events: {
+ "load img": "deferScrollToTop",
+ },
+
+ deferScrollToTop: function(){
+ setTimeout(this.scrollToTop.bind(this), 0)
+ },
+
+ refreshScroller: function(){
+ this.scroller.refresh()
+ },
+
+ scrollToTop: function(){
+ this.scroller.refresh()
+ app.collection.scroller.scrollTo(0, 0)
+ },
+
+}) \ No newline at end of file
diff --git a/StoneIsland/www/js/lib/view/Serializable.js b/StoneIsland/www/js/lib/view/Serializable.js
new file mode 100644
index 00000000..e9459229
--- /dev/null
+++ b/StoneIsland/www/js/lib/view/Serializable.js
@@ -0,0 +1,129 @@
+var SerializableView = View.extend({
+
+ events: {
+ "change select": "update_select",
+ "focus input": "focus_input",
+ },
+
+ preload: function(data){
+ if (! data && sdk.env == "production") { return }
+ data = data || this.test_data
+ if (! data) { return }
+
+ Object.keys(data).forEach(function(key){
+ var value = data[key]
+ var $el = this.$("[name=" + key + "]")
+
+ if ($el.attr("type") == "checkbox") {
+ $el.prop("checked", value)
+ }
+ else if ($el.prop("tagName") == "SELECT") {
+ $el.val( value )
+ this.update_select({ currentTarget: $el })
+ }
+ else {
+ $el.val( value )
+ }
+ }.bind(this))
+ },
+
+ serialize: function(){
+ var fields = {}
+ this.$("input[name], select[name], textarea[name]").each( function(){
+ if (this.type == "checkbox") {
+ if ($(this).prop("checked")) {
+ fields[this.name] = this.value
+ }
+ }
+ else {
+ fields[this.name] = this.value
+ }
+ })
+ return fields
+ },
+
+ deserialize: function(data){
+ this.$("input[name], textarea[name]").val("")
+ Object.keys(data).forEach(function(k){
+ this.$("[" + k + "]").val(data[k])
+ })
+ },
+
+ focus_input: function(e){
+ $(e.currentTarget).removeClass("error_hilite")
+ },
+
+ update_select: function(e){
+ var $target = $(e.currentTarget), value = $target.val()
+ var label = $($("select")[0]).find("option").filter(function(){ return this.value === value }).html()
+
+ $target.parent().addClass("picked")
+ $target.parent().find("span").html(label)
+ },
+
+ validate: function(errors){
+ var data = this.serialize()
+ var errors = []
+ var presence_msgs = this.validate_presence
+ Object.keys(presence_msgs).forEach(function(k){
+ if (! data[k]) errors.push( [ k, presence_msgs[k] ] )
+ })
+ this.validate_fields && this.validate_fields(data, errors)
+ this.cc && this.cc.validate(errors)
+ this.address && this.address.validate(errors)
+ return { errors: errors, data: data }
+ },
+
+ show_errors: function(errors){
+ var msgs = []
+ errors.forEach(function(e, i){
+ if (i > 0) { return }
+ this.$("[name=" + e[0] + "]").addClass('error_hilite')
+ msgs.push(e[1])
+ }.bind(this))
+ this.$msg.html(msgs.join("<br>"))
+ this.$msg.addClass('alert-notice')
+ },
+
+ hide_errors: function(){
+ this.$msg.removeClass('alert-notice')
+ this.$msg.html("")
+ },
+
+ save: function(e){
+ e && e.preventDefault()
+
+ var valid = this.validate()
+ if (valid.errors.length) {
+ this.show_errors(valid.errors)
+ return
+ }
+ else {
+ this.hide_errors()
+ }
+
+ app.curtain.show("loading")
+ this.action({
+ data: valid.data,
+ success: function(data){
+ app.curtain.hide("loading")
+ this.success(data)
+ }.bind(this),
+ error: function(data){
+ app.curtain.hide("loading")
+ this.error(data)
+ }.bind(this),
+ })
+ },
+
+ success: function(data){
+ console.log("SUCCESS", data)
+ },
+
+ error: function(data){
+ console.log("FAIL", data)
+ },
+
+})
+
+var FormView = View.extend(SerializableView.prototype).extend(ScrollableView.prototype)
diff --git a/StoneIsland/www/js/lib/view/View.js b/StoneIsland/www/js/lib/view/View.js
new file mode 100644
index 00000000..41638ab7
--- /dev/null
+++ b/StoneIsland/www/js/lib/view/View.js
@@ -0,0 +1,147 @@
+var View = (function($, _){
+
+ var View = function(options) {
+ this._id = _.uniqueId('view')
+ this.type = "view"
+ options || (options = {});
+ _.extend(this, _.pick(options, viewOptions))
+ this._ensureElement()
+ this.initialize.apply(this, arguments)
+ this.delegateEvents()
+ }
+
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+
+ _.extend(View.prototype, {
+
+ // The default `tagName` of a View's element is `"div"`.
+ tagName: 'div',
+
+ $: function(selector) {
+ return this.$el.find(selector);
+ },
+
+ initialize: function(){},
+
+ setElement: function(element, delegate) {
+ if (this.$el) this.undelegateEvents();
+ this.$el = element instanceof $ ? element : $(element);
+ this.el = this.$el[0];
+ if (delegate !== false) this.delegateEvents();
+ return this;
+ },
+
+ // Set callbacks, where `this.events` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save',
+ // 'click .open': function(e) { ... }
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ // This only works for delegate-able events: not `focus`, `blur`, and
+ // not `change`, `submit`, and `reset` in Internet Explorer.
+ delegateEvents: function(events) {
+ if (!(events || (events = _.result(this, 'events')))) return this;
+ this.undelegateEvents();
+ for (var key in events) {
+ var method = events[key];
+ if (!_.isFunction(method)) method = this[events[key]];
+ if (!method) continue;
+
+ var match = key.match(delegateEventSplitter);
+ var eventName = match[1], selector = match[2];
+ method = _.bind(method, this);
+ eventName += '.delegateEvents' + this._id;
+ if (is_mobile) {
+ if (eventName === 'mouseenter' || eventName === 'mouseleave') {
+ continue
+ }
+// if (eventName === 'click') {
+// eventName = 'tap'
+// }
+ }
+ if (selector === '') {
+ this.$el.on(eventName, method);
+ } else {
+ this.$el.on(eventName, selector, method);
+ }
+ }
+ return this;
+ },
+
+ // Clears all callbacks previously bound to the view with `delegateEvents`.
+ undelegateEvents: function() {
+ this.$el.off('.delegateEvents' + this._id);
+ return this;
+ },
+
+ // Ensure that the View has a DOM element to render into.
+ // If `this.el` is a string, pass it through `$()`, take the first
+ // matching element, and re-assign it to `el`. Otherwise, create
+ // an element from the `id`, `className` and `tagName` properties.
+ _ensureElement: function() {
+ this.setElement(_.result(this, 'el'), false);
+ },
+
+ preventDefault: function(e){
+ e && e.preventDefault()
+ },
+
+ stopPropagation: function(e){
+ e && e.stopPropagation()
+ },
+
+ });
+
+
+ var extend = function(protoProps, staticProps) {
+ var staticProps = staticProps || {}
+ var parent = this;
+ var child;
+ var childEvents = {};
+
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call the parent's constructor.
+ if (protoProps && _.has(protoProps, 'constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
+
+ // Extend events so we can subclass views
+ _.extend(childEvents, parent.prototype.events, protoProps.events)
+
+ // Add static properties to the constructor function, if supplied.
+ _.extend(child, parent, staticProps);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ var Surrogate = function(){ this.constructor = child; };
+ Surrogate.prototype = parent.prototype;
+ child.prototype = new Surrogate;
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
+
+ // Set a convenience property in case the parent's prototype is needed
+ // later.
+ child.prototype.__super__ = parent.prototype;
+ child.prototype.events = childEvents
+
+ return child;
+ };
+
+ View.extend = extend;
+
+ return View;
+})(jQuery, _)