Example #1
0
var getView = function (opts) {
    var formOpts;
    opts = opts || {};
    formOpts = opts.form || {};
    var FormView = AmpersandFormView.extend({
        fields: function () {
            return [
                new AmpersandCheckboxView({
                    name: 'check',
                    value: true
                }),
                new AmpersandInputView({
                    name: 'text',
                    type: 'text',
                    value: 'Original value'
                }),
                new AmpersandInputView({
                    name: 'textarea',
                    type: 'textarea',
                    value: 'Original value'
                })
            ];
        }
    });

    // Create a View with a nested FormView.
    var View = AmpersandView.extend({
        template: '<form data-hook="test-form"></form>',
        render: function () {
            this.renderWithTemplate();
            this.form = new FormView({
                autoRender: formOpts.autoRender,
                el: this.queryByHook('test-form'),
                model: this.model,
                values: {
                    text: 'Overriden value'
                }
            });
            this.registerSubview(this.form);
            return this;
        }
    });

    var view = new View({
        model: new Model()
    });

    return view.render();
};
 _setDefaultSubviews: function () {
   
   var DefaultView = View.extend({
     template: '<div></div>'
   });
   
   // Ensure subviews
   this.subviews = _.extend({}, this.subviews);
   
   // Defaults for drawer
   this.subviews.drawer = _.extend({
     view: new DefaultView(),
     width: 256,
     animationSpeed: 0
   }, this.subviews.drawer);
   
   // Defaults for main
   this.subviews.main = _.extend({
     view: new DefaultView(),
   }, this.subviews.main);
 },
Example #3
0
'use strict';

var AmpersandView = require('ampersand-view');

module.exports = AmpersandView.extend({
    pageTitle: function () {
        return 'Ampersand.js // Gifalicious // Gif #' + this.model.id;
    },
    template: require('../templates/gif.html'),
    bindings: {
        'model.src': [
            {
                type: 'attribute',
                hook: 'src',
                name: 'src'
            },
            {
                type: 'value',
                hook: 'show-url-value'
            }
        ]
    }
});
  options || (options = {});
  var self = this;
  var gap = (options.gap || 20); // defaults to 20

  self.events = self.events || {};
  assign(self.events, {scroll: 'infiniteScroll'});

  self.infiniteScroll = function () {
    if (this.el.scrollHeight - this.el.scrollTop <= this.el.clientHeight + gap) {
      this.collection.fetchPage(options);
    }
  };
}

// Correctly setup the prototype chain
var BaseView = View.extend();
InfiniteScrollView.prototype = Object.create(BaseView.prototype);
InfiniteScrollView.extend = BaseView.extend;

/**
 * Infinite Scroll View constructor
 *
 * @param {Object}  options - the valid options according to the `ampersand-view`
 *                            and this module
 */
function InfiniteScrollView (options) {
  options || (options = {});
  BaseView.call(this, options);
  InfiniteScrollSetup.call(this, options);
}
module.exports = View.extend({
  
  autoRender: true,
  
  props: {
    template: ['string', true, defaultTemplate()],
    responsiveWidth: ['number', false, 640],
    forceNarrow: ['boolean', false, false],
    rightDrawer: ['boolean', false, false],
    withHeader: ['boolean', false, false], // TODO: test this next!
    defaultNarrowClass: ['string', false , 'narrow'],
    
    // Flags
    narrow: ['boolean', false, false],
    selected: ['string', false, 'main'],
    
    // Elements
    main: 'any',
    drawer: 'any',
    
    subviews: 'object'
  },
  
  events: {
    'click [data-hook=adv-drawer]': '_drawerClicked'
  },
  
  initialize: function () {
    
    this._setDefaultSubviews();
    
    var resizeHandler = _.bind(this._handleWindowResize, this);
    var closeDrawer = _.bind(this.closeDrawer, this);
    var closeDrawerWithKey = _.bind(this._closeDrawerWithKey, this);
    
    // Resize listeners
    window.addEventListener('resize', resizeHandler);
    this.listenTo(this, 'resize', resizeHandler);
    this.listenTo(this, 'change:forceNarrow', this._triggerNarrowMode, this);
    this.listenTo(this, 'change:rightDrawer', this._triggerRightDrawerMode, this);
    document.body.addEventListener('click', closeDrawer);
    document.body.addEventListener('keydown', closeDrawerWithKey)
    
    // Handle teardown
    this.once('remove', function () {
      
      // TODO: test this
      window.removeEventListener('resize', resizeHandler);
      document.body.removeEventListener('click', closeDrawer);
    });
    
    return this;
  },
  
  render: function () {
    
    // TODO: test this
    if (this.rendered) {
      return;
    }
    
    this.renderWithTemplate();
    
    // Query elements
    this.drawer = this.queryByHook('adv-drawer');
    this.main = this.queryByHook('adv-main');
    
    // Render Subviews
    this.renderSubview(this.subviews.drawer.view, this.drawer);
    this.renderSubview(this.subviews.main.view, this.main);
    
    this._setDefaultStyles();
    
    if (this.forceNarrow) {
      this._triggerNarrowMode();
    }
    
    if (this.rightDrawer) {
      this._triggerRightDrawerMode();
    }
    
    _.defer(_.bind(function () {
      this.trigger('resize');
    }, this));
    
    return this;
  },
  
  _setDefaultSubviews: function () {
    
    var DefaultView = View.extend({
      template: '<div></div>'
    });
    
    // Ensure subviews
    this.subviews = _.extend({}, this.subviews);
    
    // Defaults for drawer
    this.subviews.drawer = _.extend({
      view: new DefaultView(),
      width: 256,
      animationSpeed: 0
    }, this.subviews.drawer);
    
    // Defaults for main
    this.subviews.main = _.extend({
      view: new DefaultView(),
    }, this.subviews.main);
  },
  
  _setDefaultStyles: function () {
    
    var drawerStyles = {
      width: this.subviews.drawer.width + 'px'
    };
    drawerStyles[prefix.js + 'Transition'] = prefix.css + 'transform ' + this.subviews.drawer.animationSpeed + 'ms ease-in-out';
    style(this.drawer, drawerStyles);
    
    var mainStyles = {
      width: prefixedCalc('100% - ' + this.subviews.drawer.width + 'px')
    };
    mainStyles[prefix.js + 'Transition'] = 'width ' + this.subviews.drawer.animationSpeed + 'ms ease-in-out';
    style(this.main, mainStyles);
  },
  
  _handleWindowResize: function (e) {
    
    // No need to restyle elements if view is always narrow
    if (this.forceNarrow) {
      return this._triggerNarrowMode();
    }
    
    if (this.el && outerWidth(this.el) <= this.responsiveWidth) {
      return this._triggerNarrowMode();
    }
    
    this._triggerWideMode();
  },
  
  // Hide the drawer when it's less than the response width
  _triggerNarrowMode: function () {
    
    classList(this.el).add(this.defaultNarrowClass);
    
    style(this.main, {
      width: '100%'
    });
    
    this.narrow = true;
  },
  
  // Show the drawer when it's more than the response width
  _triggerWideMode: function () {
    
    classList(this.el).remove(this.defaultNarrowClass);
    
    style(this.main, {
      width: prefixedCalc('100% - ' + this.subviews.drawer.width + 'px')
    });
    
    this.narrow = false;
  },
  
  _triggerRightDrawerMode: function () {
    classList(this.el).add('right');
  },
  
  // Disables clicks from leaking outside the container
  // This is useful for closing the drawer when clicking 
  // outside of it.
  _drawerClicked: function (e) {
    
    e.stopPropagation();
  },
  
  _closeDrawerWithKey: function (e) {
    
    if (e.keyCode === 27 && this.selected === 'drawer') {
      this.closeDrawer();
    }
  },
    
  openDrawer: function () {
    
    // No need to do anything if drawer is visible in the layout
    if (!this.narrow || this.selected === 'drawer') {
      return;
    }
    
    classList(this.el).add('drawer');
    
    // Ensures an event doesn't trigger open and close at the same time
    _.defer(function (ctx) {
      ctx.selected = 'drawer';
    }, this);
  },
  
  closeDrawer: function () {
    
    // Do nothing if we don't need to
    if (!this.narrow || this.selected === 'main') {
      return;
    }
    
    classList(this.el).remove('drawer');
    
    // Ensures an event doesn't trigger open and close at the same time
    _.defer(function (ctx) {
      ctx.selected = 'main';
    }, this);
  },
  
  toggleDrawer: function (e) {
    
    e && e.stopPropagation();
    e && e.preventDefault();
    
    (this.selected === 'main') ? this.openDrawer() : this.closeDrawer();
  }
});
Example #6
0
var templates = require('../templates');
var ViewSwitcher = require('ampersand-view-switcher');


