├── .jshintrc ├── LICENSE.md ├── README.md ├── async.min.js ├── cssmenu.css ├── index.html ├── scanner.js └── style.css /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser" : true, 3 | "devel" : true, 4 | "esversion" : 9, 5 | 6 | "globals" : {} 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matthew Gizzi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snipe-IT Barcode Scanner 2 | 3 | ## Introduction 4 | 5 | The purpose of this project was to work around a few current limitations of [Snipe-IT](https://github.com/snipe/snipe-it) using the v4 API. This was done for my own needs, in hopes it gets depricated into future features of Snipe-IT. 6 | 7 | It doesn't have to look good, just needs to work. Built with barcode scanner usage in mind, something that adds a CR/LF at the end. 8 | 9 | The wish is to walk into a managed location (COMM room, conf room, etc), or user (cubicle, etc) and just start zapping. Regardless of the current state of the asset, checked in/out, and if the asset is assigned correctly, just update how it is today. Furthermore, mark the audit date as today and set the next audit date one year away for reporting purposes. 10 | 11 | ## Features 12 | 13 | * ~~Bulk checking out to user and location~~ () 14 | * Bulk updating asset user and location (regardless if checked in/out) 15 | * Bulk checking in assets 16 | * Ability to set next audit date to one year on updating assets 17 | 18 | ## Installation 19 | 20 | 1. Clone the project into 21 | 1. Fetch API key from 22 | 1. Login and add your API key, stored in browser local storage (Optionally add to scanner.js for single user mode) 23 | 1. Access with any modern web browser 24 | 25 | NOTE: Check folder/file ownership for the webserver (EX: chmod -R www-data:www-data ) 26 | -------------------------------------------------------------------------------- /async.min.js: -------------------------------------------------------------------------------- 1 | (function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.async={})})(this,function(e){'use strict';function t(e,...t){return(...a)=>e(...t,...a)}function a(e){return function(...t){var a=t.pop();return e.call(this,t,a)}}function n(e){setTimeout(e,0)}function i(e){return(t,...a)=>e(()=>t(...a))}function r(e){return u(e)?function(...t){const a=t.pop(),n=e.apply(this,t);return s(n,a)}:a(function(t,a){var n;try{n=e.apply(this,t)}catch(t){return a(t)}return n&&"function"==typeof n.then?s(n,a):void a(null,n)})}function s(e,t){return e.then(e=>{l(t,null,e)},e=>{l(t,e&&e.message?e:new Error(e))})}function l(e,t,a){try{e(t,a)}catch(e){Ee(t=>{throw t},e)}}function u(e){return"AsyncFunction"===e[Symbol.toStringTag]}function c(e){return"AsyncGenerator"===e[Symbol.toStringTag]}function p(e){return"function"==typeof e[Symbol.asyncIterator]}function d(e){if("function"!=typeof e)throw new Error("expected a function");return u(e)?r(e):e}function o(e,t=e.length){if(!t)throw new Error("arity is undefined");return function(...a){return"function"==typeof a[t-1]?e.apply(this,a):new Promise((n,i)=>{a[t-1]=(e,...t)=>e?i(e):void n(1{d(e).apply(i,a.concat(t))},n)});return n}}function f(e,t,a,n){t=t||[];var i=[],r=0,s=d(a);return e(t,(e,t,a)=>{var n=r++;s(e,(e,t)=>{i[n]=t,a(e)})},e=>{n(e,i)})}function y(e){return e&&"number"==typeof e.length&&0<=e.length&&0==e.length%1}function m(e){function t(...t){if(null!==e){var a=e;e=null,a.apply(this,t)}}return Object.assign(t,e),t}function g(e){return e[Symbol.iterator]&&e[Symbol.iterator]()}function k(e){var t=-1,a=e.length;return function(){return++t=t||c||l||(c=!0,e.next().then(({value:e,done:t})=>{if(!(u||l))return c=!1,t?(l=!0,void(0>=p&&n(null))):void(p++,a(e,d,r),d++,i())}).catch(s))}function r(e,t){return p-=1,u?void 0:e?s(e):!1===e?(l=!0,void(u=!0)):t===be||l&&0>=p?(l=!0,n(null)):void i()}function s(e){u||(c=!1,l=!0,n(e))}let l=!1,u=!1,c=!1,p=0,d=0;i()}function b(e,t,a){function n(e,t){!1===e&&(l=!0);!0===l||(e?a(e):(++r===s||t===be)&&a(null))}a=m(a);var i=0,r=0,{length:s}=e,l=!1;for(0===s&&a(null);i{t=e,a=n}),e}function M(e,t,a){function n(e,t){g.push(()=>l(e,t))}function i(){if(!h){if(0===g.length&&0===o)return a(null,p);for(;g.length&&oe()),i()}function l(e,t){if(!f){var n=x((t,...n)=>{if(o--,!1===t)return void(h=!0);if(2>n.length&&([n]=n),t){var i={};if(Object.keys(p).forEach(e=>{i[e]=p[e]}),i[e]=n,f=!0,y=Object.create(null),h)return;a(t,i)}else p[e]=n,s(e)});o++;var i=d(t[t.length-1]);1{const i=e[n];Array.isArray(i)&&0<=i.indexOf(t)&&a.push(n)}),a}"number"!=typeof t&&(a=t,t=null),a=m(a||_());var c=Object.keys(e).length;if(!c)return a(null);t||(t=c);var p={},o=0,h=!1,f=!1,y=Object.create(null),g=[],k=[],v={};return Object.keys(e).forEach(t=>{var a=e[t];if(!Array.isArray(a))return n(t,[a]),void k.push(t);var i=a.slice(0,a.length-1),s=i.length;return 0===s?(n(t,a),void k.push(t)):void(v[t]=s,i.forEach(l=>{if(!e[l])throw new Error("async.auto task `"+t+"` has a non-existent dependency `"+l+"` in "+i.join(", "));r(l,()=>{s--,0===s&&n(t,a)})}))}),function(){for(var e,t=0;k.length;)e=k.pop(),t++,u(e).forEach(e=>{0==--v[e]&&k.push(e)});if(t!==c)throw new Error("async.auto cannot execute tasks due to a recursive dependency")}(),i(),a[Fe]}function A(e){const t=e.toString().replace(ze,"");let a=t.match(Te);if(a||(a=t.match(Ce)),!a)throw new Error("could not parse args in autoInject\nSource:\n"+t);let[,n]=a;return n.replace(/\s/g,"").split(Pe).map(e=>e.replace(Re,"").trim())}function I(e,t){var a={};return Object.keys(e).forEach(t=>{function n(e,t){var a=i.map(t=>e[t]);a.push(t),d(r)(...a)}var i,r=e[t],s=u(r),l=!s&&1===r.length||s&&0===r.length;if(Array.isArray(r))i=[...r],r=i.pop(),a[t]=i.concat(0{r(e,a),t(...n)};f[e].push(a)}function r(e,t){return e?t?void(f[e]=f[e].filter(e=>e!==t)):f[e]=[]:Object.keys(f).forEach(e=>f[e]=[])}function s(e,...t){f[e].forEach(e=>e(...t))}function l(e,t,a,n){function i(e,...t){return e?a?s(e):r():1>=t.length?r(t[0]):void r(t)}if(null!=n&&"function"!=typeof n)throw new Error("task callback must be a function");k.started=!0;var r,s,l={data:e,callback:a?i:n||i};if(t?k._tasks.unshift(l):k._tasks.push(l),y||(y=!0,Ee(()=>{y=!1,k.process()})),a||!n)return new Promise((e,t)=>{r=e,s=t})}function u(e){return function(t,...a){o-=1;for(var n=0,r=e.length;ns("drain")),!0)}if(null==t)t=1;else if(0===t)throw new RangeError("Concurrency must not be zero");var p=d(e),o=0,h=[];const f={error:[],drain:[],saturated:[],unsaturated:[],empty:[]};var y=!1;const m=e=>t=>t?void(r(e),n(e,t)):new Promise((t,a)=>{i(e,(e,n)=>e?a(e):void t(n))});var g=!1,k={_tasks:new Ne,*[Symbol.iterator](){yield*k._tasks[Symbol.iterator]()},concurrency:t,payload:a,buffer:t/4,started:!1,paused:!1,push(e,t){return Array.isArray(e)?c(e)?void 0:e.map(e=>l(e,!1,!1,t)):l(e,!1,!1,t)},pushAsync(e,t){return Array.isArray(e)?c(e)?void 0:e.map(e=>l(e,!1,!0,t)):l(e,!1,!0,t)},kill(){r(),k._tasks.empty()},unshift(e,t){return Array.isArray(e)?c(e)?void 0:e.map(e=>l(e,!0,!1,t)):l(e,!0,!1,t)},unshiftAsync(e,t){return Array.isArray(e)?c(e)?void 0:e.map(e=>l(e,!0,!0,t)):l(e,!0,!0,t)},remove(e){k._tasks.remove(e)},process(){var e=Math.min;if(!g){for(g=!0;!k.paused&&o{t.apply(a,e.concat((e,...t)=>{n(e,t)}))},(e,t)=>n(e,...t)),n[Fe]}}function C(...e){return T(...e.reverse())}function P(...e){return function(...t){var a=t.pop();return a(null,...e)}}function R(e,t){return(a,n,i,r)=>{var s,l=!1;const u=d(i);a(n,(a,n,i)=>{u(a,(n,r)=>n||!1===n?i(n):e(r)&&!s?(l=!0,s=t(!0,a),i(null,be)):void i())},e=>e?r(e):void r(null,l?s:t(!1)))}}function z(e){return(t,...a)=>d(t)(...a,(t,...a)=>{"object"==typeof console&&(t?console.error&&console.error(t):console[e]&&a.forEach(t=>console[e](t)))})}function N(e,t,a){const n=d(t);return Ke(e,(...e)=>{const t=e.pop();n(...e,(e,a)=>t(e,!a))},a)}function V(e){return(t,a,n)=>e(t,n)}function Y(e){return u(e)?e:function(...t){var a=t.pop(),n=!0;t.push((...e)=>{n?Ee(()=>a(...e)):a(...e)}),e.apply(this,t),n=!1}}function q(e,t,a,n){var r=Array(t.length);e(t,(e,t,n)=>{a(e,(e,a)=>{r[t]=!!a,n(e)})},e=>{if(e)return n(e);for(var a=[],s=0;s{a(e,(a,r)=>a?n(a):void(r&&i.push({index:t,value:e}),n(a)))},e=>e?n(e):void n(null,i.sort((e,t)=>e.index-t.index).map(e=>e.value)))}function Q(e,t,a,n){var i=y(t)?q:D;return i(e,t,d(a),n)}function U(e,t,a){return lt(e,1/0,t,a)}function G(e,t,a){return lt(e,1,t,a)}function W(e,t,a){return ct(e,1/0,t,a)}function H(e,t,a){return ct(e,1,t,a)}function J(e,t=e=>e){var n=Object.create(null),r=Object.create(null),s=d(e),l=a((e,a)=>{var u=t(...e);u in n?Ee(()=>a(null,...n[u])):u in r?r[u].push(a):(r[u]=[a],s(...e,(e,...t)=>{e||(n[u]=t);var a=r[u];delete r[u];for(var s=0,c=a.length;s{a(e[0],t)},t,1)}function $(e){return(e<<1)+1}function ee(e){return(e+1>>1)-1}function te(e,t){return e.priority===t.priority?e.pushCount{}){if("function"!=typeof n)throw new Error("task callback must be a function");if(a.started=!0,Array.isArray(e)||(e=[e]),0===e.length&&a.idle())return Ee(()=>a.drain());for(var r,s=0,u=e.length;s{let n={};if(e&&(n.error=e),0=t.length&&([i]=t),n.value=i}a(null,n)}),t.apply(this,e)})}function re(e){var t;return Array.isArray(e)?t=e.map(ie):(t={},Object.keys(e).forEach(a=>{t[a]=ie.call(this,e[a])})),t}function se(e,t,a,n){const i=d(a);return Q(e,t,(e,t)=>{i(e,(e,a)=>{t(e,!a)})},n)}function le(e){return function(){return e}}function ue(e,t,a){function n(){r((e,...t)=>{!1===e||(e&&s++arguments.length&&"function"==typeof e?(a=t||_(),t=e):(ce(i,e),a=a||_()),"function"!=typeof t)throw new Error("Invalid arguments for async.retry");var r=d(t),s=1;return n(),a[Fe]}function ce(e,a){if("object"==typeof a)e.times=+a.times||gt,e.intervalFunc="function"==typeof a.interval?a.interval:le(+a.interval||kt),e.errorFilter=a.errorFilter;else if("number"==typeof a||"string"==typeof a)e.times=+a||gt;else throw new Error("Invalid arguments for async.retry")}function pe(e,t){t||(t=e,e=null);let n=e&&e.arity||t.length;u(t)&&(n+=1);var i=d(t);return a((t,a)=>{function r(e){i(...t,e)}return(t.length{var s,l=!1;a.push((...e)=>{l||(r(...e),clearTimeout(s))}),s=setTimeout(function(){var t=e.name||"anonymous",a=new Error("Callback function \""+t+"\" timed out.");a.code="ETIMEDOUT",n&&(a.info=n),l=!0,r(a)},t),i(...a)})}function he(e){for(var t=Array(e);e--;)t[e]=e;return t}function fe(e,t,a,n){var i=d(a);return qe(he(e),t,i,n)}function ye(e,t,a){return fe(e,1/0,t,a)}function me(e,t,a){return fe(e,1,t,a)}function ge(e,t,a,n){3>=arguments.length&&"function"==typeof t&&(n=a,a=t,t=Array.isArray(e)?[]:{}),n=m(n||_());var i=d(a);return Me(e,(e,a,n)=>{i(t,e,a,n)},e=>n(e,t)),n[Fe]}function ke(e){return(...t)=>(e.unmemoized||e)(...t)}function ve(e,t,a){const n=d(e);return bt(e=>n((t,a)=>e(t,!a)),t,a)}var Se,Le="function"==typeof setImmediate&&setImmediate,xe="object"==typeof process&&"function"==typeof process.nextTick;Se=Le?setImmediate:xe?process.nextTick:n;var Ee=i(Se);const be={};var Oe=e=>(t,a,n)=>{function i(e,t){if(!u)if(d-=1,e)l=!0,n(e);else if(!1===e)l=!0,u=!0;else{if(t===be||l&&0>=d)return l=!0,n(null);o||r()}}function r(){for(o=!0;d=d&&n(null));d+=1,a(t.value,t.key,x(i))}o=!1}if(n=m(n),0>=e)throw new RangeError("concurrency limit cannot be less than 1");if(!t)return n(null);if(c(t))return E(t,e,a,n);if(p(t))return E(t[Symbol.asyncIterator](),e,a,n);var s=L(t),l=!1,u=!1,d=0,o=!1;r()},_e=o(function(e,t,a,n){return Oe(t)(e,d(a),n)},4),Me=o(function(e,t,a){var n=y(e)?b:O;return n(e,d(t),a)},3),Ae=o(function(e,t,a){return f(Me,e,t,a)},3),Ie=h(Ae),je=o(function(e,t,a){return _e(e,1,t,a)},3),we=o(function(e,t,a){return f(je,e,t,a)},3),Be=h(we);const Fe=Symbol("promiseCallback");var Te=/^(?:async\s+)?(?:function)?\s*\w*\s*\(\s*([^)]+)\s*\)(?:\s*{)/,Ce=/^(?:async\s+)?\(?\s*([^)=]+)\s*\)?(?:\s*=>)/,Pe=/,/,Re=/(=.+)?(\s*)$/,ze=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;class Ne{constructor(){this.head=this.tail=null,this.length=0}removeLink(e){return e.prev?e.prev.next=e.next:this.head=e.next,e.next?e.next.prev=e.prev:this.tail=e.prev,e.prev=e.next=null,this.length-=1,e}empty(){for(;this.head;)this.shift();return this}insertAfter(e,t){t.prev=e,t.next=e.next,e.next?e.next.prev=t:this.tail=t,e.next=t,this.length+=1}insertBefore(e,t){t.prev=e.prev,t.next=e,e.prev?e.prev.next=t:this.head=t,e.prev=t,this.length+=1}unshift(e){this.head?this.insertBefore(this.head,e):j(this,e)}push(e){this.tail?this.insertAfter(this.tail,e):j(this,e)}shift(){return this.head&&this.removeLink(this.head)}pop(){return this.tail&&this.removeLink(this.tail)}toArray(){return[...this]}*[Symbol.iterator](){for(var e=this.head;e;)yield e.data,e=e.next}remove(e){for(var t=this.head;t;){var{next:a}=t;e(t)&&this.removeLink(t),t=a}return this}}var Ve,Ye=o(function(e,t,a,n){n=m(n);var r=d(a);return je(e,(e,a,n)=>{r(t,e,(e,a)=>{t=a,n(e)})},e=>n(e,t))},4),qe=o(function(e,t,a,n){return f(Oe(t),e,a,n)},4),De=o(function(e,t,a,n){var i=d(a);return qe(e,t,(e,t)=>{i(e,(e,...a)=>e?t(e):t(e,a))},(e,t)=>{for(var a=[],r=0;re,(e,t)=>t)(Me,e,t,a)},3),We=o(function(e,t,a,n){return R(e=>e,(e,t)=>t)(Oe(t),e,a,n)},4),He=o(function(e,t,a){return R(e=>e,(e,t)=>t)(Oe(1),e,t,a)},3),Je=z("dir"),Ke=o(function(e,t,a){function n(e,...t){return e?a(e):void(!1===e||(r=t,l(...t,i)))}function i(e,t){return e?a(e):!1===e?void 0:t?void s(n):a(null,...r)}a=x(a);var r,s=d(e),l=d(t);return i(null,!0)},3),Xe=o(function(e,t,a){return Me(e,V(d(t)),a)},3),Ze=o(function(e,t,a,n){return Oe(t)(e,V(d(a)),n)},4),$e=o(function(e,t,a){return Ze(e,1,t,a)},3),et=o(function(e,t,a){return R(e=>!e,e=>!e)(Me,e,t,a)},3),tt=o(function(e,t,a,n){return R(e=>!e,e=>!e)(Oe(t),e,a,n)},4),at=o(function(e,t,a){return R(e=>!e,e=>!e)(je,e,t,a)},3),nt=o(function(e,t,a){return Q(Me,e,t,a)},3),it=o(function(e,t,a,n){return Q(Oe(t),e,a,n)},4),rt=o(function(e,t,a){return Q(je,e,t,a)},3),st=o(function(e,t){function a(e){return e?n(e):void(!1===e||i(a))}var n=x(t),i=d(Y(e));return a()},2),lt=o(function(e,t,a,n){var i=d(a);return qe(e,t,(e,t)=>{i(e,(a,n)=>a?t(a):t(a,{key:n,val:e}))},(e,t)=>{for(var a={},{hasOwnProperty:r}=Object.prototype,s=0;s{r(e,t,(e,n)=>e?a(e):void(i[t]=n,a(e)))},e=>n(e,i))},4);Ve=xe?process.nextTick:Le?setImmediate:n;var pt=i(Ve),dt=o((e,t,a)=>{var n=y(t)?[]:{};e(t,(e,t,a)=>{d(e)((e,...i)=>{2>i.length&&([i]=i),n[t]=i,a(e)})},e=>a(e,n))},3);class ot{constructor(){this.heap=[],this.pushCount=Number.MIN_SAFE_INTEGER}get length(){return this.heap.length}empty(){return this.heap=[],this}percUp(e){for(let a;0e)(Me,e,t,a)},3),St=o(function(e,t,a,n){return R(Boolean,e=>e)(Oe(t),e,a,n)},4),Lt=o(function(e,t,a){return R(Boolean,e=>e)(je,e,t,a)},3),xt=o(function(e,t,a){function n(e,t){var n=e.criteria,a=t.criteria;return na?1:0}var i=d(t);return Ae(e,(e,t)=>{i(e,(a,n)=>a?t(a):void t(a,{value:e,criteria:n}))},(e,t)=>e?a(e):void a(null,t.sort(n).map(e=>e.value)))},3),Et=o(function(e,t){var a,n=null;return $e(e,(e,t)=>{d(e)((e,...i)=>!1===e?t(e):void(2>i.length?[a]=i:a=i,n=e,t(e?null:{})))},()=>t(n,a))}),bt=o(function(e,t,a){function n(e,...t){if(e)return a(e);l=t;!1===e||s(i)}function i(e,t){return e?a(e):!1===e?void 0:t?void r(n):a(null,...l)}a=x(a);var r=d(t),s=d(e),l=[];return s(i)},3),Ot=o(function(e,t){function a(t){var a=d(e[i++]);a(...t,x(n))}function n(n,...r){return!1===n?void 0:n||i===e.length?t(n,...r):void a(r)}if(t=m(t),!Array.isArray(e))return t(new Error("First argument to waterfall must be an array of functions"));if(!e.length)return t();var i=0;a([])});e.default={apply:t,applyEach:Ie,applyEachSeries:Be,asyncify:r,auto:M,autoInject:I,cargo:B,cargoQueue:F,compose:C,concat:Qe,concatLimit:De,concatSeries:Ue,constant:P,detect:Ge,detectLimit:We,detectSeries:He,dir:Je,doUntil:N,doWhilst:Ke,each:Xe,eachLimit:Ze,eachOf:Me,eachOfLimit:_e,eachOfSeries:je,eachSeries:$e,ensureAsync:Y,every:et,everyLimit:tt,everySeries:at,filter:nt,filterLimit:it,filterSeries:rt,forever:st,groupBy:U,groupByLimit:lt,groupBySeries:G,log:ut,map:Ae,mapLimit:qe,mapSeries:we,mapValues:W,mapValuesLimit:ct,mapValuesSeries:H,memoize:J,nextTick:pt,parallel:K,parallelLimit:X,priorityQueue:ae,queue:Z,race:ht,reduce:Ye,reduceRight:ne,reflect:ie,reflectAll:re,reject:ft,rejectLimit:yt,rejectSeries:mt,retry:ue,retryable:pe,seq:T,series:de,setImmediate:Ee,some:vt,someLimit:St,someSeries:Lt,sortBy:xt,timeout:oe,times:ye,timesLimit:fe,timesSeries:me,transform:ge,tryEach:Et,unmemoize:ke,until:ve,waterfall:Ot,whilst:bt,all:et,allLimit:tt,allSeries:at,any:vt,anyLimit:St,anySeries:Lt,find:Ge,findLimit:We,findSeries:He,flatMap:Qe,flatMapLimit:De,flatMapSeries:Ue,forEach:Xe,forEachSeries:$e,forEachLimit:Ze,forEachOf:Me,forEachOfSeries:je,forEachOfLimit:_e,inject:Ye,foldl:Ye,foldr:ne,select:nt,selectLimit:it,selectSeries:rt,wrapSync:r,during:bt,doDuring:Ke},e.apply=t,e.applyEach=Ie,e.applyEachSeries=Be,e.asyncify=r,e.auto=M,e.autoInject=I,e.cargo=B,e.cargoQueue=F,e.compose=C,e.concat=Qe,e.concatLimit=De,e.concatSeries=Ue,e.constant=P,e.detect=Ge,e.detectLimit=We,e.detectSeries=He,e.dir=Je,e.doUntil=N,e.doWhilst=Ke,e.each=Xe,e.eachLimit=Ze,e.eachOf=Me,e.eachOfLimit=_e,e.eachOfSeries=je,e.eachSeries=$e,e.ensureAsync=Y,e.every=et,e.everyLimit=tt,e.everySeries=at,e.filter=nt,e.filterLimit=it,e.filterSeries=rt,e.forever=st,e.groupBy=U,e.groupByLimit=lt,e.groupBySeries=G,e.log=ut,e.map=Ae,e.mapLimit=qe,e.mapSeries=we,e.mapValues=W,e.mapValuesLimit=ct,e.mapValuesSeries=H,e.memoize=J,e.nextTick=pt,e.parallel=K,e.parallelLimit=X,e.priorityQueue=ae,e.queue=Z,e.race=ht,e.reduce=Ye,e.reduceRight=ne,e.reflect=ie,e.reflectAll=re,e.reject=ft,e.rejectLimit=yt,e.rejectSeries=mt,e.retry=ue,e.retryable=pe,e.seq=T,e.series=de,e.setImmediate=Ee,e.some=vt,e.someLimit=St,e.someSeries=Lt,e.sortBy=xt,e.timeout=oe,e.times=ye,e.timesLimit=fe,e.timesSeries=me,e.transform=ge,e.tryEach=Et,e.unmemoize=ke,e.until=ve,e.waterfall=Ot,e.whilst=bt,e.all=et,e.allLimit=tt,e.allSeries=at,e.any=vt,e.anyLimit=St,e.anySeries=Lt,e.find=Ge,e.findLimit=We,e.findSeries=He,e.flatMap=Qe,e.flatMapLimit=De,e.flatMapSeries=Ue,e.forEach=Xe,e.forEachSeries=$e,e.forEachLimit=Ze,e.forEachOf=Me,e.forEachOfSeries=je,e.forEachOfLimit=_e,e.inject=Ye,e.foldl=Ye,e.foldr=ne,e.select=nt,e.selectLimit=it,e.selectSeries=rt,e.wrapSync=r,e.during=bt,e.doDuring=Ke,Object.defineProperty(e,"__esModule",{value:!0})}); -------------------------------------------------------------------------------- /cssmenu.css: -------------------------------------------------------------------------------- 1 | #cssmenu ul, 2 | #cssmenu li, 3 | #cssmenu span, 4 | #cssmenu a { 5 | margin: 0; 6 | padding: 0; 7 | position: relative; 8 | } 9 | #cssmenu { 10 | line-height: 1; 11 | border-radius: 5px 5px 0 0; 12 | -moz-border-radius: 5px 5px 0 0; 13 | -webkit-border-radius: 5px 5px 0 0; 14 | background: #141414; 15 | background: -moz-linear-gradient(top, #333333 0%, #141414 100%); 16 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #141414)); 17 | background: -webkit-linear-gradient(top, #333333 0%, #141414 100%); 18 | background: -o-linear-gradient(top, #333333 0%, #141414 100%); 19 | background: -ms-linear-gradient(top, #333333 0%, #141414 100%); 20 | background: linear-gradient(to bottom, #333333 0%, #141414 100%); 21 | border-bottom: 2px solid #0fa1e0; 22 | width: auto; 23 | } 24 | #cssmenu:after, 25 | #cssmenu ul:after { 26 | content: ''; 27 | display: block; 28 | clear: both; 29 | } 30 | #cssmenu a { 31 | background: #141414; 32 | background: -moz-linear-gradient(top, #333333 0%, #141414 100%); 33 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #141414)); 34 | background: -webkit-linear-gradient(top, #333333 0%, #141414 100%); 35 | background: -o-linear-gradient(top, #333333 0%, #141414 100%); 36 | background: -ms-linear-gradient(top, #333333 0%, #141414 100%); 37 | background: linear-gradient(to bottom, #333333 0%, #141414 100%); 38 | color: #ffffff; 39 | display: block; 40 | font-family: Helvetica, Arial, Verdana, sans-serif; 41 | padding: 19px 20px; 42 | text-decoration: none; 43 | } 44 | #cssmenu ul { 45 | list-style: none; 46 | } 47 | #cssmenu > ul > li { 48 | display: inline-block; 49 | float: left; 50 | margin: 0; 51 | } 52 | #cssmenu.align-center { 53 | text-align: center; 54 | } 55 | #cssmenu.align-center > ul > li { 56 | float: none; 57 | } 58 | #cssmenu.align-center ul ul { 59 | text-align: left; 60 | } 61 | #cssmenu.align-right > ul { 62 | float: right; 63 | } 64 | #cssmenu.align-right ul ul { 65 | text-align: right; 66 | } 67 | #cssmenu > ul > li > a { 68 | color: #ffffff; 69 | font-size: 12px; 70 | } 71 | #cssmenu > ul > li:hover:after { 72 | content: ''; 73 | display: block; 74 | width: 0; 75 | height: 0; 76 | position: absolute; 77 | left: 50%; 78 | bottom: 0; 79 | border-left: 10px solid transparent; 80 | border-right: 10px solid transparent; 81 | border-bottom: 10px solid #0fa1e0; 82 | margin-left: -10px; 83 | } 84 | #cssmenu > ul > li:first-child > a { 85 | border-radius: 5px 0 0 0; 86 | -moz-border-radius: 5px 0 0 0; 87 | -webkit-border-radius: 5px 0 0 0; 88 | } 89 | #cssmenu.align-right > ul > li:first-child > a, 90 | #cssmenu.align-center > ul > li:first-child > a { 91 | border-radius: 0; 92 | -moz-border-radius: 0; 93 | -webkit-border-radius: 0; 94 | } 95 | #cssmenu.align-right > ul > li:last-child > a { 96 | border-radius: 0 5px 0 0; 97 | -moz-border-radius: 0 5px 0 0; 98 | -webkit-border-radius: 0 5px 0 0; 99 | } 100 | #cssmenu > ul > li.active > a, 101 | #cssmenu > ul > li:hover > a { 102 | color: #ffffff; 103 | box-shadow: inset 0 0 3px #000000; 104 | -moz-box-shadow: inset 0 0 3px #000000; 105 | -webkit-box-shadow: inset 0 0 3px #000000; 106 | background: #070707; 107 | background: -moz-linear-gradient(top, #262626 0%, #070707 100%); 108 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #262626), color-stop(100%, #070707)); 109 | background: -webkit-linear-gradient(top, #262626 0%, #070707 100%); 110 | background: -o-linear-gradient(top, #262626 0%, #070707 100%); 111 | background: -ms-linear-gradient(top, #262626 0%, #070707 100%); 112 | background: linear-gradient(to bottom, #262626 0%, #070707 100%); 113 | } 114 | #cssmenu .has-sub { 115 | z-index: 1; 116 | } 117 | #cssmenu .has-sub:hover > ul { 118 | display: block; 119 | } 120 | #cssmenu .has-sub ul { 121 | display: none; 122 | position: absolute; 123 | width: 200px; 124 | top: 100%; 125 | left: 0; 126 | } 127 | #cssmenu.align-right .has-sub ul { 128 | left: auto; 129 | right: 0; 130 | } 131 | #cssmenu .has-sub ul li { 132 | *margin-bottom: -1px; 133 | } 134 | #cssmenu .has-sub ul li a { 135 | background: #0fa1e0; 136 | border-bottom: 1px dotted #31b7f1; 137 | font-size: 11px; 138 | filter: none; 139 | display: block; 140 | line-height: 120%; 141 | padding: 10px; 142 | color: #ffffff; 143 | } 144 | #cssmenu .has-sub ul li:hover a { 145 | background: #0c7fb0; 146 | } 147 | #cssmenu ul ul li:hover > a { 148 | color: #ffffff; 149 | } 150 | #cssmenu .has-sub .has-sub:hover > ul { 151 | display: block; 152 | } 153 | #cssmenu .has-sub .has-sub ul { 154 | display: none; 155 | position: absolute; 156 | left: 100%; 157 | top: 0; 158 | } 159 | #cssmenu.align-right .has-sub .has-sub ul, 160 | #cssmenu.align-right ul ul ul { 161 | left: auto; 162 | right: 100%; 163 | } 164 | #cssmenu .has-sub .has-sub ul li a { 165 | background: #0c7fb0; 166 | border-bottom: 1px dotted #31b7f1; 167 | } 168 | #cssmenu .has-sub .has-sub ul li a:hover { 169 | background: #0a6d98; 170 | } 171 | #cssmenu ul ul li.last > a, 172 | #cssmenu ul ul li:last-child > a, 173 | #cssmenu ul ul ul li.last > a, 174 | #cssmenu ul ul ul li:last-child > a, 175 | #cssmenu .has-sub ul li:last-child > a, 176 | #cssmenu .has-sub ul li.last > a { 177 | border-bottom: 0; 178 | } 179 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Snipe-IT Bulk Scanner 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /scanner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Configuration options. 4 | var siteUrl = "http://snipeit"; 5 | var apiToken = ""; 6 | var tabsArray = ['Check-in', 'Checkout/Updating (User)', 'Checkout/Updating (Location)', 'Load List']; 7 | 8 | 9 | // "Private" global variables. Do not touch. 10 | var blankMsg = "\tEmpty value, skipping"; 11 | var apiPrefix = "/api/v1"; 12 | var mainDomElement = "#scanner"; 13 | var locations = new Set(); 14 | var staff = new Set(); 15 | var notifyUsers = []; 16 | var version = "0.2" 17 | 18 | function initScanner(callback) { 19 | 20 | var page = document.createElement("div"); 21 | page.id = mainDomElement; 22 | 23 | async.series([ 24 | loadAPIKey, 25 | initNav, 26 | checkAuth, 27 | initTabBody, 28 | initPage 29 | ], 30 | function(err, results) { 31 | if (err) { 32 | console.log(err); 33 | } else { 34 | console.log("Initialized Page"); 35 | // console.log(results); 36 | } 37 | }); 38 | 39 | // 40 | // // Get the element with id="defaultOpen" and click on it 41 | // document.getElementById("defaultOpen").click(); 42 | } 43 | 44 | function initTabBody(callback) { 45 | var tabDiv = document.createElement("div"); 46 | tabDiv.id = "tabDiv"; 47 | document.querySelector(mainDomElement).appendChild(tabDiv); 48 | 49 | callback(null, "Initialized Body"); 50 | } 51 | 52 | function initNav(callback) { 53 | document.querySelector(mainDomElement).appendChild(createTabs()); 54 | callback(null, "Initialized Nav"); 55 | } 56 | 57 | function checkAuth(callback) { 58 | httpGet(apiPrefix + "/hardware/1", function(response) { 59 | if (response.serial != '') { 60 | document.getElementById("login").innerHTML = ""; 61 | callback(null, "Authenticated"); 62 | } 63 | }, function(error) { 64 | document.getElementById("actions").innerHTML = ""; 65 | callback("Not authorized"); 66 | }); 67 | } 68 | 69 | function createTabs() { 70 | var options = document.createElement("div"); 71 | options.id = "cssmenu"; 72 | var ulist = document.createElement("ul"); 73 | 74 | // Home 75 | var home = document.createElement("li"); 76 | home.id = "home"; 77 | var link = document.createElement("a"); 78 | link.href = "#"; 79 | var span = document.createElement("span"); 80 | span.innerText = "Home"; 81 | link.addEventListener("click", function() { 82 | closeAction(); 83 | }); 84 | 85 | link.appendChild(span); 86 | home.appendChild(link); 87 | ulist.appendChild(home); 88 | 89 | // Actions Menu 90 | var actions = document.createElement("li"); 91 | actions.id = "actions"; 92 | actions.className = "has-sub"; 93 | var actionUList = document.createElement("ul"); 94 | link = document.createElement("a"); 95 | link.href = "#"; 96 | span = document.createElement("span"); 97 | span.innerText = "Actions"; 98 | 99 | link.appendChild(span); 100 | actions.appendChild(link); 101 | actions.appendChild(actionUList); 102 | ulist.appendChild(actions); 103 | 104 | // Action List 105 | for (var tab in tabsArray) { 106 | var actionLi = document.createElement("li"); 107 | actionLi.id = tabsArray[tab]; 108 | link = document.createElement("a"); 109 | link.href = "#"; 110 | span = document.createElement("span"); 111 | span.innerText = tabsArray[tab]; 112 | link.addEventListener("click", function(e) { 113 | openAction(e); 114 | }); 115 | 116 | link.appendChild(span); 117 | actionLi.appendChild(link); 118 | actionUList.appendChild(actionLi); 119 | } 120 | 121 | // Settings 122 | // var settings = document.createElement("li"); 123 | // settings.id = "settings"; 124 | // link = document.createElement("a"); 125 | // link.href = "#"; 126 | // span = document.createElement("span"); 127 | // span.innerText = "Settings"; 128 | // 129 | // link.appendChild(span); 130 | // settings.appendChild(link); 131 | // ulist.appendChild(settings); 132 | 133 | // Login 134 | var login = document.createElement("li"); 135 | login.id = "login"; 136 | link = document.createElement("a"); 137 | link.href = "#"; 138 | span = document.createElement("span"); 139 | span.innerText = "Login"; 140 | link.addEventListener("click", function() { 141 | createModal("", "Store API Key"); 142 | var modalContent = document.getElementById("textmodal"); 143 | var inputAPI = document.createElement("textarea"); 144 | inputAPI.id = "textAPI"; 145 | inputAPI.cols = "50"; 146 | inputAPI.rows = "10"; 147 | 148 | var btnSave = document.createElement("button"); 149 | btnSave.id = "btnSave"; 150 | btnSave.innerText = "Save"; 151 | btnSave.type = "button"; 152 | btnSave.addEventListener("click", function() { 153 | saveAPIKey(document.getElementById("textAPI")); 154 | }); 155 | 156 | modalContent.appendChild(inputAPI); 157 | modalContent.appendChild(btnSave); 158 | }); 159 | 160 | link.appendChild(span); 161 | login.appendChild(link); 162 | ulist.appendChild(login); 163 | 164 | // End 165 | options.appendChild(ulist); 166 | 167 | return options; 168 | } 169 | 170 | function initPage(callback){ 171 | async.series([ 172 | loadLocations, 173 | loadStaff, 174 | createLocationTab, 175 | createUserTab, 176 | createCheckIn, 177 | createLoadList 178 | ], 179 | function(err, result) { 180 | if (err) { 181 | console.log(err); 182 | } 183 | }); 184 | callback(null, "Done"); 185 | } 186 | 187 | function loadLocations(callback) { 188 | httpGet(apiPrefix + "/locations?order=asc&sort=name", function(response) { 189 | var locationNames = response.rows; 190 | locationNames.forEach(function(location) { 191 | var option = new Set(); 192 | if (option === "") { 193 | return; 194 | } 195 | option.text = location.name; 196 | option.value = location.id; 197 | locations.add(option); 198 | }); 199 | callback(); 200 | }); 201 | } 202 | 203 | function loadStaff(callback){ 204 | httpGet(apiPrefix + "/users?limit=200&order=asc&sort=name", function(response) { 205 | var userNames = response.rows; 206 | userNames.forEach(function(user) { 207 | var option = new Set(); 208 | if (option === "") { 209 | return; 210 | } 211 | option.value = user.id; 212 | option.text = user.name; 213 | staff.add(option); 214 | }); 215 | callback(); 216 | }); 217 | } 218 | 219 | function createLocationTab(callback) { 220 | var tab = tabsArray[3]; 221 | var elem = document.createElement("div"); 222 | elem.className = "tab_body"; 223 | elem.id = tab; 224 | document.getElementById("tabDiv").appendChild(elem); 225 | 226 | elem.appendChild(createInput()); 227 | elem.appendChild(createAudit()); 228 | elem.appendChild(createLocationsList()); 229 | elem.appendChild(createSubmit()); 230 | 231 | callback(); 232 | } 233 | 234 | function createSubmit(tab) { 235 | var submit = document.createElement("button"); 236 | submit.id = "btnSubmit"; 237 | submit.innerText = "Submit"; 238 | submit.type = "button"; 239 | submit.addEventListener("click", function() { 240 | doTab(tab); 241 | }); 242 | 243 | return submit; 244 | } 245 | 246 | function createLocationsList() { 247 | var locationsDiv = document.createElement("div"); 248 | locationsDiv.className = "locations"; 249 | 250 | var locationLabel = document.createElement("p"); 251 | locationLabel.innerText = "Check-out to the following"; 252 | 253 | var locationList = document.createElement("select"); 254 | locationList.className = "selectList"; 255 | locationList.disabled = true; 256 | 257 | locations.forEach(function(set) { 258 | var option = document.createElement("option"); 259 | option.text = set.text; 260 | option.value = set.value; 261 | locationList.appendChild(option); 262 | }); 263 | locationList.disabled = false; 264 | 265 | locationsDiv.appendChild(locationLabel); 266 | locationsDiv.appendChild(locationList); 267 | 268 | return locationsDiv; 269 | } 270 | 271 | function createLocationTab(callback) { 272 | var tab = tabsArray[2]; 273 | var elem = document.createElement("div"); 274 | elem.className = "tab_body"; 275 | elem.id = tab; 276 | document.getElementById("tabDiv").appendChild(elem); 277 | 278 | elem.appendChild(createInput()); 279 | elem.appendChild(createAudit()); 280 | elem.appendChild(createLocationsList()); 281 | elem.appendChild(createSubmit(tab)); 282 | 283 | callback(); 284 | } 285 | 286 | function createUserTab(callback) { 287 | var tab = tabsArray[1]; 288 | var elem = document.createElement("div"); 289 | elem.className = "tab_body"; 290 | elem.id = tab; 291 | document.getElementById("tabDiv").appendChild(elem); 292 | 293 | elem.appendChild(createInput()); 294 | elem.appendChild(createAudit()); 295 | elem.appendChild(createOpenUsers()); 296 | elem.appendChild(createStaffList()); 297 | elem.appendChild(createSubmit(tab)); 298 | 299 | callback(); 300 | } 301 | 302 | function createOpenUsers() { 303 | var userPageDiv = document.createElement("div"); 304 | userPageDiv.className = "userPage"; 305 | var userPageLabel = document.createElement("p"); 306 | userPageLabel.innerText = "Open users page?"; 307 | 308 | var checkbox = document.createElement("input"); 309 | checkbox.type = "checkbox"; 310 | checkbox.name = "userPage"; 311 | checkbox.checked = true; 312 | checkbox.id = "userPage"; 313 | 314 | userPageDiv.appendChild(userPageLabel); 315 | userPageDiv.appendChild(checkbox); 316 | 317 | return userPageDiv; 318 | } 319 | 320 | function createStaffList() { 321 | var usersDiv = document.createElement("div"); 322 | usersDiv.className = "users"; 323 | 324 | var userLabel = document.createElement("p"); 325 | userLabel.innerText = "Check-out to the following"; 326 | 327 | var userList = document.createElement("select"); 328 | userList.className = "selectList"; 329 | userList.disabled = true; 330 | 331 | staff.forEach(function(set) { 332 | var option = document.createElement("option"); 333 | option.text = set.text; 334 | option.value = set.value; 335 | userList.appendChild(option); 336 | }); 337 | userList.disabled = false; 338 | 339 | usersDiv.appendChild(userLabel); 340 | usersDiv.appendChild(userList); 341 | 342 | return usersDiv; 343 | } 344 | 345 | function checkBlank(assetTag, callback) { 346 | if (assetTag == "") { 347 | callback(blankMsg); 348 | } else { 349 | callback(null, assetTag); 350 | } 351 | } 352 | 353 | function checkIfDeployed(searchAssetCallback, compareID, callback) { 354 | /* 355 | 0 = not deployed 356 | 1 = deployed, but needs updating 357 | 2 = deployed, already assigned 358 | */ 359 | if (searchAssetCallback.status_label.status_meta == "deployed") { 360 | if (searchAssetCallback.assigned_to.id == compareID) { 361 | console.log("\tAlready assigned correctly"); 362 | callback(null, 2); 363 | } else { 364 | console.log("\tAlready assigned, updating"); 365 | if (searchAssetCallback.assigned_to.type == "user") { 366 | notifyUsers.push(searchAssetCallback.assigned_to.id); 367 | } 368 | callback(null, 1); 369 | } 370 | } else { 371 | callback(null, 0); 372 | } 373 | } 374 | 375 | function checkInAsset(assetID, callback) { 376 | var dataObj = { 377 | }; 378 | httpPost(apiPrefix + "/hardware/" + assetID + "/checkin", dataObj, function(response) { 379 | if (response.status == "error") { 380 | callback(response.messages); 381 | return; 382 | } 383 | console.log("\tChecked in " + assetID); 384 | callback(null); 385 | }); 386 | } 387 | 388 | function checkOutAsset(assetID, dataObj, callback) { 389 | httpPost(apiPrefix + "/hardware/" + assetID + "/checkout", dataObj, function(response) { 390 | if (response.status == "error") { 391 | callback(response.messages); 392 | return; 393 | } 394 | console.log("\tChecked out " + assetID); 395 | callback(null); 396 | }); 397 | } 398 | 399 | function createAudit() { 400 | var auditDiv = document.createElement("div"); 401 | auditDiv.className = "checkbox"; 402 | var auditLabel = document.createElement("p"); 403 | auditLabel.innerText = "Update audit date? (1yr)"; 404 | 405 | var checkbox = document.createElement("input"); 406 | checkbox.type = "checkbox"; 407 | checkbox.name = "audit"; 408 | checkbox.checked = true; 409 | checkbox.id = "audit"; 410 | 411 | auditDiv.appendChild(auditLabel); 412 | auditDiv.appendChild(checkbox); 413 | 414 | return auditDiv; 415 | } 416 | 417 | function createInput() { 418 | var inputDiv = document.createElement("div"); 419 | inputDiv.className = "input"; 420 | 421 | var inputLabel = document.createElement("p"); 422 | inputLabel.innerText = "Barcodes here\nOne per line"; 423 | 424 | var inputArea = document.createElement("textarea"); 425 | inputArea.id = "inputarea"; 426 | inputArea.cols = "10"; 427 | inputArea.rows = "10"; 428 | 429 | inputDiv.appendChild(inputLabel); 430 | inputDiv.appendChild(inputArea); 431 | 432 | return inputDiv; 433 | } 434 | 435 | function createModal(name, textHeader) { 436 | var divModal = document.createElement("div"); 437 | divModal.className = "modal"; 438 | divModal.id = "modal" + name; 439 | 440 | var modalHeader = document.createElement("div"); 441 | modalHeader.className = "modal-header"; 442 | 443 | var contentModal = document.createElement("div"); 444 | contentModal.className = "modal-content"; 445 | var contentText = document.createElement("p"); 446 | contentText.id = "textmodal"; 447 | 448 | modalHeader = document.createElement("h4"); 449 | modalHeader.innerText = textHeader; 450 | 451 | contentModal.appendChild(modalHeader); 452 | contentModal.appendChild(contentText); 453 | 454 | var modalClose = document.createElement("span"); 455 | modalClose.className = "close"; 456 | modalClose.innerText = "Close"; 457 | modalClose.addEventListener("click", function() { 458 | var modal = document.querySelector(".modal"); 459 | if (modal !== null) { 460 | modal.remove(); 461 | } 462 | }); 463 | contentModal.appendChild(modalClose); 464 | divModal.appendChild(contentModal); 465 | divModal.style.display = "block"; 466 | 467 | document.querySelector(mainDomElement).appendChild(divModal); 468 | } 469 | 470 | function disableInput(elem) { 471 | elem.querySelectorAll("select.selectList")[0].disabled = true; 472 | elem.querySelectorAll("button#btnSubmit")[0].disabled = true; 473 | } 474 | 475 | function doAudit(dataObj, callback) { 476 | httpPost(apiPrefix + "/hardware/audit", dataObj, function() { 477 | console.log("\tAsset marked for audit at " + dataObj.next_audit_date); 478 | callback(null); 479 | }); 480 | } 481 | 482 | function doTab(tab) { 483 | switch (tab) { 484 | case tabsArray[0]: 485 | doCheckin(tab); 486 | break; 487 | case tabsArray[1]: 488 | doUser(tab); 489 | break; 490 | case tabsArray[2]: 491 | doLocation(tab); 492 | break; 493 | case tabsArray[3]: 494 | doLoadList(tab); 495 | break; 496 | } 497 | } 498 | 499 | function doCheckin(tab) { 500 | var currentTabElements; 501 | var assetID; 502 | var elems = document.getElementById("tabDiv").querySelectorAll(".tab_body"); 503 | for (var i in elems) { 504 | if (elems[i].style.display != "none") { // Find the only active one 505 | currentTabElements = elems[i]; 506 | break; 507 | } 508 | } 509 | var assetArray = getAssetIDArray(currentTabElements.querySelectorAll("textarea#inputarea")[0].value); 510 | async.eachOfLimit(assetArray, 1, function(assetTag, index, assetArrayCallback) { 511 | //disableInput(elem); 512 | currentTabElements.querySelectorAll("#btnSubmit")[0].disabled = true; 513 | async.waterfall([ 514 | function(callback) { 515 | console.log("Trying asset tag " + assetTag); 516 | callback(null, assetTag); 517 | }, 518 | checkBlank, 519 | searchAsset, 520 | function(searchAssetCallback, callback) { 521 | assetID = searchAssetCallback.id; 522 | callback(null, searchAssetCallback, 0); // zero to force checkIfDeployed condition 1 523 | }, 524 | checkIfDeployed, 525 | function(deployedState, callback) { 526 | if (deployedState > 0) { 527 | callback(null, assetID); 528 | } else { 529 | callback("OK"); 530 | } 531 | }, 532 | checkInAsset, 533 | function(callback) { 534 | if (currentTabElements.querySelectorAll("#audit")[0].checked) { 535 | var today = new Date(); 536 | var dd = today.getDate(); 537 | var mm = today.getMonth()+1; //January is 0! 538 | var yyyy = today.getFullYear(); 539 | var nextyear = yyyy + 1; 540 | 541 | if(dd<10) { 542 | dd = '0'+dd; 543 | } 544 | 545 | if(mm<10) { 546 | mm = '0'+mm; 547 | } 548 | 549 | var dataObj = { 550 | "asset_tag": assetTag, 551 | "note": "", 552 | "next_audit_date": nextyear + "-" + mm + "-" + dd 553 | }; 554 | doAudit(dataObj, callback); 555 | } else { 556 | callback(null); 557 | } 558 | }, 559 | function(callback) { 560 | callback(null, "Done"); 561 | } 562 | ], function(error, result) { 563 | if (error === blankMsg) { 564 | // Just to break out of waterfall 565 | console.log(error); 566 | error = undefined; 567 | } else if (error === "OK") { 568 | console.log("Asset already checked-in"); 569 | error = undefined; 570 | } else if (error) { 571 | console.log(error); 572 | } else { 573 | console.log(result); 574 | } 575 | assetArrayCallback(error, result); 576 | }); 577 | }, function(error, result) { 578 | if (error) { 579 | console.log("Fatal - Stopped checking"); 580 | alert("Something went wrong\nCheck console for error"); 581 | } else { 582 | // Clean up 583 | currentTabElements.querySelectorAll("textarea#inputarea")[0].value = ""; 584 | printUserPages(currentTabElements); 585 | console.log("Done everything, cleared inputs"); 586 | } 587 | currentTabElements.querySelectorAll("#btnSubmit")[0].disabled = false; 588 | }); 589 | } 590 | 591 | function doLoadList(tab) { 592 | var currentTabElements; 593 | var elems = document.getElementById("tabDiv").querySelectorAll(".tab_body"); 594 | for (var i in elems) { 595 | if (elems[i].style.display != "none") { // Find the only active one 596 | currentTabElements = elems[i]; 597 | break; 598 | } 599 | } 600 | 601 | var assetArray = getAssetIDArray(currentTabElements.querySelectorAll("textarea#inputarea")[0].value); 602 | createModal(tabsArray[3], "Save text as a CSV file"); 603 | async.eachOfLimit(assetArray, 1, function(assetTag, index, assetArrayCallback) { 604 | //disableInput(elem); 605 | async.waterfall([ 606 | function(callback) { 607 | console.log("Reading asset tag " + assetTag); 608 | callback(null, assetTag); 609 | }, 610 | checkBlank, 611 | function(assetTag, callback) { 612 | httpGet(apiPrefix + "/hardware/bytag/" + assetTag, function(response) { 613 | var itemCSV = assetTag; 614 | if (currentTabElements.querySelectorAll("#category")[0].checked) { itemCSV += "," + response.category.name; } 615 | if (currentTabElements.querySelectorAll("#make")[0].checked) { itemCSV += "," + response.manufacturer.name; } 616 | if (currentTabElements.querySelectorAll("#model")[0].checked) { itemCSV += "," + response.model_number; } 617 | itemCSV += "," + response.serial; 618 | var modalContent = document.querySelector(mainDomElement).querySelectorAll("#textmodal")[0]; 619 | var text = document.createTextNode(itemCSV); 620 | modalContent.appendChild(text); 621 | modalContent.appendChild(document.createElement("br")); 622 | var modal = document.getElementById("modal" + tabsArray[3]); 623 | modal.style.display = "block"; 624 | 625 | callback(null); 626 | }); 627 | }, 628 | function(callback) { 629 | callback(null, "Done"); 630 | } 631 | ], function(error, result) { 632 | if (error === blankMsg) { 633 | // Just to break out of waterfall 634 | console.log(error); 635 | error = undefined; 636 | } else if (error) { 637 | console.log(error); 638 | } else { 639 | console.log(result); 640 | } 641 | assetArrayCallback(error, result); 642 | }); 643 | }, function(error, result) { 644 | if (error) { 645 | console.log("Fatal - Stopped checking"); 646 | alert("Something went wrong\nCheck console for error"); 647 | } else { 648 | currentTabElements.querySelectorAll("textarea#inputarea")[0].value = ""; 649 | console.log("Done everything, cleared inputs"); 650 | } 651 | //enableInput(elem); 652 | }); 653 | } 654 | 655 | function doLocation(tab) { 656 | var currentTabElements; 657 | var elems = document.getElementById("tabDiv").querySelectorAll(".tab_body"); 658 | for (var i in elems) { 659 | if (elems[i].style.display != "none") { // Find the only active one 660 | currentTabElements = elems[i]; 661 | break; 662 | } 663 | } 664 | 665 | var assetArray = getAssetIDArray(currentTabElements.querySelectorAll("textarea#inputarea")[0].value); 666 | var locationObj = currentTabElements.querySelectorAll("select.selectList")[0]; 667 | var locationID = locationObj.value; 668 | var assetID = ""; 669 | async.eachOfLimit(assetArray, 1, function(assetTag, index, assetArrayCallback) { 670 | disableInput(currentTabElements); 671 | async.waterfall([ 672 | function(callback) { 673 | console.log("Trying asset tag " + assetTag); 674 | callback(null, assetTag); 675 | }, 676 | checkBlank, 677 | searchAsset, 678 | function(searchAssetCallback, callback) { 679 | assetID = searchAssetCallback.id; 680 | callback(null, searchAssetCallback, locationID); 681 | }, 682 | checkIfDeployed, 683 | function(deployedState, callback) { 684 | var dataObj = { 685 | "id": assetID, 686 | "checkout_to_type": "location", 687 | "assigned_location": locationID 688 | }; 689 | switch(deployedState) { 690 | case 0: 691 | checkOutAsset(assetID, dataObj, callback); 692 | break; 693 | case 1: 694 | checkInAsset(assetID, function() { 695 | checkOutAsset(assetID, dataObj, callback); 696 | }); 697 | break; 698 | case 2: 699 | callback(null); 700 | } 701 | }, 702 | function(callback) { 703 | if (currentTabElements.querySelectorAll("#audit")[0].checked) { 704 | var today = new Date(); 705 | var dd = today.getDate(); 706 | var mm = today.getMonth()+1; //January is 0! 707 | var yyyy = today.getFullYear(); 708 | var nextyear = yyyy + 1; 709 | 710 | if(dd<10) { 711 | dd = '0'+dd; 712 | } 713 | 714 | if(mm<10) { 715 | mm = '0'+mm; 716 | } 717 | 718 | var dataObj = { 719 | "asset_tag": assetTag, 720 | "note": "", 721 | "location_id": locationID, 722 | "next_audit_date": nextyear + "-" + mm + "-" + dd 723 | }; 724 | doAudit(dataObj, callback); 725 | } else { 726 | callback(null); 727 | } 728 | }, 729 | function(callback) { 730 | callback(null, "Done"); 731 | } 732 | ], function(error, result) { 733 | if (error === blankMsg) { 734 | // Just to break out of waterfall 735 | console.log(error); 736 | error = undefined; 737 | } else if (error) { 738 | console.log(error); 739 | } else { 740 | console.log(result); 741 | } 742 | assetArrayCallback(error, result); 743 | }); 744 | }, function(error, result) { 745 | if (error) { 746 | console.log("Fatal - Stopped checking"); 747 | alert("Something went wrong\nCheck console for error"); 748 | } else { 749 | // Clean up 750 | currentTabElements.querySelectorAll("textarea#inputarea")[0].value = ""; 751 | console.log("Done everything, cleared inputs"); 752 | } 753 | enableInput(currentTabElements); 754 | }); 755 | } 756 | 757 | function doUser(tab) { 758 | var currentTabElements; 759 | var elems = document.getElementById("tabDiv").querySelectorAll(".tab_body"); 760 | for (var i in elems) { 761 | if (elems[i].style.display != "none") { // Find the only active one 762 | currentTabElements = elems[i]; 763 | break; 764 | } 765 | } 766 | 767 | var assetArray = getAssetIDArray(currentTabElements.querySelectorAll("textarea#inputarea")[0].value); 768 | var userObj = currentTabElements.querySelectorAll("select.selectList")[0]; 769 | var userID = userObj.value; 770 | var assetID = ""; 771 | async.eachOfLimit(assetArray, 1, function(assetTag, index, assetArrayCallback) { 772 | disableInput(currentTabElements); 773 | async.waterfall([ 774 | function(callback) { 775 | console.log("Trying asset tag " + assetTag); 776 | callback(null, assetTag); 777 | }, 778 | checkBlank, 779 | searchAsset, 780 | function(searchAssetCallback, callback) { 781 | assetID = searchAssetCallback.id; 782 | callback(null, searchAssetCallback, userID); 783 | }, 784 | checkIfDeployed, 785 | function(deployedState, callback) { 786 | var dataObj = { 787 | "id": assetID, 788 | "checkout_to_type": "user", 789 | "assigned_user": userID 790 | }; 791 | switch(deployedState) { 792 | case 0: 793 | checkOutAsset(assetID, dataObj, callback); 794 | break; 795 | case 1: 796 | checkInAsset(assetID, function() { 797 | checkOutAsset(assetID, dataObj, callback); 798 | }); 799 | break; 800 | case 2: 801 | callback(null); 802 | } 803 | }, 804 | function(callback) { 805 | if (currentTabElements.querySelectorAll("#audit")[0].checked) { 806 | var today = new Date(); 807 | var dd = today.getDate(); 808 | var mm = today.getMonth()+1; //January is 0! 809 | var yyyy = today.getFullYear(); 810 | var nextyear = yyyy + 1; 811 | 812 | if(dd<10) { 813 | dd = '0'+dd; 814 | } 815 | 816 | if(mm<10) { 817 | mm = '0'+mm; 818 | } 819 | 820 | var dataObj = { 821 | "asset_tag": assetTag, 822 | "note": "", 823 | "next_audit_date": nextyear + "-" + mm + "-" + dd 824 | }; 825 | doAudit(dataObj, callback); 826 | } else { 827 | callback(null); 828 | } 829 | }, 830 | function(callback) { 831 | callback(null, "Done"); 832 | } 833 | ], function(error, result) { 834 | if (error === blankMsg) { 835 | // Just to break out of waterfall 836 | console.log(error); 837 | error = undefined; 838 | } else if (error) { 839 | console.log(error); 840 | } else { 841 | console.log(result); 842 | } 843 | assetArrayCallback(error, result); 844 | }); 845 | }, function(error, result) { 846 | if (error) { 847 | console.log("Fatal - Stopped checking"); 848 | alert("Something went wrong\nCheck console for error"); 849 | } else { 850 | // Clean up 851 | currentTabElements.querySelectorAll("textarea#inputarea")[0].value = ""; 852 | notifyUsers.push(userID); 853 | printUserPages(currentTabElements); 854 | 855 | console.log("Done everything, cleared inputs"); 856 | } 857 | enableInput(currentTabElements); 858 | }); 859 | } 860 | 861 | function enableInput(elem) { 862 | elem.querySelectorAll("select.selectList")[0].disabled = false; 863 | elem.querySelectorAll("button#btnSubmit")[0].disabled = false; 864 | } 865 | 866 | function searchAsset(assetTag, callback) { 867 | httpGet(apiPrefix + "/hardware/bytag/" + assetTag, function(response) { 868 | if (response.id <= 0) { 869 | callback("Invalid tag " + assetTag); 870 | } else { 871 | callback(null, response); 872 | } 873 | }); 874 | } 875 | 876 | function getAssetIDArray(inputList) { 877 | var inputArray = inputList.split('\n'); 878 | if (inputList == "") { 879 | alert("Need inputs"); 880 | return; 881 | } else { 882 | return inputArray; 883 | } 884 | } 885 | 886 | function httpGet(url, successCallback, errorCallback) { 887 | httpRequest("GET", url, "", successCallback, errorCallback); 888 | } 889 | 890 | function httpPost(url, dataObj, successCallback, errorCallback) { 891 | httpRequest("Post", url, dataObj, successCallback, errorCallback); 892 | } 893 | 894 | function httpPatch(url, dataObj, successCallback, errorCallback) { 895 | httpRequest("Patch", url, dataObj, successCallback, errorCallback); 896 | } 897 | 898 | function httpRequest(method, url, dataObj, successCallback, errorCallback) { 899 | var payload = false; 900 | if (dataObj !== "") { 901 | payload = dataObj; 902 | } 903 | var xhr = new XMLHttpRequest(); 904 | xhr.withCredentials = true; 905 | xhr.onreadystatechange = function() { 906 | if (this.readyState == this.DONE) { 907 | var response = this.responseText; 908 | if (response === "") { 909 | var msg = "No response for request " + url; 910 | console.warn(msg); 911 | alert(msg); 912 | return; 913 | } 914 | 915 | var obj = JSON.parse(response); 916 | if (this.status == 200) { 917 | return successCallback(obj); 918 | } else if (this.status == 401) { 919 | alert(obj.error); 920 | if (errorCallback !== undefined) { 921 | errorCallback(obj.error); 922 | } 923 | return; 924 | } 925 | 926 | if (obj.error !== null) { 927 | console.error(obj.message); 928 | 929 | if (errorCallback !== undefined) { 930 | errorCallback(obj); 931 | } else { 932 | alert(obj.message); 933 | } 934 | } 935 | } 936 | }; 937 | 938 | xhr.open(method, siteUrl + url, true); 939 | xhr.setRequestHeader("Accept", "application/json"); 940 | xhr.setRequestHeader("Content-Type", "application/json"); 941 | xhr.setRequestHeader("Authorization", 'Bearer ' + apiToken); 942 | xhr.send(JSON.stringify(payload)); 943 | } 944 | 945 | function createCheckIn(callback) { 946 | var tab = tabsArray[0]; 947 | var elem = document.createElement("div"); 948 | elem.className = "tab_body"; 949 | elem.id = tab; 950 | document.getElementById("tabDiv").appendChild(elem); 951 | 952 | elem.appendChild(createInput()); 953 | elem.appendChild(createOpenUsers()); 954 | elem.appendChild(createAudit()); 955 | elem.appendChild(createSubmit(tab)); 956 | 957 | callback(); 958 | } 959 | 960 | function createLoadList(callback) { 961 | var tab = tabsArray[3]; 962 | var elem = document.createElement("div"); 963 | elem.className = "tab_body"; 964 | elem.id = tab; 965 | document.getElementById("tabDiv").appendChild(elem); 966 | 967 | elem.appendChild(createInput()); 968 | 969 | var extraFieldsDiv = document.createElement("div"); 970 | extraFieldsDiv.className = "outputfields"; 971 | var extraFieldsLabel = document.createElement("p"); 972 | extraFieldsLabel.innerText = "Add the following to the output?"; 973 | extraFieldsDiv.appendChild(extraFieldsLabel); 974 | 975 | var addCategory = document.createElement("div"); 976 | addCategory.className = "checkbox"; 977 | var checkboxCategory = document.createElement("input"); 978 | checkboxCategory.type = "checkbox"; 979 | checkboxCategory.name = "category"; 980 | checkboxCategory.checked = true; 981 | checkboxCategory.id = "category"; 982 | var labelCategory = document.createElement("p"); 983 | labelCategory.innerText = "Category"; 984 | addCategory.appendChild(checkboxCategory); 985 | addCategory.appendChild(labelCategory); 986 | 987 | var addMake = document.createElement("div"); 988 | addMake.className = "checkbox"; 989 | var checkboxMake = document.createElement("input"); 990 | checkboxMake.type = "checkbox"; 991 | checkboxMake.name = "make"; 992 | checkboxMake.checked = true; 993 | checkboxMake.id = "make"; 994 | var labelMake = document.createElement("p"); 995 | labelMake.innerText = "Make"; 996 | addMake.appendChild(checkboxMake); 997 | addMake.appendChild(labelMake); 998 | 999 | var addModel = document.createElement("div"); 1000 | addModel.className = "checkbox"; 1001 | var checkboxModel = document.createElement("input"); 1002 | checkboxModel.type = "checkbox"; 1003 | checkboxModel.name = "model"; 1004 | checkboxModel.checked = true; 1005 | checkboxModel.id = "model"; 1006 | var labelModel = document.createElement("p"); 1007 | labelModel.innerText = "Model"; 1008 | addModel.appendChild(checkboxModel); 1009 | addModel.appendChild(labelModel); 1010 | 1011 | var submit = document.createElement("button"); 1012 | submit.id = "btnSubmit"; 1013 | submit.innerText = "Submit"; 1014 | submit.type = "button"; 1015 | submit.addEventListener("click", function() { 1016 | doLoadList(elem, tab); 1017 | }); 1018 | 1019 | elem.appendChild(extraFieldsDiv); 1020 | elem.appendChild(addCategory); 1021 | elem.appendChild(addMake); 1022 | elem.appendChild(addModel); 1023 | elem.appendChild(submit); 1024 | 1025 | callback(); 1026 | } 1027 | 1028 | function loadAPIKey(callback) { 1029 | if (apiToken != "") { 1030 | callback(null, "Load API Key"); 1031 | } else { 1032 | apiToken = localStorage.getItem("API_Token"); 1033 | callback(null, "Load API Key"); 1034 | } 1035 | } 1036 | 1037 | function openAction(evt) { 1038 | // Get all elements with class="tab_body" and hide them 1039 | var allTabBody = document.getElementsByClassName("tab_body"); 1040 | for (let i of allTabBody) { 1041 | i.style.display = "none"; 1042 | } 1043 | 1044 | var en = allTabBody.namedItem(evt.currentTarget.innerText); 1045 | en.style.display = "block"; 1046 | } 1047 | 1048 | function closeAction(evt) { 1049 | // Get all elements with class="tab_body" and hide them 1050 | var allTabBody = document.getElementsByClassName("tab_body"); 1051 | for (let i of allTabBody) { 1052 | i.style.display = "none"; 1053 | } 1054 | } 1055 | 1056 | function saveAPIKey(key) { 1057 | localStorage.setItem("API_Token", key.value); 1058 | window.location.reload(); 1059 | } 1060 | 1061 | function printUserPages(currentTabElements) { 1062 | // Get list without duplicates 1063 | var uniqueUserList = Array.from(new Set(notifyUsers)); 1064 | 1065 | if (currentTabElements.querySelectorAll("#userPage")[0].checked) { 1066 | if (uniqueUserList.length > 1) { 1067 | window.alert("Opening user pages including whom the assets were assigned to"); 1068 | } 1069 | uniqueUserList.forEach(function(user) { 1070 | window.open(siteUrl + "/users/" + user + "/print", '_blank'); 1071 | }); 1072 | } 1073 | notifyUsers = []; 1074 | } 1075 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #009688; /* fallback for old browsers */ 3 | font-family: sans-serif; 4 | margin: 0; 5 | } 6 | 7 | #scanner { 8 | } 9 | 10 | p { 11 | margin: 5px; 12 | } 13 | 14 | select { 15 | height: 25px; 16 | } 17 | 18 | /* ======== Tabs ======== */ 19 | /* Style the nav */ 20 | #nav { 21 | display: flex; 22 | justify-content: flex-start; 23 | } 24 | 25 | /* Style the tab content */ 26 | .tab_body { 27 | display: none; /* Hidden by default */ 28 | } 29 | /* ======== Modal ======== */ 30 | /* The Modal (background) */ 31 | .modal { 32 | display: none; /* Hidden by default */ 33 | position: fixed; /* Stay in place */ 34 | z-index: 1; /* Sit on top */ 35 | left: 0; 36 | top: 0; 37 | width: 100%; /* Full width */ 38 | height: 100%; /* Full height */ 39 | overflow: auto; /* Enable scroll if needed */ 40 | background-color: rgb(0,0,0); /* Fallback color */ 41 | background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ 42 | } 43 | 44 | /* Modal Content/Box */ 45 | .modal-content { 46 | background-color: #fefefe; 47 | margin: 15% auto; /* 15% from the top and centered */ 48 | padding: 20px; 49 | border: 1px solid #888; 50 | width: 80%; /* Could be more or less, depending on screen size */ 51 | } 52 | 53 | /* The Close Button */ 54 | .close { 55 | color: #aaa; 56 | float: right; 57 | font-size: 28px; 58 | font-weight: bold; 59 | } 60 | 61 | .close:hover, 62 | .close:focus { 63 | color: black; 64 | text-decoration: none; 65 | cursor: pointer; 66 | } 67 | --------------------------------------------------------------------------------