diff --git a/.gitignore b/.gitignore index 56d6e17..fa8c89b 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,7 @@ luac.out *.hex +# old FossilSCM repo +piazza.repo +# info I needed to figure out what features to implement +info/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cd994e5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "server/bitser"] + path = server/bitser + url = https://github.com/gvx/bitser.git diff --git a/jsapp/AboutUs.js b/jsapp/AboutUs.js new file mode 100644 index 0000000..648c31b --- /dev/null +++ b/jsapp/AboutUs.js @@ -0,0 +1,18 @@ +var AboutUs = function(){ + return { + view: function(vnode){ + return m('.about-us', [ + m('h1.text-center', "About Us"), + m('p', "Welcome to Piazza Optical, your number one source for all your optical needs. From frames and lenses to lab equipment and accessories, we are here to provide you with all these supplies while giving you personalized attention and customer service. We're dedicated to making available products that rank the very best in the latest trends in frames and sunglasses, with a focus on quality of materials and craftsmanship at the lowest cost to aid you and your store/practice in achieving the most profits."), + m('p', "Founded in 1995, Piazza Optical has come a long way from its beginnings in a tiny office in North Miami, FL. When we first started out, our passion for fashion forward frames became an obsession and drove us to scour the best factories of the world in our industry, and gave us the pillars to become a leader in the optical supplies business without losing the personal touch, highlighting honesty and integrity which sets us apart from the competition far and near. Such is the base of our company, and our honesty towards and respect for our customers is what keeps our worldwide following at the top after all these years."), + m('p', "We hope you enjoy our products as much as we enjoy offering them to you. If you have any questions or comments, please don't hesitate to contact us!"), + m('.divider',''), + m('p', "Bienvenidos a Piazza Optical la principal distribuidora de armazones ópticas, lentes oftálmicas y equipos de consultorio y de laboratorio. Estamos aquí para proveer a ópticas, distribuidores y oftalmólogos muchos de los artículos relacionados con su establecimiento, brindando a cada cliente atención personalizada y servicio al cliente. Estamos dedicados a poner a su disposición artículos ópticos de la más alta gama en las últimas tendencias de la moda en armazones y gafas de sol, poniendo especial enfoque en la calidad de materiales y mano de obra al menor costo para promover mayores ganancias para Ud. y su óptica."), + m('p', "Fundada en 1995, Piazza Optical ha logrado una larga trayectoria desde nuestros comienzos en aquella modesta oficina en North Miami, FL. Desde el principio, nuestra pasión por la moda en óptica se tornó en una obsesión y nos impulsó a buscar y establecer una firme relación con las mejores fábricas de la industria, lo cual es el pilar para convertirnos en líderes en el ramo sin por ello perder el toque personal, permitiendonos brillar en honestidad e integridad que nos diferencia de la competencia lejana y cercana. Tal es la base de nuestra empresa, ya que nuestra honestidad y respeto por nuestros clientes es lo que hace que éstos se mantengan fieles a nuestra compañía despues de tantos años."), + m('p', "Deseamos que disfruten de nuestros productos de la misma manera que nosotros disfrutamos ponerlos a vuestra disposición. Si tienen algún comentario o pregunta, por favor no duden en contactarnos!") + ]); + } + }; + }; + +export default AboutUs; \ No newline at end of file diff --git a/jsapp/Accessories.js b/jsapp/Accessories.js new file mode 100644 index 0000000..63b8fb5 --- /dev/null +++ b/jsapp/Accessories.js @@ -0,0 +1,15 @@ +import ItemBrowser from './ItemBrowser.js'; +import _Accessories from './_Accessories.js'; + +var Accessories = function(){ +return { +view: function(){ +return m('.equipment', [ + m('h1.text-center','Accessories'), + m(ItemBrowser, {items: _Accessories.items}) + ]); +} +}; +}; + +export default Accessories; \ No newline at end of file diff --git a/jsapp/AccountApplicationBox.js b/jsapp/AccountApplicationBox.js new file mode 100644 index 0000000..83ee6c4 --- /dev/null +++ b/jsapp/AccountApplicationBox.js @@ -0,0 +1,170 @@ +import _AccountApplication from "./_AccountApplication.js"; + +var AccountApplicationBox = function(){ + return { + view: function(vnode){ + return m('.account-application.form-horizontal', [ + m('h1.text-center', 'Account Application Form'), + m('h3', 'Account Information'), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', [ + 'Username', + m('small', '(use your email address)') + ]) + ]), + m('col-9', [ + m('input[type=text]', { + onchange: function(e){ _AccountApplication.username = e.target.value; }, + value: _AccountApplication.username + }) + ]) + ]), + m('h3', 'Personal Information'), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Salutation') + ]), + m('.col-9', [ + _AccountApplication.salutations.map(function(s){ + return [ + m('label.form-radio.form-inline', [ + m('input[type=radio][name=salutation]', { + onchange: function(e){ _AccountApplication.salutation = e.target.value; }, + value: s.value + }), + m('i.form-icon',''), + s.text + ]) + ]; + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'First Name') + ]), + m('.col-9', [ + m('input[type=text][placeholder=First Name]', { + onchange: function(e){ _AccountApplication.firstname = e.target.value; }, + value: _AccountApplication.firstname + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Last Name') + ]), + m('.col-9', [ + m('input[type=text][placeholder=Last Name]', { + onchange: function(e){ _AccountApplication.lastname = e.target.value; }, + value: _AccountApplication.lastname + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Position in Company') + ]), + m('.col-9', _AccountApplication.positions.map(function(s){ + return [ + m('label.form-radio.form-inline', [ + m('input[type=radio][name=position]', { + onchange: function(e){ _AccountApplication.position = e.target.value; }, + value: s.value + }), + m('i.form-icon',''), + s.text + ]) + ]; + }) + ) + ]), + m('h3', 'Business Information'), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Name of Business') + ]), + m('.col-9', [ + m('input[type=text][placeholder=Name of Business (your shop/clinic/practice name)]', { + onchange: function(e){ _AccountApplication.store = e.target.value; }, + value: _AccountApplication.store + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', [ + 'Address', + m('small', '(full address including city and country)') + ]) + ]), + m('.col-9', [ + m('input[type=text][placeholder=Address (full address including city and country)]', { + onchange: function(e){ _AccountApplication.address = e.target.value; }, + value: _AccountApplication.address + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Type of Business') + ]), + m('.col-9', [ + _AccountApplication.practice_types.map(function(s){ + return [ + m('label.form-radio.form-inline', [ + m('input[type=radio][name=practice_type].practice-type', { + onchange: function(e){ _AccountApplication.practice_type = e.target.value; }, + value: s.value + }), + m('i.form-icon', ''), + s.text + ]) + ]; + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Phone (Office)') + ]), + m('.col-9', [ + m('input[type=text][placeholder=Phone (Office)]', { + onchange: function(e){ _AccountApplication.phone_office = e.target.value; }, + value: _AccountApplication.phone_office + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Phone (Cell/WhatsApp)') + ]), + m('.col-9', [ + m('input[type=text][placeholder=Phone (Cell/WhatsApp)]', { + onchange: function(e){ _AccountApplication.phone_cell = e.target.value; }, + value: _AccountApplication.phone_cell + }) + ]) + ]), + m('.form-group', [ + m('.col-3', [ + m('label.form-label', 'Email Address') + ]), + m('.col-9', [ + m('input[type=text][placeholder=Email Address].email', { + onchange: function(e){ _AccountApplication.email = e.target.value; }, + value: _AccountApplication.email + }) + ]) + ]), + m('button.btn.btn-primary.application-submit-button', {onclick: function(e){ + e.preventDefault(); // this is require since the button is in a form + _AccountApplication.submit(); + }}, "Submit") + ]); + } + }; + }; + +export default AccountApplicationBox; \ No newline at end of file diff --git a/jsapp/Admin.js b/jsapp/Admin.js new file mode 100644 index 0000000..3e391df --- /dev/null +++ b/jsapp/Admin.js @@ -0,0 +1,46 @@ +import OpenOrders from './OpenOrders.js'; +import ApplicationManager from './ApplicationManager.js'; +import ItemImageUploader from './ItemImageUploader.js'; +import FrameAdmin from './FrameAdmin.js'; +import _Admin from './_Admin.js'; +//import _Image from './_Image.js'; + +var c = function(page){ + return (_Admin.page === page ? '.active' : '') + } + +var Admin = function(){ + var image; + return { + view: function(vnode){ + return m('.admin',[ + m('h1.text-center','Admin'), + m('ul.tab.tab-block.c-hand', [ + m('li.tab-item'+c(OpenOrders), { + onclick: function(){ _Admin.page = OpenOrders; } + }, [ + m('a', 'Orders') + ]), + m('li.tab-item'+c(ApplicationManager), { + onclick: function(){ _Admin.page = ApplicationManager; } + }, [ + m('a', 'Account Applications') + ]), + m('li.tab-item'+c(FrameAdmin), { + onclick: function(){ _Admin.page = FrameAdmin; } + }, [ + m('a', 'Frames') + ]), + m('li.tab-item'+c(ItemImageUploader), { + onclick: function(){ _Admin.page = ItemImageUploader; } + }, [ + m('a', 'Image Upload') + ]) + ]), + m(_Admin.page) + ]) + } + }; + }; + +export default Admin; \ No newline at end of file diff --git a/jsapp/ApplicationManager.js b/jsapp/ApplicationManager.js new file mode 100644 index 0000000..c1cd8a9 --- /dev/null +++ b/jsapp/ApplicationManager.js @@ -0,0 +1,30 @@ +import _Admin from './_Admin.js'; + +function ApplicationManager(ivnode){ +return { view: function(vnode){ + return m('.application-manager',[ + m('h2', 'Open Applications'), + m('button.btn.refresh-application-button',{onclick: function(){ + _Admin.getOpenApplications(); + }},[m('i.icon.icon-refresh'), 'Refresh']), + m('ul.open-application-list', _Admin.open_applications.map(function(a){ + return m('li', [ + m('.name', a.salutation+' '+a.firstname+' '+a.lastname+ '('+a.position+' @ '+a.store+')'), + m('.info', a.practice_type+' (Office: '+a.phone_office+', Cell: '+a.phone_cell+', Email: '+a.email+')'), + m('.address', a.address), + m('input[type=text][placeholder=Password].application',{ + onchange: function(e){ a.password = e.target.value }, + value: a.password + }), + m('button.btn.btn-success.accept-application-button', {onclick: function(){ + _Admin.acceptApplication(a); + }}, 'Accept'), + m('button.btn.btn-error.accept-application-button', {onclick: function(){ + _Admin.declineApplication(a); + }}, 'Decline') + ]); + })) + ]); +}}} + +export default ApplicationManager; \ No newline at end of file diff --git a/jsapp/BaseBrowser.js b/jsapp/BaseBrowser.js new file mode 100644 index 0000000..58de227 --- /dev/null +++ b/jsapp/BaseBrowser.js @@ -0,0 +1,39 @@ +import _Cart from './_Cart.js'; +import BaseListItem from './BaseListItem.js'; + +var BaseBrowser = function(){ +return { +view: function(){ +var lenses = []; +return m('.base-listing', [ + m('table.table.table-striped', [ + m('thead', [ + m('tr', [ + m('th', 'Base'), + m('th', 'Add'), + m('th', 'Material'), + m('th', 'AR'), + m('th', 'Photochromatic'), + m('th', 'Price'), + m('th', 'Quantity') + ]) + ]), + m('tbody', [ + lenses.map(function(lens){ + return m(BaseListItem, {lens: lens}); + }), + m('tr', [ + m('td.text-center', {colspan: 7}, [ + m('button.btn.btn-primary.btn-lg.btn-block', [ + m('i.icon.icon-plus') + ]) + ]) + ]) + ]) + ]) + ]); +} +}; +} + +export default BaseBrowser; \ No newline at end of file diff --git a/jsapp/BaseListItem.js b/jsapp/BaseListItem.js new file mode 100644 index 0000000..bb3a6ca --- /dev/null +++ b/jsapp/BaseListItem.js @@ -0,0 +1,75 @@ +var BaseListItem = function(){ +return { +view: function(vnode){ +var lens = vnode.attrs.lens; +return m('tr', [ + m('td', [ // Base + m('.form-group', [ + m('select.form-select', [ + m('option', {value: '2'}, 'Base 2'), + m('option', {value: '4'}, 'Base 4'), + m('option', {value: '6'}, 'Base 6'), + m('option', {value: '8'}, 'Base 8') + ]) + ]) + ]), + m('td', [ // Add + m('.form-group', [ + m('select.form-select', [ + m('option', {value: '1.00'}, '1.00'), + m('option', {value: '1.25'}, '1.25'), + m('option', {value: '1.50'}, '1.50'), + m('option', {value: '1.75'}, '1.75'), + m('option', {value: '2.00'}, '2.00'), + m('option', {value: '2.25'}, '2.25'), + m('option', {value: '2.50'}, '2.50'), + m('option', {value: '2.75'}, '2.75'), + m('option', {value: '3.00'}, '3.00') + ]) + ]) + ]), + m('td', [ // Material + m('.form-group', [ + m('select.form-select', [ + m('option', {value: 'cr39'}, 'CR39'), + m('option', {value: 'ft'}, 'F/T'), + m('option', {value: 'kriptok'}, 'Kriptok'), + m('option', {value: 'progressive'}, 'Progressive') + ]) + ]) + ]), + m('td', [ // AR Coating + m('.form-group', [ + m('label.form-checkbox', [ + m('input', {type: 'checkbox', checked: false}), + m('i.form-icon') + ]), + ]) + ]), + m('td', [ // Photochromatic + m('.form-group', [ + m('select.form-select', [ + m('option', {value: ''}, 'None'), + m('option', {value: 'photogray'}, 'Photogray'), + m('option', {value: 'photobrown'}, 'Photobrown') + ]) + ]) + ]), + m('td', '$3'), // Price + m('td', [ // Quantity + m('.form-group.input-group', [ + m('input.form-input', { + type: 'text', + value: '0' + }), + m('button.btn.btn-primary.input-group-btn', [ + m('i.icon.icon-delete') + ]) + ]) + ]) + ]); +} +}; +}; + +export default BaseListItem; \ No newline at end of file diff --git a/jsapp/Carousel.js b/jsapp/Carousel.js new file mode 100644 index 0000000..a3fd552 --- /dev/null +++ b/jsapp/Carousel.js @@ -0,0 +1,47 @@ +var Carousel = function(){ +return { +view: function(vnode){ + var images = vnode.attrs.images; + var name = vnode.attrs.name; + + return m('.carousel', [ + images.map(function(image,index){ + return m('input.carousel-locator', { + id: 'slide-'+index, + type: 'radio', + name: name, + hidden: true, + checked: (index===0 ? true : false) + }); + }), + m('.carousel-container', images.map(function(image, index){ + var prev_index = (index === 0 ? images.length-1 : index-1); + var next_index = (index === images.length-1 ? 0 : index+1); + return m('figure.carousel-item', [ + m('label.item-prev.btn.btn-action.btn-large', { + "for": 'slide-'+prev_index + }, [ + m('i.icon.icon-arrow-left') + ]), + m('label.item-next.btn.btn-action.btn-large', { + "for": 'slide-'+next_index + }, [ + m('i.icon.icon-arrow-right') + ]), + m('img.img-responsive.rounded', { + src: image.src, + alt: image.description + }) + ]); + })), + m('.carousel-nav', images.map(function(image, index){ + return m('label.nav-item.text-hide.c-hand',{ + "for": 'slide-'+index + }, index.toString()); + })), + ]); +} +}; +}; + +export default Carousel; \ No newline at end of file diff --git a/jsapp/Cart.js b/jsapp/Cart.js new file mode 100644 index 0000000..4363c38 --- /dev/null +++ b/jsapp/Cart.js @@ -0,0 +1,77 @@ +import _Cart from './_Cart.js'; +import _Item from './_Item.js'; +import _User from './_User.js'; +import _DB from './_DB.js'; +import _Message from './_Message.js'; +import toNumber from './util/toNumber.js'; +import isNumber from './util/isNumber.js'; + +function Cart(initialVnode) { + return { + view: function(vnode) { + if(!_User.isLoggedIn()){ + return m('.cart', [ + m("h1.text-center", "Shopping Cart"), + m('p', 'You must be logged-in to see your cart.') + ]); + } + var grand_total = 0; + return m(".cart", [ + m("h1.text-center", "Shopping Cart"), + m("table.table.table-striped.table-hover.line-item-list", [ + m('thead', [ + m('tr', [ + m('th', 'Item'), + m('th', 'Price'), + m('th', 'Quantity'), + m('th', 'Subtotal') + ]) + ]), + m('tbody', + _Cart.current.lineitem_ids.map(function(lineitem_id, lineitem_index){ + var lineitem = _DB.getRecord(lineitem_id); + var item = _DB.getRecord(lineitem.item_id); + var price = lineitem.price; + var quantity = lineitem.quantity; + var sub_total = price*quantity; + grand_total += sub_total; + return m('tr.line-item', [ + m('td.item-title', item.name), + m('td.price', "$"+price), + m('td', [ + m('input[type=text].quantity.form-input', { + // TODO: Don't update quantity if it's not a (whole) number + onchange: function (e) { + //lineitem.quantity = toNumber(e.target.value); + var new_quantity = toNumber(e.target.value); + //if(new_quantity === 0){ + // _Cart.removeLineItem(lineitem_index); // TODO: change to removeByLineitem, it's more efficient + // } + //else if(!isNumber(new_quantity)){ + if(!isNumber(new_quantity)){ + //console.log("Qty. is not a number."); + _Message.addError("Quantity '"+new_quantity+"' is not a number."); + } + else{ + _Cart.updateQuantityOfItem(item, new_quantity); + //lineitem.quantity = new_quantity; + } + }, + value: lineitem.quantity + }), + ]), + m('td.line-item-total', '$'+sub_total) + ]); + }) + ) + ]), + m('.total', [ + m('label.grand-total-label', "Grand Total: "), + m('span.grand-total', '$'+grand_total) + ]), + m("button.btn.btn-primary", {onclick: _Cart.placeOrder}, "Place Order") + ]); + } + } +} +export default Cart \ No newline at end of file diff --git a/jsapp/CollectionsList.js b/jsapp/CollectionsList.js new file mode 100644 index 0000000..b7fb382 --- /dev/null +++ b/jsapp/CollectionsList.js @@ -0,0 +1,37 @@ +import _Item from './_Item.js'; +import _Navigation from './_Navigation.js'; +import ItemBrowser from './ItemBrowser.js'; +import _CollectionsList from './_CollectionsList.js'; +import _User from './_User.js'; + +var CollectionsList = function(){ +return {view: function(vnode){ + var brands = _CollectionsList.house_brands; + return m('.container', [ + m('.columns', [ + m('.column.col-12', [ + m('h1.text-center', 'Collections') + ]) + ]), + m('.columns', brands.map(function(brand){ + return m('.column.col-4.col-md-6.col-xs-12.mt-1', [ + m('.card.c-hand', {onclick: function(e){ + //var item_browser_attrs = {}; + //item_browser_attrs.items = []; + _Item.getItems({brand:brand}).then(function(items){ + //item_browser_attrs.items = items; + _Navigation.navigateTo(ItemBrowser, {items: items}); + }); + }},[ + m('.card-header', [ + m('.card-title.h3.text-center', brand) + ]) + ]) + ]); + } + )) + ]); +}}; +}; + +export default CollectionsList; \ No newline at end of file diff --git a/jsapp/ContactUs.js b/jsapp/ContactUs.js new file mode 100644 index 0000000..e4e32dc --- /dev/null +++ b/jsapp/ContactUs.js @@ -0,0 +1,25 @@ +var ContactUs = function(){ +return { +view: function(vnode){ + return m('.contactus', [ + m('h1.text-center', 'Contact Us'), + m('h3', ['Phone ', m('small', 'Customer Service & General Inquiries')]), + m('p', '(305)-818-6786'), + m('h3', 'Email'), + m('p', 'silvano@piazzaoptical.com'), + m('h3', 'Hours'), + m('p', 'Mon-Fri 9:00-5:30'), + m('h3', 'Address'), + m('p', [ + '8200 NW 27th. Street', + m('br'), + 'Suite 105', + m('br'), + 'Miami, FL 33122' + ]) + ]); +} +}; +}; + +export default ContactUs; diff --git a/jsapp/DesignerCollectionsList.js b/jsapp/DesignerCollectionsList.js new file mode 100644 index 0000000..c067ac3 --- /dev/null +++ b/jsapp/DesignerCollectionsList.js @@ -0,0 +1,38 @@ +import _Item from './_Item.js'; +import _Navigation from './_Navigation.js'; +import ItemBrowser from './ItemBrowser.js'; +import _CollectionsList from './_CollectionsList.js'; +import _User from './_User.js'; + +var DesignerCollectionsList = function(){ +return {view: function(vnode){ + var brands = _CollectionsList.designer_brands; + return m('.container', [ + m('.columns', [ + m('.column.col-12', [ + m('h1.text-center', 'Designer Collections') + ]) + ]), + _User.isGuest() ? m('.columns', [m('.column.col-12', 'Please Sign-In to See Designer Brands')]) : + m('.columns', brands.map(function(brand){ + return m('.column.col-4.col-md-6.col-xs-12.mt-1', [ + m('.card.c-hand', {onclick: function(e){ + //var item_browser_attrs = {}; + //item_browser_attrs.items = []; + _Item.getItems({brand:brand}).then(function(items){ + //item_browser_attrs.items = items; + _Navigation.navigateTo(ItemBrowser, {items: items}); + }); + }},[ + m('.card-header', [ + m('.card-title.h3.text-center', brand) + ]) + ]) + ]); + } + )) + ]); +}}; +}; + +export default DesignerCollectionsList; \ No newline at end of file diff --git a/jsapp/Equipment.js b/jsapp/Equipment.js new file mode 100644 index 0000000..b934653 --- /dev/null +++ b/jsapp/Equipment.js @@ -0,0 +1,15 @@ +import ItemBrowser from './ItemBrowser.js'; +import _Equipment from './_Equipment.js'; + +var Equipment = function(){ +return { +view: function(){ +return m('.equipment', [ + m('h1.text-center','Equipment'), + m(ItemBrowser, {items: _Equipment.items}) + ]); +} +}; +}; + +export default Equipment; \ No newline at end of file diff --git a/jsapp/Events.js b/jsapp/Events.js new file mode 100644 index 0000000..bd0c9aa --- /dev/null +++ b/jsapp/Events.js @@ -0,0 +1,11 @@ +var Events = function(){ + return { + view: function(vnode){ + return m('.events', [ + m('h1.text-center', "Upcoming Events") + ]); + } + }; + }; + +export default Events; \ No newline at end of file diff --git a/jsapp/FileUploadDropArea.js b/jsapp/FileUploadDropArea.js new file mode 100644 index 0000000..c1af6ad --- /dev/null +++ b/jsapp/FileUploadDropArea.js @@ -0,0 +1,41 @@ +var noop = function(){}; + +function FileUploadDropArea(){ + return { + view: function(vnode){ + var cb = vnode.attrs.cb; + if(typeof cb !== 'Function'){ + cb = noop; + } + return m('.file-upload-drop-area',{ + ondragover: function(e){ + e.preventDefault(); + }, + ondrop: function(e){ + e.preventDefault(); // don't open the file in the browser + var files = []; + + // some browsers use `.items` and some use `.files`, so check for both: + if (e.dataTransfer.items) { + for (var i = 0; i < e.dataTransfer.items.length; i++) { + if (e.dataTransfer.items[i].kind === 'file') { // If dropped items aren't files, reject them + files.push( e.dataTransfer.items[i].getAsFile() ); + } + } + } + else { + for (var i = 0; i < e.dataTransfer.files.length; i++) { + files.push( e.dataTransfer.files[i] ); + } + } + for(var i=0, file=null; i B) { + return 1; +  } + else{ // names must be equal +   return 0; + } + } + +function ItemBrowser(initialVnode) { + return { + view: function(vnode) { + var items = vnode.attrs.items + items.sort(cmp_fn); + return m(".item-browser.container", [ + //m(ItemFilter), + m(ItemListing, {items: items}) + ]); + } + } +} +export default ItemBrowser \ No newline at end of file diff --git a/jsapp/ItemChooser.js b/jsapp/ItemChooser.js new file mode 100644 index 0000000..f0a6b27 --- /dev/null +++ b/jsapp/ItemChooser.js @@ -0,0 +1,63 @@ +import _Item from './_Item.js'; +import isArray from './util/isArray.js'; + +var ItemChooser = function(){ +var search_term = ''; +var results = []; +var chosen_item = null; +return { +view: function(vnode){ + var onchoose = vnode.attrs.onchoose; + return m('.item-search', [ + m('input[type=text][placeholder=Model]', { + onchange: function(e){ + search_term = e.target.value; + results = []; + if(search_term !== ''){ + _Item.searchByString(search_term) + .then(function(res){ + if(res.success === true){ + if(isArray(res.results)){ // empty list comes back as `{}` from server instead of `[]` + results = res.results; + } + else{ + results = []; + } + } + }); + } + }, + value: search_term + }), + m('table.table.table-striped.table-hover.results', [ + m('thead', [ + m('tr', [ + m('th', 'Brand'), + m('th', 'Model'), + m('th', 'Color') + ]) + ]), + m('tbody', + results.map(function(item){ + var active_class = (chosen_item === item ? '.active' : ''); + return m('tr'+active_class, { + onclick: function(e){ + e.stopPropagation(); + chosen_item = item; + onchoose(item); + } + },[ + m('td', item.brand), + m('td', item.model), + m('td', item.color) + ]) + }) + ) + ] + ) + ]); +} +}; +}; + +export default ItemChooser; \ No newline at end of file diff --git a/jsapp/ItemFilter.js b/jsapp/ItemFilter.js new file mode 100644 index 0000000..3f7f651 --- /dev/null +++ b/jsapp/ItemFilter.js @@ -0,0 +1,14 @@ + +function ItemFilter(initialVnode) { + return { + view: function(vnode) { + return m(".item-filter", [ + m(".heading", "Brand"), + m(".heading", "Color"), + m(".heading", "Category"), + m(".heading", "Tags") + ]); + } + } +} +export default ItemFilter \ No newline at end of file diff --git a/jsapp/ItemImageController.js b/jsapp/ItemImageController.js new file mode 100644 index 0000000..515b76d --- /dev/null +++ b/jsapp/ItemImageController.js @@ -0,0 +1,75 @@ +import _Image from './_Image.js'; +import _Item from './_Item.js'; +import _DB from './_DB.js'; +import _FullscreenImage from './_FullscreenImage.js'; +import isObject from './util/isObject.js'; + +function ItemImageController(ivnode){ +return { view: function(vnode){ + var item = vnode.attrs.item; + if(item === null){ + return m('.msg', 'No item chosen.'); + } + if(typeof item.image_ids === 'undefined'){ item.image_ids=[]; } + var images = item.image_ids.map(function(image_id){ + if(isObject(image_id)){ + return image_id; + } + else{ + return _DB.getRecord(image_id); + } + }); + var no_images = (images.length === 0); + return m('.item-image-controller.bg-secondary.text-center', { + style: 'height: 500px;', + ondragover: function(e){ + e.preventDefault(); + }, + ondrop: function(e){ + e.preventDefault(); // don't open the file in the browser + var files = []; + + // some browsers use `.items` and some use `.files`, so check for both: + if (e.dataTransfer.items) { + for (var i = 0; i < e.dataTransfer.items.length; i++) { + if (e.dataTransfer.items[i].kind === 'file') { // If dropped items aren't files, reject them + files.push( e.dataTransfer.items[i].getAsFile() ); + } + } + } + else { + for (var i = 0; i < e.dataTransfer.files.length; i++) { + files.push( e.dataTransfer.files[i] ); + } + } + for(var i=0; i0&&(i.className=l.join(" ")),r[e]={tag:o,attrs:i}}(i),a):(a.tag=i,a)}if(i.trust=function(t){return null==t&&(t=""),e("<",void 0,void 0,t,void 0,void 0)},i.fragment=function(){var n=t.apply(0,arguments);return n.tag="[",n.children=e.normalizeChildren(n.children),n},(a=function(e){if(!(this instanceof a))throw new Error("Promise must be called with `new`");if("function"!=typeof e)throw new TypeError("executor must be a function");var t=this,n=[],r=[],o=s(n,!0),l=s(r,!1),i=t._instance={resolvers:n,rejectors:r},u="function"==typeof setImmediate?setImmediate:setTimeout;function s(e,o){return function a(s){var f;try{if(!o||null==s||"object"!=typeof s&&"function"!=typeof s||"function"!=typeof(f=s.then))u(function(){o||0!==e.length||console.error("Possible unhandled promise rejection:",s);for(var t=0;t0||e(n)}}var r=n(l);try{e(n(o),r)}catch(e){r(e)}}c(e)}).prototype.then=function(e,t){var n,r,o=this._instance;function l(e,t,l,i){t.push(function(t){if("function"!=typeof e)l(t);else try{n(e(t))}catch(e){r&&r(e)}}),"function"==typeof o.retry&&i===o.state&&o.retry()}var i=new a(function(e,t){n=e,r=t});return l(e,o.resolvers,n,!0),l(t,o.rejectors,r,!1),i},a.prototype.catch=function(e){return this.then(null,e)},a.prototype.finally=function(e){return this.then(function(t){return a.resolve(e()).then(function(){return t})},function(t){return a.resolve(e()).then(function(){return a.reject(t)})})},a.resolve=function(e){return e instanceof a?e:new a(function(t){t(e)})},a.reject=function(e){return new a(function(t,n){n(e)})},a.all=function(e){return new a(function(t,n){var r=e.length,o=0,l=[];if(0===e.length)t([]);else for(var i=0;i'+t.children+"",i=i.firstChild):i.innerHTML=t.children,t.dom=i.firstChild,t.domSize=i.childNodes.length,t.instance=[];for(var a,u=r.createDocumentFragment();a=i.firstChild;)t.instance.push(a),u.appendChild(a);w(e,u,o)}function h(e,t,n,r,o,l){if(t!==n&&(null!=t||null!=n))if(null==t||0===t.length)s(e,n,0,n.length,r,o,l);else if(null==n||0===n.length)x(e,t,0,t.length);else{var i=null!=t[0]&&null!=t[0].key,a=null!=n[0]&&null!=n[0].key,u=0,f=0;if(!i)for(;f=f&&C>=u&&(w=t[S],b=n[C],w.key===b.key);)w!==b&&p(e,w,b,r,o,l),null!=b.dom&&(o=b.dom),S--,C--;for(;S>=f&&C>=u&&(d=t[f],h=n[u],d.key===h.key);)f++,u++,d!==h&&p(e,d,h,r,y(t,f,o),l);for(;S>=f&&C>=u&&u!==C&&d.key===b.key&&w.key===h.key;)g(e,w,E=y(t,f,o)),w!==h&&p(e,w,h,r,E,l),++u<=--C&&g(e,d,o),d!==b&&p(e,d,b,r,o,l),null!=b.dom&&(o=b.dom),f++,w=t[--S],b=n[C],d=t[f],h=n[u];for(;S>=f&&C>=u&&w.key===b.key;)w!==b&&p(e,w,b,r,o,l),null!=b.dom&&(o=b.dom),C--,w=t[--S],b=n[C];if(u>C)x(e,t,f,S+1);else if(f>S)s(e,n,u,C+1,r,o,l);else{var j,z,A=o,O=C-u+1,N=new Array(O),T=0,P=0,$=2147483647,I=0;for(P=0;P=u;P--){null==j&&(j=v(t,f,S+1));var L=j[(b=n[P]).key];null!=L&&($=L<$?L:-1,N[P-u]=L,w=t[L],t[L]=null,w!==b&&p(e,w,b,r,o,l),null!=b.dom&&(o=b.dom),I++)}if(o=A,I!==S-f+1&&x(e,t,f,S+1),0===I)s(e,n,u,C+1,r,o,l);else if(-1===$)for(T=(z=function(e){for(var t=[0],n=0,r=0,o=0,l=m.length=e.length,o=0;o>>1)+(r>>>1)+(n&r&1);e[t[a]]0&&(m[o]=t[n-1]),t[n]=o)}}for(n=t.length,r=t[n-1];n-- >0;)t[n]=r,r=m[r];return m.length=0,t}(N)).length-1,P=C;P>=u;P--)h=n[P],-1===N[P-u]?c(e,h,r,l,o):z[T]===P-u?T--:g(e,h,o),null!=h.dom&&(o=n[P].dom);else for(P=C;P>=u;P--)h=n[P],-1===N[P-u]&&c(e,h,r,l,o),null!=h.dom&&(o=n[P].dom)}}else{var R=t.lengthR&&x(e,t,u,t.length),n.length>R&&s(e,n,u,n.length,r,o,l)}}}function p(t,n,r,o,i,u){var s=n.tag;if(s===r.tag){if(r.state=n.state,r.events=n.events,function(e,t){do{if(null!=e.attrs&&"function"==typeof e.attrs.onbeforeupdate){var n=a.call(e.attrs.onbeforeupdate,e,t);if(void 0!==n&&!n)break}if("string"!=typeof e.tag&&"function"==typeof e.state.onbeforeupdate){var n=a.call(e.state.onbeforeupdate,e,t);if(void 0!==n&&!n)break}return!1}while(0);return e.dom=t.dom,e.domSize=t.domSize,e.instance=t.instance,e.attrs=t.attrs,e.children=t.children,e.text=t.text,!0}(r,n))return;if("string"==typeof s)switch(null!=r.attrs&&_(r.attrs,r,o),s){case"#":!function(e,t){e.children.toString()!==t.children.toString()&&(e.dom.nodeValue=t.children),t.dom=e.dom}(n,r);break;case"<":!function(e,t,n,r,o){t.children!==n.children?(E(e,t),d(e,n,r,o)):(n.dom=t.dom,n.domSize=t.domSize,n.instance=t.instance)}(t,n,r,u,i);break;case"[":!function(e,t,n,r,o,l){h(e,t.children,n.children,r,o,l);var i=0,a=n.children;if(n.dom=null,null!=a){for(var u=0;u-1||null!=e.attrs&&e.attrs.is||"href"!==t&&"list"!==t&&"form"!==t&&"width"!==t&&"height"!==t)&&t in e.dom}var N=/[A-Z]/g;function T(e){return"-"+e.toLowerCase()}function P(e){return"-"===e[0]&&"-"===e[1]?e:"cssFloat"===e?"float":e.replace(N,T)}function $(e,t,n){if(t===n);else if(null==n)e.style.cssText="";else if("object"!=typeof n)e.style.cssText=n;else if(null==t||"object"!=typeof t)for(var r in e.style.cssText="",n)null!=(o=n[r])&&e.style.setProperty(P(r),String(o));else{for(var r in n){var o;null!=(o=n[r])&&(o=String(o))!==String(t[r])&&e.style.setProperty(P(r),o)}for(var r in t)null!=t[r]&&null==n[r]&&e.style.removeProperty(P(r))}}function I(){this._=n}function L(e,t,n){if(null!=e.events){if(e.events[t]===n)return;null==n||"function"!=typeof n&&"object"!=typeof n?(null!=e.events[t]&&e.dom.removeEventListener(t.slice(2),e.events,!1),e.events[t]=void 0):(null==e.events[t]&&e.dom.addEventListener(t.slice(2),e.events,!1),e.events[t]=n)}else null==n||"function"!=typeof n&&"object"!=typeof n||(e.events=new I,e.dom.addEventListener(t.slice(2),e.events,!1),e.events[t]=n)}function R(e,t,n){"function"==typeof e.oninit&&a.call(e.oninit,t),"function"==typeof e.oncreate&&n.push(a.bind(e.oncreate,t))}function _(e,t,n){"function"==typeof e.onupdate&&n.push(a.bind(e.onupdate,t))}return I.prototype=Object.create(null),I.prototype.handleEvent=function(e){var t,n=this["on"+e.type];"function"==typeof n?t=n.call(e.currentTarget,e):"function"==typeof n.handleEvent&&n.handleEvent(e),this._&&!1!==e.redraw&&(0,this._)(),!1===t&&(e.preventDefault(),e.stopPropagation())},function(t,r,o){if(!t)throw new TypeError("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.");var l=[],i=u(),a=t.namespaceURI;null==t.vnodes&&(t.textContent=""),r=e.normalizeChildren(Array.isArray(r)?r:[r]);var s=n;try{n="function"==typeof o?o:void 0,h(t,t.vnodes,r,l,null,"http://www.w3.org/1999/xhtml"===a?void 0:a)}finally{n=s}t.vnodes=r,null!=i&&u()!==i&&"function"==typeof i.focus&&i.focus();for(var c=0;c=0&&(o.splice(l,2),t(n,[],u)),null!=r&&(o.push(n,r),t(n,e(r),u))},redraw:u}}(u,requestAnimationFrame,console),c=function(e){if("[object Object]"!==Object.prototype.toString.call(e))return"";var t=[];for(var n in e)r(n,e[n]);return t.join("&");function r(e,n){if(Array.isArray(n))for(var o=0;o=0&&(v+=e.slice(n,o)),s>=0&&(v+=(n<0?"?":"&")+u.slice(s,h));var m=c(a);return m&&(v+=(n<0&&s<0?"?":"&")+m),r>=0&&(v+=e.slice(r)),d>=0&&(v+=(r<0?"":"&")+u.slice(d)),v},h=function(e,t,n){var r=0;function o(e){return new t(e)}function l(e){return function(r,l){"string"!=typeof r?(l=r,r=r.url):null==l&&(l={});var i=new t(function(t,n){e(d(r,l.params),l,function(e){if("function"==typeof l.type)if(Array.isArray(e))for(var n=0;n=200&&e.target.status<300||304===e.target.status||/^file:\/\//i.test(t),a=e.target.response;if("json"===c?e.target.responseType||"function"==typeof n.extract||(a=JSON.parse(e.target.responseText)):c&&"text"!==c||null==a&&(a=e.target.responseText),"function"==typeof n.extract?(a=n.extract(e.target,n),i=!0):"function"==typeof n.deserialize&&(a=n.deserialize(a)),i)r(a);else{try{l=e.target.responseText}catch(e){l=a}var u=new Error(l);u.code=e.target.status,u.response=a,o(u)}}catch(e){o(e)}},"function"==typeof n.config&&(f=n.config(f,n,t)||f)!==h&&(l=f.abort,f.abort=function(){d=!0,l.call(this)}),null==u?f.send():"function"==typeof n.serialize?f.send(n.serialize(u)):u instanceof e.FormData?f.send(u):f.send(JSON.stringify(u))}),jsonp:l(function(t,n,o,l){var i=n.callbackName||"_mithril_"+Math.round(1e16*Math.random())+"_"+r++,a=e.document.createElement("script");e[i]=function(t){delete e[i],a.parentNode.removeChild(a),o(t)},a.onerror=function(){delete e[i],a.parentNode.removeChild(a),l(new Error("JSONP request failed"))},a.src=t+(t.indexOf("?")<0?"?":"&")+encodeURIComponent(n.callbackKey||"callback")+"="+encodeURIComponent(i),e.document.documentElement.appendChild(a)})}}(window,a,s.redraw),p=s,v=function(){return i.apply(this,arguments)};v.m=i,v.trust=i.trust,v.fragment=i.fragment,v.mount=p.mount;var m=i,y=a,g=function(e){if(""===e||null==e)return{};"?"===e.charAt(0)&&(e=e.slice(1));for(var t=e.split("&"),n={},r={},o=0;o-1&&u.pop();for(var c=0;c1&&"/"===l[l.length-1]&&(l=l.slice(0,-1))):l="/",{path:l,params:t<0?{}:g(e.slice(t+1,r))}},b=function(e){var t=w(e),n=Object.keys(t.params),r=[],o=new RegExp("^"+t.path.replace(/:([^\/.-]+)(\.{3}|\.(?!\.)|-)?|[\\^$*+.()|\[\]{}]/g,function(e,t,n){return null==t?"\\"+e:(r.push({k:t,r:"..."===n}),"..."===n?"(.*)":"."===n?"([^/]+)\\.":"([^/]+)"+(n||""))})+"$");return function(e){for(var l=0;l