module.exports = View.extend({
    template: templates.body,
    autoRender: true,
    events: {
        'click a[href]': 'handleLinkClick'
    },
    initialize: function () {
        this.listenTo(app.router, 'page', this.handlePage);
    },
    render: function () {
        this.renderWithTemplate(this);
        this.pageSwitcher = new ViewSwitcher(this.queryByHook('page-container'));
    },
    handlePage: function (page) {
        this.pageSwitcher.set(page);
    },
    handleLinkClick: function (event) {
        var target = event.target;
        var isLocal = target.origin === location.origin;
        
        if (isLocal && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
            app.router.history.navigate(target.pathname, {trigger: true});
            event.preventDefault();
        }
    }
});
Example #7
0
module.exports = View.extend({

  template,

  props: {
    name: ['string', true],
    placeholder: 'string',
    label: ['string', true],
    value: 'string',
    infoText: ['string', true, ''],
    errorText: ['string', true, ''],
    errorLabel: 'string',
    isValid: ['boolean', true, false],
    isActive: ['boolean', true, false],
    isDirty: ['boolean', true, false],
    options: ['string', true], // HTML string of <options> for select
    modifiers: ['array', true, () => []],
    validations: ['array', true, () => []],
    tabIndex: 'number',
  },

  derived: {
    modifierClasses: {
      deps: ['modifiers', 'modifiers.length'],
      fn() {
        return this.modifiers.reduce((modClass, modifier) => {
          return `${modClass} control-group--${modifier}`;
        }, '').trim();
      }
    },

    showError: {
      deps: ['isValid', 'isDirty', 'errorText'],
      fn() {
        return this.errorText && !this.isValid && this.isDirty;
      }
    },

    formattedLabel: {
      deps: ['label'],
      fn() {
        return this.label ? `${this.label}: ` : '';
      }
    },

    isInline: {
      deps: ['modifiers'],
      fn() {
        return this.modifiers && this.modifiers.indexOf('inline') !== -1;
      }
    }
  },

  bindings: {
    formattedLabel: [{
      type: 'toggle',
      hook: 'label'
    }, {
      type: 'text',
      hook: 'label'
    }],

    placeholder: {
      type: 'text',
      hook: 'placeholder'
    },

    name: {
      type: 'attribute',
      hook: 'select',
      name: 'name',
    },

    modifierClasses: {
      type: 'class',
    },

    errorLabel: {
      type: 'attribute',
      name: 'data-err'
    },

    isInline: {
      type: 'booleanClass',
      yes: 'icon-new-chevron-light',
      no: 'icon-new-chevron-link',
      hook: 'select',
    },

    errorText: [{
      type: 'text',
      hook: 'error-text',
    }, {
      type: 'toggle',
      hook: 'error-text',
    }, {
      type: 'toggle',
      hook: 'info-text',
      invert: true,
    }],

    infoText: {
      type: 'text',
      hook: 'info-text',
    },

    isActive: {
      type: 'booleanClass',
      hook: 'control',
      name: 'control-group__control--active',
    },

    showError: {
      type: 'booleanClass',
      hook: 'control',
      yes: 'control-group__control--error',
    },

    tabIndex: {
      type: 'attribute',
      hook: 'select',
      name: 'tabindex'
    }
  },

  events: {
    'focus [data-hook=select]': 'onFocus',
    'blur [data-hook=select]': 'onBlur',
    'change [data-hook=select]': 'onChange',
  },

  initialize() {
    this.listenTo(this, 'error', (errorText) => {
      this.errorText = errorText;
      this.isValid = false;
      this.isDirty = true;
    });

    this.listenTo(this, 'change:value', () => this.onValueChange());
  },

  render() {
    this.renderWithTemplate();
    this.cacheElements({
      selectElement: '[data-hook=select]'
    });

    if (this.value) {
      this.onValueChange();
    }
  },

  setValue(value) {
    const { selectElement } = this;
    if (selectElement) {
      this.value = value;
      selectElement.value = value;
    }
  },

  onBlur() {
    this.isActive = false;
    this.trigger('blur');
  },

  onFocus() {
    this.isActive = true;
    this.trigger('focus');
  },

  onChange(event) {
    this.value = event.target.value;
  },

  onValueChange() {
    const { selectElement } = this;
    if (selectElement) {
      selectElement.value = this.value;
    }

    if (this.value && !this.isDirty) {
      this.isDirty = true;
    }

    this.validate();
  },

  validate() {
    const { validations, value } = this;

    let i = 0;
    let isValid = true;
    while (i < validations.length) {
      const validation = validations[i];
      isValid = isValid && validation.run(value);

      if (!isValid) {
        this.errorText = validation.message || "*Choose an option";
        this.isValid = false;
        return;
      }

      i++;
    }

    this.isValid = true;
    this.errorText = '';
  },
});
var Collection = AmpCollection.extend({
    model: Person,
    last: function () {
        return this.models[this.models.length - 1];
    },
    first: function () {
        return this.models[0];
    }
});

