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); },
'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(); } });
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(); } } });
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);
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'); } }); } });
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 © Esri — 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]] } })
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; } });
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' } } })
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 } } })
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; } });
var View = require('ampersand-view'); var templates = require('../../build/templates'); module.exports = View.extend({ template: templates.pages.home });
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 = ''; } });
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;
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);
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; } });
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}); } });
'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); } });
/* 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)); } });
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() {} });