var ItemView = AmpView.extend({
    template: '<div></div>',
    bindings: {
        'model.name': ''
    },
    render: function () {
        this.renderWithTemplate();
        this.el.id = '_' + this.model.id;
        return this;
    }
});

var MainView = AmpView.extend({
    initialize: function () {
        this.el = document.createElement('div');
        this.el.id = 'container';
        this.collection = new Collection(data);
    },
    render: function (opts) {
        this.el.innerHTML = '<ul></ul>';
        this.renderCollection(this.collection, ItemView, this.get('ul'), opts);
Example #9
0
module.exports = View.extend({
    template: templates.body,
    initialize: function () {
        // this marks the correct nav item selected
        app.router.history.on('route', this.updateActiveNav, this);
    },
    events: {
        'click a[href]': 'handleLinkClick'
    },
    render: function () {
        // some additional stuff we want to add to the document head
        $('head').append(templates.head());

        // main renderer
        this.renderWithTemplate({me: me});

        // init and configure our page switcher
        this.pageSwitcher = new ViewSwitcher(this.getByRole('page-container'), {
            show: function (newView, oldView) {
                // it's inserted and rendered for me
                document.title = _.result(newView.pageTitle) || "Ampersand Sample";
                document.scrollTop = 0;

                // add a class specifying it's active
                newView.el.classList.add('active');

                // store an additional reference, just because
                app.currentPage = newView;
            }
        });

        // setting a favicon for fun (note, it's dyanamic)
        setFavicon('/images/ampersand.png');
        return this;
    },

    setPage: function (view) {
        // tell the view switcher to render the new one
        this.pageSwitcher.set(view);

        // mark the correct nav item selected
        this.updateActiveNav();
    },

    handleLinkClick: function (e) {
        var t = $(e.target);
        var aEl = t.is('a') ? t[0] : t.closest('a')[0];
        var local = window.location.host === aEl.host;
        var path = aEl.pathname.slice(1);

        // if the window location host and target host are the
        // same it's local, else, leave it alone
        if (local) {
            e.preventDefault();
            app.navigate(path);
        }
    },

    updateActiveNav: function () {
        var pathname = window.location.pathname;
        $('.nav a').each(function () {
            var navArray = _.compact($(this).attr('href').split('/')).join('/').toLowerCase();
            var pathArray = _.compact(pathname.split('/')).join('/').toLowerCase();

            if (pathArray === navArray) {
                $(this).parent().addClass('active');
            } else {
                $(this).parent().removeClass('active');
            }
        });
    }
});
Example #10
0
var View = require('ampersand-view')
var templates = require('../../templates')

module.exports = View.extend({
  template: templates.includes.apps.line
})
module.exports = View.extend({
    template : defaultTemplate,

    initialize: function(opts) {
        if (!opts || !opts.name) throw new Error('must pass in a name');

        // settings
        this.el = opts.el;
        this.name = opts.name;
        this.startingValue = !!opts.value;
        this.value = this.startingValue;
        this.label = opts.label || opts.name;
        this.required = (typeof opts.required === 'boolean') ? opts.required : false;
        this.validClass = opts.validClass || 'input-valid';
        this.invalidClass = opts.invalidClass || 'input-invalid';
        this.requiredMessage = opts.requiredMessage || 'This box must be checked.';
        this.parent = opts.parent || this.parent;
        this.autoRender = opts.autoRender;
        if (opts.template) this.template = opts.template;
        this.beforeSubmit = opts.beforeSubmit || this.beforeSubmit;

        this.setValue(this.value);
    },
    clear: function () {
        return this.setValue(false);
    },
    reset: function () {
        return this.setValue(this.startingValue);
    },
    remove: function () {
        if (this.input) this.input.removeEventListener('change', this.handleInputEvent, false);
        View.prototype.remove.call(this);
    },
    render: function () {
        this.renderWithTemplate(this);
        this.input = this.el.querySelector('input');
        this.labelEl = this.el.querySelector('[data-hook~=label]');
        this.messageContainer = this.el.querySelector('[data-hook~=message-container]');
        this.messageEl = this.el.querySelector('[data-hook~=message-text]');

        // bind event handlers
        this.handleInputEvent = this.handleInputEvent.bind(this);

        // add our event handlers
        this.input.addEventListener('change', this.handleInputEvent, false);

        this.setMessage(this.message);
        this.input.checked = !!this.value;
        this.input.name = this.name;
        this.labelEl.textContent = this.label;
    },
    // handle input events and show appropriate errors
    handleInputEvent: function () {
        // track whether user has edited directly
        if (document.activeElement === this.input) this.directlyEdited = true;
        this.value = this.input ? this.input.checked : this.value;
        this.test();
        if (this.parent) this.parent.update(this);
    },
    // set the error message if exists
    // hides the message container entirely otherwise
    setMessage: function (message) {
        var input = this.input;
        if (!input) return;
        this.message = message;
        // there is an error
        if (message && this.shouldValidate) {
            this.messageContainer.style.display = 'block';
            this.messageEl.textContent = message;
            dom.addClass(input, this.invalidClass);
            dom.removeClass(input, this.validClass);
        } else {
            this.messageContainer.style.display = 'none';
            if (this.shouldValidate && this.editedDirectly) {
                dom.addClass(input, this.validClass);
                dom.removeClass(input, this.invalidClass);
            }
        }
    },
    setValue: function (value) {
        if (this.input) this.input.checked = !!value;
        return this.handleInputEvent();
    },
    beforeSubmit: function () {
        this.shouldValidate = true;
        return this.handleInputEvent();
    },
    test: function () {
        var valid = !this.required || !!this.value;
        if (valid) this.shouldValidate = true;
        this.valid = valid;
        if (this.shouldValidate && !valid) {
            this.setMessage(this.requiredMessage);
        } else {
            this.setMessage('');
        }
        return valid;
    }
});
module.exports = View.extend({
    template: [
        '<label>',
            '<span data-hook="label"></span>',
            '<input class="form-input">',
            '<div data-hook="message-container" class="message message-below message-error">',
                '<p data-hook="message-text"></p>',
            '</div>',
        '</label>'
    ].join(''),
    bindings: {
        'name': {
            type: 'attribute',
            selector: 'input, textarea',
            name: 'name'
        },
        'label': [
            {
                hook: 'label'
            },
            {
                type: 'toggle',
                hook: 'label'
            }
        ],
        'message': {
            type: 'text',
            hook: 'message-text'
        },
        'showMessage': {
            type: 'toggle',
            hook: 'message-container'
        },
        'placeholder': {
            type: 'attribute',
            selector: 'input, textarea',
            name: 'placeholder'
        }
    },
    initialize: function (spec) {
        spec || (spec = {});
        this.tests = this.tests || spec.tests || [];
        this.on('change:type', this.handleTypeChange, this);
        this.handleChange = this.handleChange.bind(this);
        this.handleInputChanged = this.handleInputChanged.bind(this);
        var value = !spec.value && spec.value !== 0 ? '' : spec.value;
        this.startingValue = value;
        this.inputValue = value;
        this.on('change:valid change:value', this.reportToParent, this);
        this.on('change:validityClass', this.validityClassChanged, this);
        if (spec.autoRender) this.autoRender = spec.autoRender;
        if (spec.template) this.template = spec.template;
        if (spec.beforeSubmit) this.beforeSubmit = spec.beforeSubmit;
    },
    render: function () {
        this.renderWithTemplate();
        this.input = this.query('input') || this.query('textarea');
        // switches out input for textarea if that's what we want
        this.handleTypeChange();
        this.initInputBindings();
        // Skip validation on initial setValue
        // if the field is not required
        this.setValue(this.inputValue, !this.required);
        return this;
    },
    props: {
        inputValue: 'any',
        startingValue: 'any',
        name: 'string',
        type: ['string', true, 'text'],
        placeholder: ['string', true, ''],
        label: ['string', true, ''],
        required: ['boolean', true, true],
        directlyEdited: ['boolean', true, false],
        shouldValidate: ['boolean', true, false],
        message: ['string', true, ''],
        requiredMessage: ['string', true, 'This field is required.'],
        validClass: ['string', true, 'input-valid'],
        invalidClass: ['string', true, 'input-invalid'],
        validityClassSelector: ['string', true, 'input, textarea']
    },
    derived: {
        value: {
            deps: ['inputValue'],
            fn: function () {
                return this.inputValue;
            }
        },
        valid: {
            cache: false,
            deps: ['inputValue'],
            fn: function () {
                return !this.runTests();
            }
        },
        showMessage: {
            deps: ['message', 'shouldValidate'],
            fn: function () {
                return this.shouldValidate && this.message;
            }
        },
        changed: {
            deps: ['inputValue', 'startingValue'],
            fn: function () {
                return this.inputValue !== this.startingValue;
            }
        },
        validityClass: {
            deps: ['valid', 'validClass', 'invalidClass', 'shouldValidate'],
            fn: function () {
                if (!this.shouldValidate) {
                    return '';
                } else {
                    return this.valid ? this.validClass : this.invalidClass;
                }
            }
        }
    },
    setValue: function (value, skipValidation) {
        if (!this.input) {
            this.inputValue = value;
            return;
        }
        if (!value && value !== 0) {
            this.input.value = '';
        } else {
            this.input.value = value.toString();
        }
        this.inputValue = this.clean(this.input.value);
        if (!skipValidation && !this.getErrorMessage()) {
            this.shouldValidate = true;
        } else if (skipValidation) {
            this.shouldValidate = false;
        }
    },
    getErrorMessage: function () {
        var message = '';
        if (this.required && this.value === '') {
            return this.requiredMessage;
        } else {
            (this.tests || []).some(function (test) {
                message = test.call(this, this.value) || '';
                return message;
            }, this);
            return message;
        }
    },
    handleTypeChange: function () {
        if (this.type === 'textarea' && this.input.tagName.toLowerCase() !== 'textarea') {
            var parent = this.input.parentNode;
            var textarea = document.createElement('textarea');
            parent.replaceChild(textarea, this.input);
            this.input = textarea;
            this._applyBindingsForKey('');
        } else {
            this.input.type = this.type;
        }
    },
    clean: function (val) {
        return (this.type === 'number') ? Number(val) : val.trim();
    },
    //`input` event handler
    handleInputChanged: function () {
        if (document.activeElement === this.input) {
            this.directlyEdited = true;
        }
        this.inputValue = this.clean(this.input.value);
    },
    //`change` event handler
    handleChange: function () {
        if (this.inputValue && this.changed) {
            this.shouldValidate = true;
        }
        this.runTests();
    },
    beforeSubmit: function () {
        // catch undetected input changes that were not caught due to lack of
        // browser event firing see:
        // https://github.com/AmpersandJS/ampersand-input-view/issues/2
        this.inputValue = this.clean(this.input.value);

        // at the point where we've tried
        // to submit, we want to validate
        // everything from now on.
        this.shouldValidate = true;
        this.runTests();
    },
    runTests: function () {
        var message = this.getErrorMessage();
        if (!message && this.inputValue && this.changed) {
            // if it's ever been valid,
            // we want to validate from now
            // on.
            this.shouldValidate = true;
        }
        this.message = message;
        return message;
    },
    initInputBindings: function () {
        this.input.addEventListener('input', this.handleInputChanged, false);
        this.input.addEventListener('change', this.handleChange,false);
    },
    remove: function () {
        this.input.removeEventListener('input', this.handleInputChanged, false);
        this.input.removeEventListener('change', this.handleChange, false);
        View.prototype.remove.apply(this, arguments);
    },
    reset: function () {
        this.setValue(this.startingValue, true); //Skip validation just like on initial render
    },
    clear: function () {
        this.setValue('', true);
    },
    validityClassChanged: function (view, newClass) {
        var oldClass = view.previousAttributes().validityClass;
        getMatches(this.el, this.validityClassSelector).forEach(function (match) {
            dom.switchClass(match, oldClass, newClass);
        });
    },
    reportToParent: function () {
        if (this.parent) this.parent.update(this);
    }
});
export default View.extend({
  template: Template,
  initialize: function (opts) {
    opts || (opts = {})
    if (opts.geometry) this.geometry = opts.geometry
  },
  render: function () {
    this.renderWithTemplate(this)
    const latlng = this.geojsonToLatLng(this.geometry)
    setTimeout(() => this.initMap(this.query('.map'), latlng), 100) // TODO: This delay shouldn't be necessary
    return this
  },
  initMap: function (container, latlng) {
    const map = L.map(container, {
      trackResize: false,
      attributionControl: false,
      zoomControl: false,
      touchZoom: false,
      scrollWheelZoom: false,
      doubleClickZoom: false,
      boxZoom: false,
      dragging: false,
      tap: false,
      keyboard: false
    })

    L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}', {
      attribution: 'Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ',
      maxZoom: 16
    }).addTo(map)

    map.setView(latlng, 16)

    const icon = L.VectorMarkers.icon()
    L.marker(latlng, {icon: icon}).addTo(map)
  },
  geojsonToLatLng: function (geojson) {
    return [geojson.coordinates[1], geojson.coordinates[0]]
  }
})
Example #14
0
var View = require('ampersand-view');
var MainTemplate = require('../templates/main.jade');
var WolfView = require('./wolf');

module.exports = View.extend({
    template: MainTemplate,
    autoRender: true,
    events: {
        'click [data-hook=find-friends]': 'findFriends',
        'change [data-hook=sort-friends]': 'sortFriends'
    },
    render: function () {
        this.renderWithTemplate();

        new WolfView({
            model: this.model,
            el: this.queryByHook('me')
        });

        return this;
    },
    findFriends: function () {
        this.model.friends.fetch();
    },
    sortFriends: function (event) {
        this.model.sortFriendsBy = event.target.value;
    }
});
Example #15
0
var View = require('ampersand-view'),
  templates = require('../../templates')

module.exports = View.extend({
  template: templates.includes.host.system,
  bindings: {
    'model.uptimeFormatted': {
      type: 'text',
      hook: 'uptime'
    },
    'model.cpuSpeed': {
      type: 'text',
      hook: 'cpuSpeed'
    }
  }
})
Example #16
0
import View from 'ampersand-view'

import Template from '../templates/board-item.html'

export default View.extend({
  template: Template,
  bindings: {
    'model.title': '[data-hook~=link]',
    'model.id': {
      hook: 'link',
      type: (el, value, previousValue) => el.href = '#board/' + value
    }
  }
})
Example #17
0
var View = require('ampersand-view');
var templates = require('../templates');


module.exports = View.extend({
    template: templates.includes.user,
    bindings: {
        'model.id': '[data-hook~=name]',
        'model.points': '[data-hook~=points]',
        'model.avatar': {
            type: 'attribute',
            hook: 'avatar',
            name: 'src'
        },
        'model.viewUrl': {
            type: 'attribute',
            hook: 'name',
            name: 'href'
        }
    }
});
// this             = UserCretePage
// this.model       = PersonModel
// this.collection  = PersonsCollection
// ------------------------------------

module.exports = View.extend({
    template: templates.pages.user_create,

    subviews: {
        form: {
            role: 'user-form',
            prepareView: function () {
                return new UserFormView({
                    el: this.el,
                    submitCallback: function (data) {

                        this.collection.create(data, {
                            success: function () {
                                window.app.navigate('/users');
                            },
                            error: function (err) {
                                throw err;
                            }
                        });

                    }.bind(this)
                });
            }
        }
    }
});

module.exports = View.extend({
    template: templatizer.includes.person,
    bindings: {
        'model.fullName': '[data-hook~=name]',
        'model.avatar': {
            type: 'attribute',
            hook: 'avatar',
            name: 'src'
        },
        'model.editUrl': {
            type: 'attribute',
            hook: 'action-edit',
            name: 'href'
        },
        'model.viewUrl': {
            type: 'attribute',
            hook: 'name',
            name: 'href'
        }
    },
    events: {
        'click [data-hook~=action-delete]': 'handleRemoveClick'
    },
    handleRemoveClick: function () {
        this.model.destroy();
        return false;
    }
});
 
Example #20
0
var View = require('ampersand-view');
var templates = require('../../build/templates');

module.exports = View.extend({
	template: templates.pages.home
});
Example #21
0
var View = require('ampersand-view');
var templates = require('../../build/templates');

var Dispatcher = require('../dispatcher');

module.exports = View.extend({
	template: templates.includes.newHowl,
	autoRender: true,

	events: {
		'submit [role=new-howl]': 'createNewHowl'
	},

	initialize: function() {
		Dispatcher.actions.on('newHowlCreated', this.reset, this);
	},

	createNewHowl: function(event) {
		event.preventDefault();
		Dispatcher.actions.newHowl({
			content: this.get('[name=content]').value,
			createdAt: new Date()
		});
	},

	reset: function() {
		this.get('[name=content]').value = '';
	}

});
Example #22
0
const View = require('ampersand-view');

const StyleView = View.extend({
  template: require('./style_view.hbs'),

  props: {
    style: 'string'
  },

  render() {
    this.renderWithTemplate(this);
    return this;
  }
});

module.exports = StyleView;
Example #23
0
var View = require('ampersand-view');
var vdom = require('ampersand-virtual-dom-mixin');
var virtualize = require('./virtualize');
var App = require('./app');
var MySubview = View.extend(vdom, {
    template: function (v) {
        return "<div>WooHoo! <input type='text'/> " + v.someInt + " </div>";
    },
    props: {
        someInt: 'number'
    },
    initialize: function () {
        this.renderOnViewChange();
    }
});

var MyView = View.extend(vdom, {
    components: {
        'some-subview': MySubview
    },
    template: function (v) {
        return '<div><h1>Hi!<input type="text"/></h1><p>' + v.i + '</p><some-subview key="foo" someInt="' + v.i + '"/></div>';
    },
    props: {
        i: ['number', true, 10]
    },
    initialize: function () {
        this.on('change:i', this.render.bind(this));
        var i = setInterval(function () {
            this.i++;
        }.bind(this), 1000);
Example #24
0
module.exports = View.extend({
    template: templates.body,
    autoRender: true,
    initialize: function () {
        // this marks the correct nav item selected
        this.listenTo(app, 'page', this.handleNewPage);
    },
    events: {
        'click a[href]': 'handleLinkClick'
    },
    render: function () {
        // some additional stuff we want to add to the document head
        document.head.appendChild(domify(templates.head()));

        // main renderer
        this.renderWithTemplate(this);

        // init and configure our page switcher
        this.pageSwitcher = new ViewSwitcher(this.queryByHook('page-container'), {
            show: function (newView, oldView) {
                // it's inserted and rendered for me
                document.title = _.result(newView, 'pageTitle') || 'My Amazing App';
                document.scrollTop = 0;

                // add a class specifying it's active
                dom.addClass(newView.el, 'active');

                // store an additional reference, just because
                app.currentPage = newView;
            }
        });

        // setting a favicon for fun (note, it's dynamic)
        setFavicon('/favicon.ico');
        return this;
    },

    handleNewPage: function (view) {
        // tell the view switcher to render the new one
        this.pageSwitcher.set(view);

        // mark the correct nav item selected
        this.updateActiveNav();
    },

    // Handles all `<a>` clicks in the app not handled
    // by another view. This lets us determine if this is
    // a click that should be handled internally by the app.
    handleLinkClick: function (e) {
        // This module determines whether a click event is 
        // a local click (making sure the for modifier keys, etc)
        // and dealing with browser quirks to determine if this
        // event was from clicking an internal link. That we should
        // treat like local navigation.
        var localPath = localLinks.pathname(e);
        
        if (localPath) {
            e.preventDefault();
            app.navigate(localPath);
        }
    },

    updateActiveNav: function () {
        var path = window.location.pathname.slice(1);

        this.queryAll('.nav a[href]').forEach(function (aTag) {
            var aPath = aTag.pathname.slice(1);

            if ((!aPath && !path) || (aPath && path.indexOf(aPath) === 0)) {
                dom.addClass(aTag.parentNode, 'active');
            } else {
                dom.removeClass(aTag.parentNode, 'active');
            }
        });
    }
});
module.exports = View.extend({

    session: {
        valid: ['boolean', false, false]
    },

    derived: {
        data: {
            fn: function () {
                var res = {};
                for (var key in this._fieldViews) {
                    if (this._fieldViews.hasOwnProperty(key)) {
                        // If field name ends with '[]', don't interpret
                        // as verbose form field...
                        if (key.match(/\[\]$/)) {
                            res[key] = this._fieldViews[key].value;
                        } else {
                            set(res, key, this._fieldViews[key].value);
                        }
                    }
                }
                return this.clean(res);
            },
            cache: false
        }
    },

    initialize: function(opts) {
        opts = opts || {};
        this.el = opts.el;
        this.validCallback = opts.validCallback || this.validCallback;
        this.submitCallback = opts.submitCallback || this.submitCallback;
        this.clean = opts.clean || this.clean || function (res) { return res; };

        if (opts.model) this.model = opts.model;

        this.preventDefault = opts.preventDefault === false ? false : true;
        this.autoAppend = opts.autoAppend === false ? false : true;

        // storage for our fields
        this._fieldViews = {};
        this._fieldViewsArray = [];

        // add all our fields
        (result(opts, 'fields') || result(this, 'fields') || []).forEach(this.addField, this);

        if (opts.autoRender) {
            this.autoRender = opts.autoRender;
            // &-view requires this.template && this.autoRender to be truthy in
            // order to autoRender. template doesn't apply to &-form-view, but
            // we manually flip the bit to honor autoRender
            this.template = opts.template || this.template || true;
        }

        if (opts.values) this._startingValues = opts.values;

        if (this.validCallback) {
            this.on('change:valid', function(view, validBool) {
                this.validCallback(validBool);
            });
        }

        if (this.submitCallback) this.on('submit', this.submitCallback);
    },

    addField: function (fieldView) {
        this._fieldViews[fieldView.name] = fieldView;
        this._fieldViewsArray.push(fieldView);
        return this;
    },

    removeField: function (name, strict) {
        var field = this.getField(name, strict);
        if (field) {
            field.remove();
            delete this._fieldViews[name];
            this._fieldViewsArray.splice(this._fieldViewsArray.indexOf(field), 1);
        }
    },

    getField: function (name, strict) {
        var field = this._fieldViews[name];
        if (!field && strict) {
            throw new ReferenceError('field name  "' + name + '" not found');
        }
        return field;
    },

    setValues: function (data) {
        for (var name in data) {
            if (data.hasOwnProperty(name)) {
                this.setValue(name, data[name]);
            }
        }
    },

    checkValid: function () {
        this.valid = this._fieldViewsArray.every(function (field) {
            return field.valid;
        });
        return this.valid;
    },

    beforeSubmit: function () {
        this._fieldViewsArray.forEach(function (field) {
            if (field.beforeSubmit) field.beforeSubmit();
        });
    },

    update: function (field) {
        this.trigger('change:' + field.name, field);
        // if this one's good check 'em all
        if (field.valid) {
            this.checkValid();
        } else {
            this.valid = false;
        }
    },

    remove: function () {
        this.el.removeEventListener('submit', this.handleSubmit, false);
        this._fieldViewsArray.forEach(function (field) {
            field.remove();
        });
        return View.prototype.remove.call(this);
    },

    handleSubmit: function (e) {
        this.beforeSubmit();
        this.checkValid();
        if (!this.valid) {
            e.preventDefault();
            return false;
        }

        if (this.preventDefault) {
            e.preventDefault();
            this.trigger('submit', this.data);
            return false;
        }
    },

    reset: function () {
        this._fieldViewsArray.forEach(function (field) {
            if (isFunction(field.reset)) {
                field.reset();
            }
        });
    },

    clear: function () {
        this._fieldViewsArray.forEach(function (field) {
            if (isFunction(field.clear)) {
                field.clear();
            }
        });
    },

    render: function () {
        if (this.rendered) return;
        if (!this.el) {
            this.el = document.createElement('form');
        }
        if (this.autoAppend) {
            this.fieldContainerEl = this.el.querySelector('[data-hook~=field-container]') || this.el;
        }
        this._fieldViewsArray.forEach(function renderEachField(fV) {
            this.renderField(fV, true);
        }, this);
        if (this._startingValues) {
            // setValues is ideally executed at initialize, with no persistent
            // memory consumption inside ampersand-form-view, however, some
            // fieldViews don't permit `setValue(...)` unless the field view
            // itself is rendered.  thus, cache init values into _startingValues
            // and update all values after each field is rendered
            this.setValues(this._startingValues);
            delete this._startingValues;
        }
        this.handleSubmit = this.handleSubmit.bind(this);
        this.el.addEventListener('submit', this.handleSubmit, false);
        // force `change:valid` to be triggered when `valid === false` post-render,
        // despite `valid` not having changed from its default pre-render value of `false`
        this.set('valid', null, {silent: true});
        this.checkValid();
    },

    renderField: function (fieldView, renderInProgress) {
        if (!this.rendered && !renderInProgress) return this;
        fieldView.parent = this;
        fieldView.render();
        if (this.autoAppend) this.fieldContainerEl.appendChild(fieldView.el);
    },

    getValue: function(name) {
        var field = this.getField(name, true);
        return field.value;
    },

    setValue: function(name, value) {
        var field = this.getField(name, true);
        field.setValue(value);
        return this;
    },

    // deprecated
    getData: function() {
        console.warn('deprecation warning: ampersand-form-view `.getData()` replaced by `.data`');
        return this.data;
    }

});
Example #26
0
var MainView = View.extend({
    template: '<body><button data-hook="start-audio">Start Audio</button><button data-hook="start-video">Start Video</button><button data-hook="start-both">Start Both</button><p data-hook="mic-available">Mic Available</p><p data-hook="camera-available">Camera Available</p><p data-hook="screenshare-available">Screen Sharing Available</p><p data-hook="capturing-audio">Capturing Audio</p><p data-hook="capturing-video">Capturing Video</p></body>',
    bindings: {
        'model.capturingVideo': {
            type: 'toggle',
            hook: 'capturing-video'
        },
        'model.capturingAudio': {
            type: 'toggle',
            hook: 'capturing-audio'
        },
        'model.micAvailable': {
            type: 'toggle',
            hook: 'mic-available'
        },
        'model.cameraAvailable': {
            type: 'toggle',
            hook: 'camera-available'
        },
        'model.screenSharingAvailable': {
            type: 'toggle',
            hook: 'screenshare-available'
        }
    },
    events: {
        'click [data-hook~="start-audio"]': 'startAudio',
        'click [data-hook~="start-video"]': 'startVideo',
        'click [data-hook~="start-both"]': 'startBoth',
    },
    render: function () {
        this.renderWithTemplate();
        this.renderCollection(this.model.streams, MediaView, this.el);
    },
    startAudio: function () {
        this.model.start({audio: true, video: false});
    },
    startVideo: function () {
        this.model.start({audio: false, video: true});
    },
    startBoth: function () {
        this.model.start({audio: true, video: true});
    }
});
Example #27
0
'use strict';

var bind = require( 'lodash.bind' );
var View = require( 'ampersand-view' );
var indexTemplate = require( './about.tmpl' );

var IndexView = View.extend({

  autoRender: true,

  template: bind( indexTemplate.render, indexTemplate )

});

module.exports = IndexView;
var View = require('ampersand-view');
var ViewSwitcher = require('ampersand-view-switcher');
var templates = require('../templates');


module.exports = View.extend({
    template: templates.body,
    autoRender: true,
    initialize: function () {
        this.listenTo(app.router, 'page', this.handleNewPage);
    },
    render: function () {
        this.renderWithTemplate();
        this.pages = new ViewSwitcher(this.getByRole('page-container'));
    },
    handleNewPage: function (page) {
        this.pages.set(page);
    }
});
Example #29
0
/* global $$ */
var AmpersandView = require('ampersand-view');
var templates = require('../templates');
var dictionary = require('../dictionary');

module.exports = AmpersandView.extend({
    template: templates.errorDialog,
    // Add the dictionary to the context so that the template
    // engine can use it.
    d: dictionary,
    autoRender: true,
    bindings: {
        'model.message': '[data-hook=message-container]'
    },
    render: function() {

        this.renderWithTemplate();

        // Call Bootstraps function for modals and ensure to remove this
        // view when closing the modal. The view is attached to `body` by
        // Bootstrap.
        $$(this.el).modal({
            backdrop: 'static'
        }).on('hidden.bs.modal', this.remove.bind(this));
    }
});
Example #30
0
var LayerView = View.extend({
  template: function() {
    return `
      <div id="${this.model.getId()}" view-id="${this.cid}" class="missing-layer-view" style="will-change:transform, opacity, backfaceVisibility;width:100%;height:100%;display:table">
        <div style="display:table-cell;color:#666;vertical-align:middle;text-align:center;font-weight:700;font-size:30px;text-shadow:0 0 4px #000">
          Missing
          <span data-hook="type"></span> for
          <span data-hook="name"></span>
          layer view
          <br/>
          <span data-hook="frametime"></span>
        </div>
      </div>
    `;
  },

  updateParameter: function(parameter) {
    if (!parameter) return;
    this.setProperty('--' + parameter.name, parameter.value);
  },

  initialize: function() {
    this.listenTo(this.model.parameters, 'change:value', this.updateParameter);
    this.model.parameters.forEach(this.updateParameter, this);
  },

  derived: {
    styleEl: {
      deps: [],
      fn: function() {
        var id = this.model.getId();
        var el = document.getElementById('style-' + id);
        if (!el) {
          el = document.createElement('style');
          el.id = 'style-' + id;
          el.appendChild(document.createTextNode(''));
          document.head.appendChild(el);
        }
        return el;
      }
    },
    sheet: {
      deps: ['styleEl'],
      fn: function() {
        return this.styleEl.sheet;
      }
    },
    cssRule: {
      deps: ['sheet', 'model.layerStyles'],
      fn: function() {
        if (this.sheet.cssRules.length === 0) {
          this.addRule('', this.model.layerStyles);
        }
        return this.sheet.cssRules[0];
      }
    },
    layerStyleObj: {
      deps: ['model', 'model.layerStyles'],
      fn: function() {
        var exp = /[\s]*([^:]+)[\s]*:[\s]*([^;]+)[\s]*;[\s]*/gim;
        return ((this.model.layerStyles || '').match(exp) || [])
          .map(s => s
            .trim()
            .split(':')
              .map(ss => ss
                .replace(';', '')
                .trim()));
      }
    }
  },

  session: {
    width: ['number', true, 400],
    height: ['number', true, 300]
  },

  bindings: {
    'model.type': '[data-hook=type]',
    'model.name': '[data-hook=name]',
    'model.active': {type: 'toggle'},
    'model.layerStyles': {
      type: function() {
        var style = this.cssRule.style;
        this.layerStyleObj.forEach(function(arr) {
          style[arr[0]] = arr[1];
        });
      }
    },
    'model.zIndex': {
      type: function(el, val) {
        this.cssRule.style.zIndex = val;
      }
    }
  },

  setProperty: function(...args) {
    this.cssRule.style.setProperty(...args);
  },

  addRule: function(selector, parameters) {
    var sheet = this.sheet;
    var prefix = '#'+ this.model.getId() +' ';
    var index = sheet.cssRules.length;
    selector = (selector.indexOf('@') === 0 ? selector : prefix + selector).trim();
    for (var i = index - 1; i >= 0; i--) {
      if (sheet.cssRules[i].selectorText === selector) {
        sheet.deleteRule(i);
      }
    }


    index = sheet.cssRules.length;

    sheet.insertRule(selector + ' { ' + parameters + ' } ', index);
    return this;
  },

  remove: function() {
    // they are some issues with bootstraping a new setup if the element is removed
    // var styleEl = this.styleEl;
    // if (styleEl && styleEl.parentNode) {
    //   styleEl.parentNode.removeChild(styleEl);
    // }
    return View.prototype.remove.apply(this, arguments);
  },

  update: function() {}
});