From f69107b60b642bf407190e3e5476e3fd3193e861 Mon Sep 17 00:00:00 2001 From: lightszentip Date: Thu, 6 Jan 2022 02:14:33 +0100 Subject: [PATCH] Create Present * add more functions to create present --- .gitignore | 1 + .travis.yml | 7 - README.md | 70 +- app/Http/Controllers/FileController.php | 57 + app/Http/Controllers/PresentsController.php | 25 +- app/Models/PresentLinks.php | 12 + app/Models/Presents.php | 7 + package.json | 2 + public/js/holder.js | 4611 +++++++++++++++++++ resources/js/app.js | 1 + resources/js/bootstrap.js | 5 +- resources/lang/de/messages-page.php | 26 +- resources/lang/de/presents.php | 14 + resources/sass/app.scss | 6 + resources/views/layouts/app.blade.php | 5 +- resources/views/navigation-menu.blade.php | 2 +- resources/views/presents-create.blade.php | 131 +- resources/views/presents.blade.php | 10 +- routes/web.php | 4 + 19 files changed, 4918 insertions(+), 78 deletions(-) delete mode 100644 .travis.yml create mode 100644 app/Http/Controllers/FileController.php create mode 100644 public/js/holder.js create mode 100644 resources/lang/de/presents.php diff --git a/.gitignore b/.gitignore index 3df674c..096cc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ yarn-error.log /public/js/app.js package-lock.json composer.lock +public/files diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 08cd9a6..0000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: php -php: - - 7.1 - - 7.2 - - 5.6 -before_script: - - composer self-update diff --git a/README.md b/README.md index 0667441..3eae76c 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,66 @@ # Presentlist / Geschenkeliste # -## Setup +## Current Version -To create default user and permission +[![GitHub version](https://badge.fury.io/gh/lightszentip%2Fgiftlist.svg)](https://badge.fury.io/gh/lightszentip%2Fgiftlist) +## Installation +### Installation over Release Zip + +* unzip +* set www root to public/ +* run install_sql.sql on your database +* create .env file in the root dir and set the settings + +#### Upgrade + +* unzip to new/ +* replace all files from new or delete and insert all files from new (exclude .env) +* run: ````shell php artisan migrate php artisan db:seed ```` +### Setup over repo +* clone the repository +* create .env file in the root dir and set the settings +* ```composer update``` +* ```npm install``` +* ```npm run dev``` +* start with ```php artisan server``` or set your www root to public +* install db with ```php artisan migrate``` -http://lightszentip.github.io/giftlist/ -[![Build Status](https://travis-ci.org/lightszentip/giftlist.svg?branch=master)](https://travis-ci.org/lightszentip/giftlist) +#### To create default user and permission + +````shell +php artisan db:seed +```` + +#### Upgrade + +* git pull +* ```composer update``` +* ```npm install``` +* ```npm run dev``` +````shell +php artisan migrate +php artisan db:seed +```` + +## Other +http://lightszentip.github.io/giftlist/ => !! For English => english is under german !! Presentlist ist eine Geschenkliste auf der man seine Wünsche zur Hochzeit, Geburtstag oder anderen Anlässen auflisten kann. Dabei kann man einen Titel, Beschreibung und auch ein Bild zum Geschnenk angeben, sowie Links zu Händlern oder dem Produkt angeben. Wenn sich dann jemand ein Geschenk von der Liste nimmt, ist es für die anderen nicht mehr sichtbar. Man kann gewählte Geschenke aber auch wieder freigeben und der Administrator sieht nicht wer sich welches Geschenk genommen hat. -![](https://raw.github.com/lightszentip/giftlist/gh-pages/screenshot01.PNG) ![](https://raw.github.com/lightszentip/giftlist/gh-pages/screenshot03.PNG) ![](https://raw.github.com/lightszentip/giftlist/gh-pages/screenshot04.PNG) ## Funktionen ## -- Install Wizard - Geschenkeliste - Detail Ansicht von Geschenken - Geschenk auswählen @@ -60,7 +96,6 @@ The app presentlist is show the wishes from wedding, birthday or from other occa ## Functions ## -- Install Wizard - list with presents - detail view of present - use a present @@ -77,7 +112,7 @@ The app presentlist is show the wishes from wedding, birthday or from other occa ## Requirements ## -- PHP 5.3.7 or higher +- PHP 7.4 or higher - MySQL Database (other database only support by you edit the source files) - PHP PDO Support for MySQL active (extension=php_pdo_mysql.dll) @@ -87,22 +122,3 @@ The app presentlist is show the wishes from wedding, birthday or from other occa Unpack the zip file to the target dir and open the url in your browser. Follow the steps of install wizard. Delete the setup folder and change the password and email address of admin account. If you have a question, problems or feedback then you can send a mail or create a new issue. - -## Verwendete PHP Libraries / Used lbraries: ## - -- Rain TPL => [https://github.com/rainphp/raintpl](https://github.com/rainphp/raintpl) -- php-i18n von Philipp Schröer=> [https://github.com/Philipp15b/php-i18n?files=1](https://github.com/Philipp15b/php-i18n?files=1) -- Medoo DB => [http://medoo.in/api/new](http://medoo.in/api/new) -- PHP Setup / Installer Script => [http://www.effiziente-webprogrammierung.info/php-scripts/setup-wizard-installer](http://www.effiziente-webprogrammierung.info/php-scripts/setup-wizard-installer) -- PHP Login von http://www.php-login.net/ => [https://github.com/panique/php-login](https://github.com/panique/php-login) -> Version Advanced -- KLogger von Kenny => [http://codefury.net/projects/klogger/](http://codefury.net/projects/klogger/) -- Kint => [http://raveren.github.io/kint/](http://raveren.github.io/kint/) - -## Verwendete JS/HTML/CSS Komponeneten / Used JS/HTML/CSS components: ## - -- TableSorter von Christian Bach => [http://mottie.github.io/tablesorter/](https://github.com/Mottie/tablesorter) -- Bootstrap 3.1 => [http://getbootstrap.com/](http://getbootstrap.com/) - - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lightszentip/giftlist/trend.png)](https://bitdeli.com/free "Bitdeli Badge") - diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php new file mode 100644 index 0000000..f9f147b --- /dev/null +++ b/app/Http/Controllers/FileController.php @@ -0,0 +1,57 @@ +all(), [ + 'file' => 'required|mimes:png,jpg,jpeg,pdf|max:2048' + ]); + + if ($validator->fails()) { + + $data['success'] = 0; + $data['error'] = $validator->errors()->first('file');// Error response + + }else{ + try { + if($request->file('file')) { + + $file = $request->file('file'); + $filename = time().'_'.$file->getClientOriginalName(); + $filename = hash('sha256', $filename).'.'.$request->file('file')->getClientOriginalExtension(); + // File upload location + $location = 'files'; + + // Upload file + $file->move($location,$filename); + + // Response + $data['success'] = 1; + $data['message'] = 'Uploaded Successfully!'; + $data['link'] = $filename; + + }else{ + // Response + $data['success'] = 0; + $data['message'] = 'File not uploaded.'; + } + } catch (\Exception $e) { + Log::warning('File upload ',$e); + $data['success'] = 0; + $data['message'] = 'File not uploaded.'; + } + + } + + return response()->json($data); + } +} diff --git a/app/Http/Controllers/PresentsController.php b/app/Http/Controllers/PresentsController.php index 5e04459..27920fb 100644 --- a/app/Http/Controllers/PresentsController.php +++ b/app/Http/Controllers/PresentsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Http\Requests\FormCreatePresentRequest; +use App\Models\PresentLinks; use App\Models\Presents; use Illuminate\Http\Request; @@ -22,14 +23,30 @@ public function createPresent(Request $request) { if(empty($user) || !$user->hasPermissionTo('createPresent')) { abort(403,'no permission'); } - return view('presents-create'); + return view('presents-create',['present' => new Presents()]); } public function storePresent(FormCreatePresentRequest $request) { - $present = new Presents($request->all()); - $present->save(); + $links = $request->input('links'); + + $present = Presents::withoutEvents(function () use ($request,$links) { + $present = new Presents($request->all()); + $present->save(); + if(!empty($links)) { + $newLinks = array(); + foreach ($links as $link) { + if(!is_null($link)) { + $saveLink = new PresentLinks(['link'=>$link,'presents_id'=>$present->id]); + $saveLink->save(); + } + } + + } + $present->save(); + return $present; + }); return redirect()->route('presents.show') - ->with('success','Present was created successfully.'); + ->with('success',__('presents.create_success',['title'=>$present->title, 'id'=> $present->id])); } } diff --git a/app/Models/PresentLinks.php b/app/Models/PresentLinks.php index 27a9aa8..15f2bc1 100644 --- a/app/Models/PresentLinks.php +++ b/app/Models/PresentLinks.php @@ -9,6 +9,18 @@ class PresentLinks extends Model { use HasFactory; + /** + * {@inheritdoc} + */ + protected $guarded = [ + 'id', 'created_at', 'updated_at', + ]; + + public function __construct(array $attributes = array()) + { + parent::__construct($attributes); + } + public function present() { return $this->belongsTo(Presents::class); diff --git a/app/Models/Presents.php b/app/Models/Presents.php index 2583a68..e4a7853 100644 --- a/app/Models/Presents.php +++ b/app/Models/Presents.php @@ -9,6 +9,13 @@ class Presents extends Model { use HasFactory; + /** + * Handle events after all transactions are committed. + * + * @var bool + */ + public $afterCommit = true; + public function links() { return $this->hasMany(PresentLinks::class); diff --git a/package.json b/package.json index c0a755b..a14a692 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "sass-loader": "^12.4.0" }, "dependencies": { + "dropzone": "^5.9.3", + "holderjs": "^2.9.9", "light-switch-bootstrap": "^0.1.3" } } diff --git a/public/js/holder.js b/public/js/holder.js new file mode 100644 index 0000000..c9a8be2 --- /dev/null +++ b/public/js/holder.js @@ -0,0 +1,4611 @@ +/*! + +Holder - client side image placeholders +Version 2.9.9+jl7z +© 2021 Ivan Malopinsky - https://imsky.co + +Site: http://holderjs.com +Issues: https://github.com/imsky/holder/issues +License: MIT + +*/ +(function (window) { + if (!window.document) return; + var document = window.document; + + //https://github.com/inexorabletash/polyfill/blob/master/web.js + if (!document.querySelectorAll) { + document.querySelectorAll = function (selectors) { + var style = document.createElement('style'), elements = [], element; + document.documentElement.firstChild.appendChild(style); + document._qsa = []; + + style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}'; + window.scrollBy(0, 0); + style.parentNode.removeChild(style); + + while (document._qsa.length) { + element = document._qsa.shift(); + element.style.removeAttribute('x-qsa'); + elements.push(element); + } + document._qsa = null; + return elements; + }; + } + + if (!document.querySelector) { + document.querySelector = function (selectors) { + var elements = document.querySelectorAll(selectors); + return (elements.length) ? elements[0] : null; + }; + } + + if (!document.getElementsByClassName) { + document.getElementsByClassName = function (classNames) { + classNames = String(classNames).replace(/^|\s+/g, '.'); + return document.querySelectorAll(classNames); + }; + } + + //https://github.com/inexorabletash/polyfill + // ES5 15.2.3.14 Object.keys ( O ) + // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys + if (!Object.keys) { + Object.keys = function (o) { + if (o !== Object(o)) { throw TypeError('Object.keys called on non-object'); } + var ret = [], p; + for (p in o) { + if (Object.prototype.hasOwnProperty.call(o, p)) { + ret.push(p); + } + } + return ret; + }; + } + + // ES5 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] ) + // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach + if (!Array.prototype.forEach) { + Array.prototype.forEach = function (fun /*, thisp */) { + if (this === void 0 || this === null) { throw TypeError(); } + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") { throw TypeError(); } + + var thisp = arguments[1], i; + for (i = 0; i < len; i++) { + if (i in t) { + fun.call(thisp, t[i], i, t); + } + } + }; + } + + //https://github.com/inexorabletash/polyfill/blob/master/web.js + (function (global) { + var B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + global.atob = global.atob || function (input) { + input = String(input); + var position = 0, + output = [], + buffer = 0, bits = 0, n; + + input = input.replace(/\s/g, ''); + if ((input.length % 4) === 0) { input = input.replace(/=+$/, ''); } + if ((input.length % 4) === 1) { throw Error('InvalidCharacterError'); } + if (/[^+/0-9A-Za-z]/.test(input)) { throw Error('InvalidCharacterError'); } + + while (position < input.length) { + n = B64_ALPHABET.indexOf(input.charAt(position)); + buffer = (buffer << 6) | n; + bits += 6; + + if (bits === 24) { + output.push(String.fromCharCode((buffer >> 16) & 0xFF)); + output.push(String.fromCharCode((buffer >> 8) & 0xFF)); + output.push(String.fromCharCode(buffer & 0xFF)); + bits = 0; + buffer = 0; + } + position += 1; + } + + if (bits === 12) { + buffer = buffer >> 4; + output.push(String.fromCharCode(buffer & 0xFF)); + } else if (bits === 18) { + buffer = buffer >> 2; + output.push(String.fromCharCode((buffer >> 8) & 0xFF)); + output.push(String.fromCharCode(buffer & 0xFF)); + } + + return output.join(''); + }; + + global.btoa = global.btoa || function (input) { + input = String(input); + var position = 0, + out = [], + o1, o2, o3, + e1, e2, e3, e4; + + if (/[^\x00-\xFF]/.test(input)) { throw Error('InvalidCharacterError'); } + + while (position < input.length) { + o1 = input.charCodeAt(position++); + o2 = input.charCodeAt(position++); + o3 = input.charCodeAt(position++); + + // 111111 112222 222233 333333 + e1 = o1 >> 2; + e2 = ((o1 & 0x3) << 4) | (o2 >> 4); + e3 = ((o2 & 0xf) << 2) | (o3 >> 6); + e4 = o3 & 0x3f; + + if (position === input.length + 2) { + e3 = 64; e4 = 64; + } + else if (position === input.length + 1) { + e4 = 64; + } + + out.push(B64_ALPHABET.charAt(e1), + B64_ALPHABET.charAt(e2), + B64_ALPHABET.charAt(e3), + B64_ALPHABET.charAt(e4)); + } + + return out.join(''); + }; + }(window)); + + //https://gist.github.com/jimeh/332357 + if (!Object.prototype.hasOwnProperty){ + /*jshint -W001, -W103 */ + Object.prototype.hasOwnProperty = function(prop) { + var proto = this.__proto__ || this.constructor.prototype; + return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]); + }; + /*jshint +W001, +W103 */ + } + + // @license http://opensource.org/licenses/MIT + // copyright Paul Irish 2015 + + + // Date.now() is supported everywhere except IE8. For IE8 we use the Date.now polyfill + // github.com/Financial-Times/polyfill-service/blob/master/polyfills/Date.now/polyfill.js + // as Safari 6 doesn't have support for NavigationTiming, we use a Date.now() timestamp for relative values + + // if you want values similar to what you'd get with real perf.now, place this towards the head of the page + // but in reality, you're just getting the delta between now() calls, so it's not terribly important where it's placed + + + (function(){ + + if ('performance' in window === false) { + window.performance = {}; + } + + Date.now = (Date.now || function () { // thanks IE8 + return new Date().getTime(); + }); + + if ('now' in window.performance === false){ + + var nowOffset = Date.now(); + + if (performance.timing && performance.timing.navigationStart){ + nowOffset = performance.timing.navigationStart; + } + + window.performance.now = function now(){ + return Date.now() - nowOffset; + }; + } + + })(); + + //requestAnimationFrame polyfill for older Firefox/Chrome versions + if (!window.requestAnimationFrame) { + if (window.webkitRequestAnimationFrame && window.webkitCancelAnimationFrame) { + //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-webkit.js + (function (global) { + global.requestAnimationFrame = function (callback) { + return webkitRequestAnimationFrame(function () { + callback(global.performance.now()); + }); + }; + + global.cancelAnimationFrame = global.webkitCancelAnimationFrame; + }(window)); + } else if (window.mozRequestAnimationFrame && window.mozCancelAnimationFrame) { + //https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/requestAnimationFrame/polyfill-moz.js + (function (global) { + global.requestAnimationFrame = function (callback) { + return mozRequestAnimationFrame(function () { + callback(global.performance.now()); + }); + }; + + global.cancelAnimationFrame = global.mozCancelAnimationFrame; + }(window)); + } else { + (function (global) { + global.requestAnimationFrame = function (callback) { + return global.setTimeout(callback, 1000 / 60); + }; + + global.cancelAnimationFrame = global.clearTimeout; + })(window); + } + } +})(this); + +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Holder"] = factory(); + else + root["Holder"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + /* + Holder.js - client side image placeholders + (c) 2012-2020 Ivan Malopinsky - https://imsky.co + */ + + module.exports = __webpack_require__(1); + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/* + Holder.js - client side image placeholders + (c) 2012-2020 Ivan Malopinsky - http://imsky.co + */ + + //Libraries and functions + var onDomReady = __webpack_require__(2); + var querystring = __webpack_require__(3); + + var SceneGraph = __webpack_require__(6); + var utils = __webpack_require__(7); + var SVG = __webpack_require__(8); + var DOM = __webpack_require__(9); + var Color = __webpack_require__(10); + var constants = __webpack_require__(11); + + var svgRenderer = __webpack_require__(12); + var sgCanvasRenderer = __webpack_require__(27); + + var extend = utils.extend; + var dimensionCheck = utils.dimensionCheck; + + //Constants and definitions + var SVG_NS = constants.svg_ns; + + var Holder = { + version: constants.version, + + /** + * Adds a theme to default settings + * + * @param {string} name Theme name + * @param {Object} theme Theme object, with foreground, background, size, font, and fontweight properties. + */ + addTheme: function(name, theme) { + name != null && theme != null && (App.settings.themes[name] = theme); + delete App.vars.cache.themeKeys; + return this; + }, + + /** + * Appends a placeholder to an element + * + * @param {string} src Placeholder URL string + * @param el A selector or a reference to a DOM node + */ + addImage: function(src, el) { + //todo: use jquery fallback if available for all QSA references + var nodes = DOM.getNodeArray(el); + nodes.forEach(function (node) { + var img = DOM.newEl('img'); + var domProps = {}; + domProps[App.setup.dataAttr] = src; + DOM.setAttr(img, domProps); + node.appendChild(img); + }); + return this; + }, + + /** + * Sets whether or not an image is updated on resize. + * If an image is set to be updated, it is immediately rendered. + * + * @param {Object} el Image DOM element + * @param {Boolean} value Resizable update flag value + */ + setResizeUpdate: function(el, value) { + if (el.holderData) { + el.holderData.resizeUpdate = !!value; + if (el.holderData.resizeUpdate) { + updateResizableElements(el); + } + } + }, + + /** + * Runs Holder with options. By default runs Holder on all images with "holder.js" in their source attributes. + * + * @param {Object} userOptions Options object, can contain domain, themes, images, and bgnodes properties + */ + run: function(userOptions) { + //todo: split processing into separate queues + userOptions = userOptions || {}; + var engineSettings = {}; + var options = extend(App.settings, userOptions); + + App.vars.preempted = true; + App.vars.dataAttr = options.dataAttr || App.setup.dataAttr; + + engineSettings.renderer = options.renderer ? options.renderer : App.setup.renderer; + if (App.setup.renderers.join(',').indexOf(engineSettings.renderer) === -1) { + engineSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html'); + } + + var images = DOM.getNodeArray(options.images); + var bgnodes = DOM.getNodeArray(options.bgnodes); + var stylenodes = DOM.getNodeArray(options.stylenodes); + var objects = DOM.getNodeArray(options.objects); + + engineSettings.stylesheets = []; + engineSettings.svgXMLStylesheet = true; + engineSettings.noFontFallback = !!options.noFontFallback; + engineSettings.noBackgroundSize = !!options.noBackgroundSize; + + stylenodes.forEach(function (styleNode) { + if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') { + var href = styleNode.attributes.href.value; + //todo: write isomorphic relative-to-absolute URL function + var proxyLink = DOM.newEl('a'); + proxyLink.href = href; + var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search; + engineSettings.stylesheets.push(stylesheetURL); + } + }); + + bgnodes.forEach(function (bgNode) { + //Skip processing background nodes if getComputedStyle is unavailable, since only modern browsers would be able to use canvas or SVG to render to background + if (!global.getComputedStyle) return; + var backgroundImage = global.getComputedStyle(bgNode, null).getPropertyValue('background-image'); + var dataBackgroundImage = bgNode.getAttribute('data-background-src'); + var rawURL = dataBackgroundImage || backgroundImage; + + var holderURL = null; + var holderString = options.domain + '/'; + var holderStringIndex = rawURL.indexOf(holderString); + + if (holderStringIndex === 0) { + holderURL = rawURL; + } else if (holderStringIndex === 1 && rawURL[0] === '?') { + holderURL = rawURL.slice(1); + } else { + var fragment = rawURL.substr(holderStringIndex).match(/([^"]*)"?\)/); + if (fragment !== null) { + holderURL = fragment[1]; + } else if (rawURL.indexOf('url(') === 0) { + throw 'Holder: unable to parse background URL: ' + rawURL; + } + } + + if (holderURL) { + var holderFlags = parseURL(holderURL, options); + if (holderFlags) { + prepareDOMElement({ + mode: 'background', + el: bgNode, + flags: holderFlags, + engineSettings: engineSettings + }); + } + } + }); + + objects.forEach(function (object) { + var objectAttr = {}; + + try { + objectAttr.data = object.getAttribute('data'); + objectAttr.dataSrc = object.getAttribute(App.vars.dataAttr); + } catch (e) { + objectAttr.error = e; + } + + var objectHasSrcURL = objectAttr.data != null && objectAttr.data.indexOf(options.domain) === 0; + var objectHasDataSrcURL = objectAttr.dataSrc != null && objectAttr.dataSrc.indexOf(options.domain) === 0; + + if (objectHasSrcURL) { + prepareImageElement(options, engineSettings, objectAttr.data, object); + } else if (objectHasDataSrcURL) { + prepareImageElement(options, engineSettings, objectAttr.dataSrc, object); + } + }); + + images.forEach(function (image) { + var imageAttr = {}; + + try { + imageAttr.src = image.getAttribute('src'); + imageAttr.dataSrc = image.getAttribute(App.vars.dataAttr); + imageAttr.rendered = image.getAttribute('data-holder-rendered'); + } catch (e) { + imageAttr.error = e; + } + + var imageHasSrc = imageAttr.src != null; + var imageHasDataSrcURL = imageAttr.dataSrc != null && imageAttr.dataSrc.indexOf(options.domain) === 0; + var imageRendered = imageAttr.rendered != null && imageAttr.rendered == 'true'; + + if (imageHasSrc) { + if (imageAttr.src.indexOf(options.domain) === 0) { + prepareImageElement(options, engineSettings, imageAttr.src, image); + } else if (imageHasDataSrcURL) { + //Image has a valid data-src and an invalid src + if (imageRendered) { + //If the placeholder has already been render, re-render it + prepareImageElement(options, engineSettings, imageAttr.dataSrc, image); + } else { + //If the placeholder has not been rendered, check if the image exists and render a fallback if it doesn't + (function(src, options, engineSettings, dataSrc, image) { + utils.imageExists(src, function(exists) { + if (!exists) { + prepareImageElement(options, engineSettings, dataSrc, image); + } + }); + })(imageAttr.src, options, engineSettings, imageAttr.dataSrc, image); + } + } + } else if (imageHasDataSrcURL) { + prepareImageElement(options, engineSettings, imageAttr.dataSrc, image); + } + }); + + return this; + } + }; + + var App = { + settings: { + domain: 'holder.js', + images: 'img', + objects: 'object', + bgnodes: 'body .holderjs', + stylenodes: 'head link.holderjs', + themes: { + 'gray': { + bg: '#EEEEEE', + fg: '#AAAAAA' + }, + 'social': { + bg: '#3a5a97', + fg: '#FFFFFF' + }, + 'industrial': { + bg: '#434A52', + fg: '#C2F200' + }, + 'sky': { + bg: '#0D8FDB', + fg: '#FFFFFF' + }, + 'vine': { + bg: '#39DBAC', + fg: '#1E292C' + }, + 'lava': { + bg: '#F8591A', + fg: '#1C2846' + } + } + }, + defaults: { + size: 10, + units: 'pt', + scale: 1 / 16 + } + }; + + /** + * Processes provided source attribute and sets up the appropriate rendering workflow + * + * @private + * @param options Instance options from Holder.run + * @param renderSettings Instance configuration + * @param src Image URL + * @param el Image DOM element + */ + function prepareImageElement(options, engineSettings, src, el) { + var holderFlags = parseURL(src.substr(src.lastIndexOf(options.domain)), options); + if (holderFlags) { + prepareDOMElement({ + mode: null, + el: el, + flags: holderFlags, + engineSettings: engineSettings + }); + } + } + + /** + * Processes a Holder URL and extracts configuration from query string + * + * @private + * @param url URL + * @param instanceOptions Instance options from Holder.run + */ + function parseURL(url, instanceOptions) { + var holder = { + theme: extend(App.settings.themes.gray, null), + stylesheets: instanceOptions.stylesheets, + instanceOptions: instanceOptions + }; + + var firstQuestionMark = url.indexOf('?'); + var parts = [url]; + + if (firstQuestionMark !== -1) { + parts = [url.slice(0, firstQuestionMark), url.slice(firstQuestionMark + 1)]; + } + + var basics = parts[0].split('/'); + + holder.holderURL = url; + + var dimensions = basics[1]; + var dimensionData = dimensions.match(/([\d]+p?)x([\d]+p?)/); + + if (!dimensionData) return false; + + holder.fluid = dimensions.indexOf('p') !== -1; + + holder.dimensions = { + width: dimensionData[1].replace('p', '%'), + height: dimensionData[2].replace('p', '%') + }; + + if (parts.length === 2) { + var options = querystring.parse(parts[1]); + + // Dimensions + + if (utils.truthy(options.ratio)) { + holder.fluid = true; + var ratioWidth = parseFloat(holder.dimensions.width.replace('%', '')); + var ratioHeight = parseFloat(holder.dimensions.height.replace('%', '')); + + ratioHeight = Math.floor(100 * (ratioHeight / ratioWidth)); + ratioWidth = 100; + + holder.dimensions.width = ratioWidth + '%'; + holder.dimensions.height = ratioHeight + '%'; + } + + holder.auto = utils.truthy(options.auto); + + // Colors + + if (options.bg) { + holder.theme.bg = utils.parseColor(options.bg); + } + + if (options.fg) { + holder.theme.fg = utils.parseColor(options.fg); + } + + //todo: add automatic foreground to themes without foreground + if (options.bg && !options.fg) { + holder.autoFg = true; + } + + if (options.theme && Object.prototype.hasOwnProperty.call(holder.instanceOptions.themes, options.theme)) { + holder.theme = extend(holder.instanceOptions.themes[options.theme], null); + } + + // Text + + if (options.text) { + holder.text = options.text; + } + + if (options.textmode) { + holder.textmode = options.textmode; + } + + if (options.size && parseFloat(options.size)) { + holder.size = parseFloat(options.size); + } + + if (options.fixedSize != null) { + holder.fixedSize = utils.truthy(options.fixedSize); + } + + if (options.font) { + holder.font = options.font; + } + + if (options.align) { + holder.align = options.align; + } + + if (options.lineWrap) { + holder.lineWrap = options.lineWrap; + } + + holder.nowrap = utils.truthy(options.nowrap); + + // Miscellaneous + + holder.outline = utils.truthy(options.outline); + + if (utils.truthy(options.random)) { + App.vars.cache.themeKeys = App.vars.cache.themeKeys || Object.keys(holder.instanceOptions.themes); + var _theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length]; + holder.theme = extend(holder.instanceOptions.themes[_theme], null); + } + } + + return holder; + } + + /** + * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders) + * + * @private + * @param settings DOM prep settings + */ + function prepareDOMElement(prepSettings) { + var mode = prepSettings.mode; + var el = prepSettings.el; + var flags = prepSettings.flags; + var _engineSettings = prepSettings.engineSettings; + var dimensions = flags.dimensions, + theme = flags.theme; + var dimensionsCaption = dimensions.width + 'x' + dimensions.height; + mode = mode == null ? (flags.fluid ? 'fluid' : 'image') : mode; + var holderTemplateRe = /holder_([a-z]+)/g; + var dimensionsInText = false; + + if (flags.text != null) { + theme.text = flags.text; + + // SVG embedding doesn't parse Unicode properly + if (el.nodeName.toLowerCase() === 'object') { + var textLines = theme.text.split('\\n'); + for (var k = 0; k < textLines.length; k++) { + textLines[k] = utils.encodeHtmlEntity(textLines[k]); + } + theme.text = textLines.join('\\n'); + } + } + + if (theme.text) { + var holderTemplateMatches = theme.text.match(holderTemplateRe); + + if (holderTemplateMatches !== null) { + //todo: optimize template replacement + holderTemplateMatches.forEach(function (match) { + if (match === 'holder_dimensions') { + theme.text = theme.text.replace(match, dimensionsCaption); + } + }); + } + } + + var holderURL = flags.holderURL; + var engineSettings = extend(_engineSettings, null); + + if (flags.font) { + /* + If external fonts are used in a placeholder rendered with SVG, Holder falls back to canvas. + + This is done because Firefox and Chrome disallow embedded SVGs from referencing external assets. + The workaround is either to change the placeholder tag from to or to use the canvas renderer. + */ + theme.font = flags.font; + if (!engineSettings.noFontFallback && el.nodeName.toLowerCase() === 'img' && App.setup.supportsCanvas && engineSettings.renderer === 'svg') { + engineSettings = extend(engineSettings, { + renderer: 'canvas' + }); + } + } + + //Chrome and Opera require a quick 10ms re-render if web fonts are used with canvas + if (flags.font && engineSettings.renderer == 'canvas') { + engineSettings.reRender = true; + } + + if (mode == 'background') { + if (el.getAttribute('data-background-src') == null) { + DOM.setAttr(el, { + 'data-background-src': holderURL + }); + } + } else { + var domProps = {}; + domProps[App.vars.dataAttr] = holderURL; + DOM.setAttr(el, domProps); + } + + flags.theme = theme; + + //todo consider using all renderSettings in holderData + el.holderData = { + flags: flags, + engineSettings: engineSettings + }; + + if (mode == 'image' || mode == 'fluid') { + DOM.setAttr(el, { + 'alt': theme.text ? (dimensionsInText ? theme.text : theme.text + ' [' + dimensionsCaption + ']') : dimensionsCaption + }); + } + + var renderSettings = { + mode: mode, + el: el, + holderSettings: { + dimensions: dimensions, + theme: theme, + flags: flags + }, + engineSettings: engineSettings + }; + + if (mode == 'image') { + if (!flags.auto) { + el.style.width = dimensions.width + 'px'; + el.style.height = dimensions.height + 'px'; + } + + if (engineSettings.renderer == 'html') { + el.style.backgroundColor = theme.bg; + } else { + render(renderSettings); + + if (flags.textmode == 'exact') { + el.holderData.resizeUpdate = true; + App.vars.resizableImages.push(el); + updateResizableElements(el); + } + } + } else if (mode == 'background' && engineSettings.renderer != 'html') { + render(renderSettings); + } else if (mode == 'fluid') { + el.holderData.resizeUpdate = true; + + if (dimensions.height.slice(-1) == '%') { + el.style.height = dimensions.height; + } else if (flags.auto == null || !flags.auto) { + el.style.height = dimensions.height + 'px'; + } + if (dimensions.width.slice(-1) == '%') { + el.style.width = dimensions.width; + } else if (flags.auto == null || !flags.auto) { + el.style.width = dimensions.width + 'px'; + } + if (el.style.display == 'inline' || el.style.display === '' || el.style.display == 'none') { + el.style.display = 'block'; + } + + setInitialDimensions(el); + + if (engineSettings.renderer == 'html') { + el.style.backgroundColor = theme.bg; + } else { + App.vars.resizableImages.push(el); + updateResizableElements(el); + } + } + } + + /** + * Core function that takes output from renderers and sets it as the source or background-image of the target element + * + * @private + * @param renderSettings Renderer settings + */ + function render(renderSettings) { + var image = null; + var mode = renderSettings.mode; + var el = renderSettings.el; + var holderSettings = renderSettings.holderSettings; + var engineSettings = renderSettings.engineSettings; + + switch (engineSettings.renderer) { + case 'svg': + if (!App.setup.supportsSVG) return; + break; + case 'canvas': + if (!App.setup.supportsCanvas) return; + break; + default: + return; + } + + //todo: move generation of scene up to flag generation to reduce extra object creation + var scene = { + width: holderSettings.dimensions.width, + height: holderSettings.dimensions.height, + theme: holderSettings.theme, + flags: holderSettings.flags + }; + + var sceneGraph = buildSceneGraph(scene); + + function getRenderedImage() { + var image = null; + switch (engineSettings.renderer) { + case 'canvas': + image = sgCanvasRenderer(sceneGraph, renderSettings); + break; + case 'svg': + image = svgRenderer(sceneGraph, renderSettings); + break; + default: + throw 'Holder: invalid renderer: ' + engineSettings.renderer; + } + + return image; + } + + image = getRenderedImage(); + + if (image == null) { + throw 'Holder: couldn\'t render placeholder'; + } + + //todo: add canvas rendering + if (mode == 'background') { + el.style.backgroundImage = 'url(' + image + ')'; + + if (!engineSettings.noBackgroundSize) { + el.style.backgroundSize = scene.width + 'px ' + scene.height + 'px'; + } + } else { + if (el.nodeName.toLowerCase() === 'img') { + DOM.setAttr(el, { + 'src': image + }); + } else if (el.nodeName.toLowerCase() === 'object') { + DOM.setAttr(el, { + 'data': image, + 'type': 'image/svg+xml' + }); + } + if (engineSettings.reRender) { + global.setTimeout(function () { + var image = getRenderedImage(); + if (image == null) { + throw 'Holder: couldn\'t render placeholder'; + } + //todo: refactor this code into a function + if (el.nodeName.toLowerCase() === 'img') { + DOM.setAttr(el, { + 'src': image + }); + } else if (el.nodeName.toLowerCase() === 'object') { + DOM.setAttr(el, { + 'data': image, + 'type': 'image/svg+xml' + }); + } + }, 150); + } + } + //todo: account for re-rendering + DOM.setAttr(el, { + 'data-holder-rendered': true + }); + } + + /** + * Core function that takes a Holder scene description and builds a scene graph + * + * @private + * @param scene Holder scene object + */ + //todo: make this function reusable + //todo: merge app defaults and setup properties into the scene argument + function buildSceneGraph(scene) { + var fontSize = App.defaults.size; + var fixedSize = scene.flags.fixedSize != null ? scene.flags.fixedSize : scene.theme.fixedSize; + if (parseFloat(scene.theme.size)) { + fontSize = scene.theme.size; + } else if (parseFloat(scene.flags.size)) { + fontSize = scene.flags.size; + } + + scene.font = { + family: scene.theme.font ? scene.theme.font : 'Arial, Helvetica, Open Sans, sans-serif', + size: fixedSize ? fontSize : textSize(scene.width, scene.height, fontSize, App.defaults.scale), + units: scene.theme.units ? scene.theme.units : App.defaults.units, + weight: scene.theme.fontweight ? scene.theme.fontweight : 'bold' + }; + + scene.text = scene.theme.text || Math.floor(scene.width) + 'x' + Math.floor(scene.height); + + scene.noWrap = scene.theme.nowrap || scene.flags.nowrap; + + scene.align = scene.theme.align || scene.flags.align || 'center'; + + switch (scene.flags.textmode) { + case 'literal': + scene.text = scene.flags.dimensions.width + 'x' + scene.flags.dimensions.height; + break; + case 'exact': + if (!scene.flags.exactDimensions) break; + scene.text = Math.floor(scene.flags.exactDimensions.width) + 'x' + Math.floor(scene.flags.exactDimensions.height); + break; + } + + var lineWrap = scene.flags.lineWrap || App.setup.lineWrapRatio; + var sceneMargin = scene.width * lineWrap; + var maxLineWidth = sceneMargin; + + var sceneGraph = new SceneGraph({ + width: scene.width, + height: scene.height + }); + + var Shape = sceneGraph.Shape; + + var holderBg = new Shape.Rect('holderBg', { + fill: scene.theme.bg + }); + + holderBg.resize(scene.width, scene.height); + sceneGraph.root.add(holderBg); + + if (scene.flags.outline) { + var outlineColor = new Color(holderBg.properties.fill); + outlineColor = outlineColor.lighten(outlineColor.lighterThan('7f7f7f') ? -0.1 : 0.1); + holderBg.properties.outline = { + fill: outlineColor.toHex(true), + width: 2 + }; + } + + var holderTextColor = scene.theme.fg; + + if (scene.flags.autoFg) { + var holderBgColor = new Color(holderBg.properties.fill); + var lightColor = new Color('fff'); + var darkColor = new Color('000', { + 'alpha': 0.285714 + }); + + holderTextColor = holderBgColor.blendAlpha(holderBgColor.lighterThan('7f7f7f') ? darkColor : lightColor).toHex(true); + } + + var holderTextGroup = new Shape.Group('holderTextGroup', { + text: scene.text, + align: scene.align, + font: scene.font, + fill: holderTextColor + }); + + holderTextGroup.moveTo(null, null, 1); + sceneGraph.root.add(holderTextGroup); + + var tpdata = holderTextGroup.textPositionData = stagingRenderer(sceneGraph); + if (!tpdata) { + throw 'Holder: staging fallback not supported yet.'; + } + holderTextGroup.properties.leading = tpdata.boundingBox.height; + + var textNode = null; + var line = null; + + function finalizeLine(parent, line, width, height) { + line.width = width; + line.height = height; + parent.width = Math.max(parent.width, line.width); + parent.height += line.height; + } + + if (tpdata.lineCount > 1) { + var offsetX = 0; + var offsetY = 0; + var lineIndex = 0; + var lineKey; + line = new Shape.Group('line' + lineIndex); + + //Double margin so that left/right-aligned next is not flush with edge of image + if (scene.align === 'left' || scene.align === 'right') { + maxLineWidth = scene.width * (1 - (1 - lineWrap) * 2); + } + + for (var i = 0; i < tpdata.words.length; i++) { + var word = tpdata.words[i]; + textNode = new Shape.Text(word.text); + var newline = word.text == '\\n'; + if (!scene.noWrap && (offsetX + word.width >= maxLineWidth || newline === true)) { + finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading); + holderTextGroup.add(line); + offsetX = 0; + offsetY += holderTextGroup.properties.leading; + lineIndex += 1; + line = new Shape.Group('line' + lineIndex); + line.y = offsetY; + } + if (newline === true) { + continue; + } + textNode.moveTo(offsetX, 0); + offsetX += tpdata.spaceWidth + word.width; + line.add(textNode); + } + + finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading); + holderTextGroup.add(line); + + if (scene.align === 'left') { + holderTextGroup.moveTo(scene.width - sceneMargin, null, null); + } else if (scene.align === 'right') { + for (lineKey in holderTextGroup.children) { + line = holderTextGroup.children[lineKey]; + line.moveTo(scene.width - line.width, null, null); + } + + holderTextGroup.moveTo(0 - (scene.width - sceneMargin), null, null); + } else { + for (lineKey in holderTextGroup.children) { + line = holderTextGroup.children[lineKey]; + line.moveTo((holderTextGroup.width - line.width) / 2, null, null); + } + + holderTextGroup.moveTo((scene.width - holderTextGroup.width) / 2, null, null); + } + + holderTextGroup.moveTo(null, (scene.height - holderTextGroup.height) / 2, null); + + //If the text exceeds vertical space, move it down so the first line is visible + if ((scene.height - holderTextGroup.height) / 2 < 0) { + holderTextGroup.moveTo(null, 0, null); + } + } else { + textNode = new Shape.Text(scene.text); + line = new Shape.Group('line0'); + line.add(textNode); + holderTextGroup.add(line); + + if (scene.align === 'left') { + holderTextGroup.moveTo(scene.width - sceneMargin, null, null); + } else if (scene.align === 'right') { + holderTextGroup.moveTo(0 - (scene.width - sceneMargin), null, null); + } else { + holderTextGroup.moveTo((scene.width - tpdata.boundingBox.width) / 2, null, null); + } + + holderTextGroup.moveTo(null, (scene.height - tpdata.boundingBox.height) / 2, null); + } + + //todo: renderlist + return sceneGraph; + } + + /** + * Adaptive text sizing function + * + * @private + * @param width Parent width + * @param height Parent height + * @param fontSize Requested text size + * @param scale Proportional scale of text + */ + function textSize(width, height, fontSize, scale) { + var stageWidth = parseInt(width, 10); + var stageHeight = parseInt(height, 10); + + var bigSide = Math.max(stageWidth, stageHeight); + var smallSide = Math.min(stageWidth, stageHeight); + + var newHeight = 0.8 * Math.min(smallSide, bigSide * scale); + return Math.round(Math.max(fontSize, newHeight)); + } + + /** + * Iterates over resizable (fluid or auto) placeholders and renders them + * + * @private + * @param element Optional element selector, specified only if a specific element needs to be re-rendered + */ + function updateResizableElements(element) { + var images; + if (element == null || element.nodeType == null) { + images = App.vars.resizableImages; + } else { + images = [element]; + } + for (var i = 0, l = images.length; i < l; i++) { + var el = images[i]; + if (el.holderData) { + var flags = el.holderData.flags; + var dimensions = dimensionCheck(el); + if (dimensions) { + if (!el.holderData.resizeUpdate) { + continue; + } + + if (flags.fluid && flags.auto) { + var fluidConfig = el.holderData.fluidConfig; + switch (fluidConfig.mode) { + case 'width': + dimensions.height = dimensions.width / fluidConfig.ratio; + break; + case 'height': + dimensions.width = dimensions.height * fluidConfig.ratio; + break; + } + } + + var settings = { + mode: 'image', + holderSettings: { + dimensions: dimensions, + theme: flags.theme, + flags: flags + }, + el: el, + engineSettings: el.holderData.engineSettings + }; + + if (flags.textmode == 'exact') { + flags.exactDimensions = dimensions; + settings.holderSettings.dimensions = flags.dimensions; + } + + render(settings); + } else { + setInvisible(el); + } + } + } + } + + /** + * Sets up aspect ratio metadata for fluid placeholders, in order to preserve proportions when resizing + * + * @private + * @param el Image DOM element + */ + function setInitialDimensions(el) { + if (el.holderData) { + var dimensions = dimensionCheck(el); + if (dimensions) { + var flags = el.holderData.flags; + + var fluidConfig = { + fluidHeight: flags.dimensions.height.slice(-1) == '%', + fluidWidth: flags.dimensions.width.slice(-1) == '%', + mode: null, + initialDimensions: dimensions + }; + + if (fluidConfig.fluidWidth && !fluidConfig.fluidHeight) { + fluidConfig.mode = 'width'; + fluidConfig.ratio = fluidConfig.initialDimensions.width / parseFloat(flags.dimensions.height); + } else if (!fluidConfig.fluidWidth && fluidConfig.fluidHeight) { + fluidConfig.mode = 'height'; + fluidConfig.ratio = parseFloat(flags.dimensions.width) / fluidConfig.initialDimensions.height; + } + + el.holderData.fluidConfig = fluidConfig; + } else { + setInvisible(el); + } + } + } + + /** + * Iterates through all current invisible images, and if they're visible, renders them and removes them from further checks. Runs every animation frame. + * + * @private + */ + function visibilityCheck() { + var renderableImages = []; + var keys = Object.keys(App.vars.invisibleImages); + var el; + + keys.forEach(function (key) { + el = App.vars.invisibleImages[key]; + if (dimensionCheck(el) && el.nodeName.toLowerCase() == 'img') { + renderableImages.push(el); + delete App.vars.invisibleImages[key]; + } + }); + + if (renderableImages.length) { + Holder.run({ + images: renderableImages + }); + } + + // Done to prevent 100% CPU usage via aggressive calling of requestAnimationFrame + setTimeout(function () { + global.requestAnimationFrame(visibilityCheck); + }, 10); + } + + /** + * Starts checking for invisible placeholders if not doing so yet. Does nothing otherwise. + * + * @private + */ + function startVisibilityCheck() { + if (!App.vars.visibilityCheckStarted) { + global.requestAnimationFrame(visibilityCheck); + App.vars.visibilityCheckStarted = true; + } + } + + /** + * Sets a unique ID for an image detected to be invisible and adds it to the map of invisible images checked by visibilityCheck + * + * @private + * @param el Invisible DOM element + */ + function setInvisible(el) { + if (!el.holderData.invisibleId) { + App.vars.invisibleId += 1; + App.vars.invisibleImages['i' + App.vars.invisibleId] = el; + el.holderData.invisibleId = App.vars.invisibleId; + } + } + + //todo: see if possible to convert stagingRenderer to use HTML only + var stagingRenderer = (function() { + var svg = null, + stagingText = null, + stagingTextNode = null; + return function(graph) { + var rootNode = graph.root; + if (App.setup.supportsSVG) { + var firstTimeSetup = false; + var tnode = function(text) { + return document.createTextNode(text); + }; + if (svg == null || svg.parentNode !== document.body) { + firstTimeSetup = true; + } + + svg = SVG.initSVG(svg, rootNode.properties.width, rootNode.properties.height); + //Show staging element before staging + svg.style.display = 'block'; + + if (firstTimeSetup) { + stagingText = DOM.newEl('text', SVG_NS); + stagingTextNode = tnode(null); + DOM.setAttr(stagingText, { + x: 0 + }); + stagingText.appendChild(stagingTextNode); + svg.appendChild(stagingText); + document.body.appendChild(svg); + svg.style.visibility = 'hidden'; + svg.style.position = 'absolute'; + svg.style.top = '-100%'; + svg.style.left = '-100%'; + //todo: workaround for zero-dimension tag in Opera 12 + //svg.setAttribute('width', 0); + //svg.setAttribute('height', 0); + } + + var holderTextGroup = rootNode.children.holderTextGroup; + var htgProps = holderTextGroup.properties; + DOM.setAttr(stagingText, { + 'y': htgProps.font.size, + 'style': utils.cssProps({ + 'font-weight': htgProps.font.weight, + 'font-size': htgProps.font.size + htgProps.font.units, + 'font-family': htgProps.font.family + }) + }); + + //Unescape HTML entities to get approximately the right width + var txt = DOM.newEl('textarea'); + txt.innerHTML = htgProps.text; + stagingTextNode.nodeValue = txt.value; + + //Get bounding box for the whole string (total width and height) + var stagingTextBBox = stagingText.getBBox(); + + //Get line count and split the string into words + var lineCount = Math.ceil(stagingTextBBox.width / rootNode.properties.width); + var words = htgProps.text.split(' '); + var newlines = htgProps.text.match(/\\n/g); + lineCount += newlines == null ? 0 : newlines.length; + + //Get bounding box for the string with spaces removed + stagingTextNode.nodeValue = htgProps.text.replace(/[ ]+/g, ''); + var computedNoSpaceLength = stagingText.getComputedTextLength(); + + //Compute average space width + var diffLength = stagingTextBBox.width - computedNoSpaceLength; + var spaceWidth = Math.round(diffLength / Math.max(1, words.length - 1)); + + //Get widths for every word with space only if there is more than one line + var wordWidths = []; + if (lineCount > 1) { + stagingTextNode.nodeValue = ''; + for (var i = 0; i < words.length; i++) { + if (words[i].length === 0) continue; + stagingTextNode.nodeValue = utils.decodeHtmlEntity(words[i]); + var bbox = stagingText.getBBox(); + wordWidths.push({ + text: words[i], + width: bbox.width + }); + } + } + + //Hide staging element after staging + svg.style.display = 'none'; + + return { + spaceWidth: spaceWidth, + lineCount: lineCount, + boundingBox: stagingTextBBox, + words: wordWidths + }; + } else { + //todo: canvas fallback for measuring text on android 2.3 + return false; + } + }; + })(); + + //Helpers + + /** + * Prevents a function from being called too often, waits until a timer elapses to call it again + * + * @param fn Function to call + */ + function debounce(fn) { + if (!App.vars.debounceTimer) fn.call(this); + if (App.vars.debounceTimer) global.clearTimeout(App.vars.debounceTimer); + App.vars.debounceTimer = global.setTimeout(function() { + App.vars.debounceTimer = null; + fn.call(this); + }, App.setup.debounce); + } + + /** + * Holder-specific resize/orientation change callback, debounced to prevent excessive execution + */ + function resizeEvent() { + debounce(function() { + updateResizableElements(null); + }); + } + + //Set up flags + + for (var flag in App.flags) { + if (!Object.prototype.hasOwnProperty.call(App.flags, flag)) continue; + App.flags[flag].match = function(val) { + return val.match(this.regex); + }; + } + + //Properties set once on setup + + App.setup = { + renderer: 'html', + debounce: 100, + ratio: 1, + supportsCanvas: false, + supportsSVG: false, + lineWrapRatio: 0.9, + dataAttr: 'data-src', + renderers: ['html', 'canvas', 'svg'] + }; + + //Properties modified during runtime + + App.vars = { + preempted: false, + resizableImages: [], + invisibleImages: {}, + invisibleId: 0, + visibilityCheckStarted: false, + debounceTimer: null, + cache: {} + }; + + //Pre-flight + + (function() { + var canvas = DOM.newEl('canvas'); + + if (canvas.getContext) { + if (canvas.toDataURL('image/png').indexOf('data:image/png') != -1) { + App.setup.renderer = 'canvas'; + App.setup.supportsCanvas = true; + } + } + + if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) { + App.setup.renderer = 'svg'; + App.setup.supportsSVG = true; + } + })(); + + //Starts checking for invisible placeholders + startVisibilityCheck(); + + if (onDomReady) { + onDomReady(function() { + if (!App.vars.preempted) { + Holder.run(); + } + if (global.addEventListener) { + global.addEventListener('resize', resizeEvent, false); + global.addEventListener('orientationchange', resizeEvent, false); + } else { + global.attachEvent('onresize', resizeEvent); + } + + if (typeof global.Turbolinks == 'object') { + global.document.addEventListener('page:change', function() { + Holder.run(); + }); + } + }); + } + + module.exports = Holder; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + + /*! + * onDomReady.js 1.4.0 (c) 2013 Tubal Martin - MIT license + * + * Specially modified to work with Holder.js + */ + + function _onDomReady(win) { + //Lazy loading fix for Firefox < 3.6 + //http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html + if (document.readyState == null && document.addEventListener) { + document.addEventListener("DOMContentLoaded", function DOMContentLoaded() { + document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false); + document.readyState = "complete"; + }, false); + document.readyState = "loading"; + } + + var doc = win.document, + docElem = doc.documentElement, + + LOAD = "load", + FALSE = false, + ONLOAD = "on"+LOAD, + COMPLETE = "complete", + READYSTATE = "readyState", + ATTACHEVENT = "attachEvent", + DETACHEVENT = "detachEvent", + ADDEVENTLISTENER = "addEventListener", + DOMCONTENTLOADED = "DOMContentLoaded", + ONREADYSTATECHANGE = "onreadystatechange", + REMOVEEVENTLISTENER = "removeEventListener", + + // W3C Event model + w3c = ADDEVENTLISTENER in doc, + _top = FALSE, + + // isReady: Is the DOM ready to be used? Set to true once it occurs. + isReady = FALSE, + + // Callbacks pending execution until DOM is ready + callbacks = []; + + // Handle when the DOM is ready + function ready( fn ) { + if ( !isReady ) { + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !doc.body ) { + return defer( ready ); + } + + // Remember that the DOM is ready + isReady = true; + + // Execute all callbacks + while ( fn = callbacks.shift() ) { + defer( fn ); + } + } + } + + // The ready event handler + function completed( event ) { + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( w3c || event.type === LOAD || doc[READYSTATE] === COMPLETE ) { + detach(); + ready(); + } + } + + // Clean-up method for dom ready events + function detach() { + if ( w3c ) { + doc[REMOVEEVENTLISTENER]( DOMCONTENTLOADED, completed, FALSE ); + win[REMOVEEVENTLISTENER]( LOAD, completed, FALSE ); + } else { + doc[DETACHEVENT]( ONREADYSTATECHANGE, completed ); + win[DETACHEVENT]( ONLOAD, completed ); + } + } + + // Defers a function, scheduling it to run after the current call stack has cleared. + function defer( fn, wait ) { + // Allow 0 to be passed + setTimeout( fn, +wait >= 0 ? wait : 1 ); + } + + // Attach the listeners: + + // Catch cases where onDomReady is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( doc[READYSTATE] === COMPLETE ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + defer( ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( w3c ) { + // Use the handy event callback + doc[ADDEVENTLISTENER]( DOMCONTENTLOADED, completed, FALSE ); + + // A fallback to window.onload, that will always work + win[ADDEVENTLISTENER]( LOAD, completed, FALSE ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + doc[ATTACHEVENT]( ONREADYSTATECHANGE, completed ); + + // A fallback to window.onload, that will always work + win[ATTACHEVENT]( ONLOAD, completed ); + + // If IE and not a frame + // continually check to see if the document is ready + try { + _top = win.frameElement == null && docElem; + } catch(e) {} + + if ( _top && _top.doScroll ) { + (function doScrollCheck() { + if ( !isReady ) { + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + _top.doScroll("left"); + } catch(e) { + return defer( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + ready(); + } + })(); + } + } + + function onDomReady( fn ) { + // If DOM is ready, execute the function (async), otherwise wait + isReady ? defer( fn ) : callbacks.push( fn ); + } + + // Add version + onDomReady.version = "1.4.0"; + // Add method to check if DOM is ready + onDomReady.isReady = function(){ + return isReady; + }; + + return onDomReady; + } + + module.exports = typeof window !== "undefined" && _onDomReady(window); + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + + //Modified version of component/querystring + //Changes: updated dependencies, dot notation parsing, JSHint fixes + //Fork at https://github.com/imsky/querystring + + /** + * Module dependencies. + */ + + var encode = encodeURIComponent; + var decode = decodeURIComponent; + var trim = __webpack_require__(4); + var type = __webpack_require__(5); + + var arrayRegex = /(\w+)\[(\d+)\]/; + var objectRegex = /\w+\.\w+/; + + /** + * Parse the given query `str`. + * + * @param {String} str + * @return {Object} + * @api public + */ + + exports.parse = function(str){ + if ('string' !== typeof str) return {}; + + str = trim(str); + if ('' === str) return {}; + if ('?' === str.charAt(0)) str = str.slice(1); + + var obj = {}; + var pairs = str.split('&'); + for (var i = 0; i < pairs.length; i++) { + var parts = pairs[i].split('='); + var key = decode(parts[0]); + var m, ctx, prop; + + if (m = arrayRegex.exec(key)) { + obj[m[1]] = obj[m[1]] || []; + obj[m[1]][m[2]] = decode(parts[1]); + continue; + } + + if (m = objectRegex.test(key)) { + m = key.split('.'); + ctx = obj; + + while (m.length) { + prop = m.shift(); + + if (!prop.length) continue; + + if (!ctx[prop]) { + ctx[prop] = {}; + } else if (ctx[prop] && typeof ctx[prop] !== 'object') { + break; + } + + if (!m.length) { + ctx[prop] = decode(parts[1]); + } + + ctx = ctx[prop]; + } + + continue; + } + + obj[parts[0]] = null == parts[1] ? '' : decode(parts[1]); + } + + return obj; + }; + + /** + * Stringify the given `obj`. + * + * @param {Object} obj + * @return {String} + * @api public + */ + + exports.stringify = function(obj){ + if (!obj) return ''; + var pairs = []; + + for (var key in obj) { + var value = obj[key]; + + if ('array' == type(value)) { + for (var i = 0; i < value.length; ++i) { + pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i])); + } + continue; + } + + pairs.push(encode(key) + '=' + encode(obj[key])); + } + + return pairs.join('&'); + }; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + + + exports = module.exports = trim; + + function trim(str){ + return str.replace(/^\s*|\s*$/g, ''); + } + + exports.left = function(str){ + return str.replace(/^\s*/, ''); + }; + + exports.right = function(str){ + return str.replace(/\s*$/, ''); + }; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + + /** + * toString ref. + */ + + var toString = Object.prototype.toString; + + /** + * Return the type of `val`. + * + * @param {Mixed} val + * @return {String} + * @api public + */ + + module.exports = function(val){ + switch (toString.call(val)) { + case '[object Date]': return 'date'; + case '[object RegExp]': return 'regexp'; + case '[object Arguments]': return 'arguments'; + case '[object Array]': return 'array'; + case '[object Error]': return 'error'; + } + + if (val === null) return 'null'; + if (val === undefined) return 'undefined'; + if (val !== val) return 'nan'; + if (val && val.nodeType === 1) return 'element'; + + val = val.valueOf + ? val.valueOf() + : Object.prototype.valueOf.apply(val) + + return typeof val; + }; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + + var SceneGraph = function(sceneProperties) { + var nodeCount = 1; + + //todo: move merge to helpers section + function merge(parent, child) { + for (var prop in child) { + parent[prop] = child[prop]; + } + return parent; + } + + var SceneNode = function(name) { + nodeCount++; + this.parent = null; + this.children = {}; + this.id = nodeCount; + this.name = 'n' + nodeCount; + if (typeof name !== 'undefined') { + this.name = name; + } + this.x = this.y = this.z = 0; + this.width = this.height = 0; + }; + + SceneNode.prototype.resize = function(width, height) { + if (width != null) { + this.width = width; + } + if (height != null) { + this.height = height; + } + }; + + SceneNode.prototype.moveTo = function(x, y, z) { + this.x = x != null ? x : this.x; + this.y = y != null ? y : this.y; + this.z = z != null ? z : this.z; + }; + + SceneNode.prototype.add = function(child) { + var name = child.name; + if (typeof this.children[name] === 'undefined') { + this.children[name] = child; + child.parent = this; + } else { + throw 'SceneGraph: child already exists: ' + name; + } + }; + + var RootNode = function() { + SceneNode.call(this, 'root'); + this.properties = sceneProperties; + }; + + RootNode.prototype = new SceneNode(); + + var Shape = function(name, props) { + SceneNode.call(this, name); + this.properties = { + 'fill': '#000000' + }; + if (typeof props !== 'undefined') { + merge(this.properties, props); + } else if (typeof name !== 'undefined' && typeof name !== 'string') { + throw 'SceneGraph: invalid node name'; + } + }; + + Shape.prototype = new SceneNode(); + + var Group = function() { + Shape.apply(this, arguments); + this.type = 'group'; + }; + + Group.prototype = new Shape(); + + var Rect = function() { + Shape.apply(this, arguments); + this.type = 'rect'; + }; + + Rect.prototype = new Shape(); + + var Text = function(text) { + Shape.call(this); + this.type = 'text'; + this.properties.text = text; + }; + + Text.prototype = new Shape(); + + var root = new RootNode(); + + this.Shape = { + 'Rect': Rect, + 'Text': Text, + 'Group': Group + }; + + this.root = root; + return this; + }; + + module.exports = SceneGraph; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Shallow object clone and merge + * + * @param a Object A + * @param b Object B + * @returns {Object} New object with all of A's properties, and all of B's properties, overwriting A's properties + */ + exports.extend = function(a, b) { + var c = {}; + for (var x in a) { + if (Object.prototype.hasOwnProperty.call(a,x)) { + c[x] = a[x]; + } + } + if (b != null) { + for (var y in b) { + if (Object.prototype.hasOwnProperty.call(b, y)) { + c[y] = b[y]; + } + } + } + return c; + }; + + /** + * Takes a k/v list of CSS properties and returns a rule + * + * @param props CSS properties object + */ + exports.cssProps = function(props) { + var ret = []; + for (var p in props) { + if (Object.prototype.hasOwnProperty.call(props, p)) { + ret.push(p + ':' + props[p]); + } + } + return ret.join(';'); + }; + + /** + * Encodes HTML entities in a string + * + * @param str Input string + */ + exports.encodeHtmlEntity = function(str) { + var buf = []; + var charCode = 0; + for (var i = str.length - 1; i >= 0; i--) { + charCode = str.charCodeAt(i); + if (charCode > 128) { + buf.unshift(['&#', charCode, ';'].join('')); + } else { + buf.unshift(str[i]); + } + } + return buf.join(''); + }; + + /** + * Checks if an image exists + * + * @param src URL of image + * @param callback Callback to call once image status has been found + */ + exports.imageExists = function(src, callback) { + var image = new Image(); + image.onerror = function() { + callback.call(this, false); + }; + image.onload = function() { + callback.call(this, true); + }; + image.src = src; + }; + + /** + * Decodes HTML entities in a string + * + * @param str Input string + */ + exports.decodeHtmlEntity = function(str) { + return str.replace(/&#(\d+);/g, function(match, dec) { + return String.fromCharCode(dec); + }); + }; + + + /** + * Returns an element's dimensions if it's visible, `false` otherwise. + * + * @param el DOM element + */ + exports.dimensionCheck = function(el) { + var dimensions = { + height: el.clientHeight, + width: el.clientWidth + }; + + if (dimensions.height && dimensions.width) { + return dimensions; + } else { + return false; + } + }; + + + /** + * Returns true if value is truthy or if it is "semantically truthy" + * @param val + */ + exports.truthy = function(val) { + if (typeof val === 'string') { + return val === 'true' || val === 'yes' || val === '1' || val === 'on' || val === '✓'; + } + return !!val; + }; + + /** + * Parses input into a well-formed CSS color + * @param val + */ + exports.parseColor = function(val) { + var hexre = /(^(?:#?)[0-9a-f]{6}$)|(^(?:#?)[0-9a-f]{3}$)/i; + var rgbre = /^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/; + var rgbare = /^rgba\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0*\.\d{1,}|1)\)$/; + + var match = val.match(hexre); + var retval; + + if (match !== null) { + retval = match[1] || match[2]; + if (retval[0] !== '#') { + return '#' + retval; + } else { + return retval; + } + } + + match = val.match(rgbre); + + if (match !== null) { + retval = 'rgb(' + match.slice(1).join(',') + ')'; + return retval; + } + + match = val.match(rgbare); + + if (match !== null) { + var normalizeAlpha = function (a) { return '0.' + a.split('.')[1]; }; + var fixedMatch = match.slice(1).map(function (e, i) { + return (i === 3) ? normalizeAlpha(e) : e; + }); + retval = 'rgba(' + fixedMatch.join(',') + ')'; + return retval; + } + + return null; + }; + + /** + * Provides the correct scaling ratio for canvas drawing operations on HiDPI screens (e.g. Retina displays) + */ + exports.canvasRatio = function () { + var devicePixelRatio = 1; + var backingStoreRatio = 1; + + if (global.document) { + var canvas = global.document.createElement('canvas'); + if (canvas.getContext) { + var ctx = canvas.getContext('2d'); + devicePixelRatio = global.devicePixelRatio || 1; + backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; + } + } + + return devicePixelRatio / backingStoreRatio; + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {var DOM = __webpack_require__(9); + + var SVG_NS = 'http://www.w3.org/2000/svg'; + var NODE_TYPE_COMMENT = 8; + + /** + * Generic SVG element creation function + * + * @param svg SVG context, set to null if new + * @param width Document width + * @param height Document height + */ + exports.initSVG = function(svg, width, height) { + var defs, style, initialize = false; + + if (svg && svg.querySelector) { + style = svg.querySelector('style'); + if (style === null) { + initialize = true; + } + } else { + svg = DOM.newEl('svg', SVG_NS); + initialize = true; + } + + if (initialize) { + defs = DOM.newEl('defs', SVG_NS); + style = DOM.newEl('style', SVG_NS); + DOM.setAttr(style, { + 'type': 'text/css' + }); + defs.appendChild(style); + svg.appendChild(defs); + } + + //IE throws an exception if this is set and Chrome requires it to be set + if (svg.webkitMatchesSelector) { + svg.setAttribute('xmlns', SVG_NS); + } + + //Remove comment nodes + for (var i = 0; i < svg.childNodes.length; i++) { + if (svg.childNodes[i].nodeType === NODE_TYPE_COMMENT) { + svg.removeChild(svg.childNodes[i]); + } + } + + //Remove CSS + while (style.childNodes.length) { + style.removeChild(style.childNodes[0]); + } + + DOM.setAttr(svg, { + 'width': width, + 'height': height, + 'viewBox': '0 0 ' + width + ' ' + height, + 'preserveAspectRatio': 'none' + }); + + return svg; + }; + + /** + * Converts serialized SVG to a string suitable for data URI use + * @param svgString Serialized SVG string + * @param [base64] Use base64 encoding for data URI + */ + exports.svgStringToDataURI = function() { + var rawPrefix = 'data:image/svg+xml;charset=UTF-8,'; + var base64Prefix = 'data:image/svg+xml;charset=UTF-8;base64,'; + + return function(svgString, base64) { + if (base64) { + return base64Prefix + btoa(global.unescape(encodeURIComponent(svgString))); + } else { + return rawPrefix + encodeURIComponent(svgString); + } + }; + }(); + + /** + * Returns serialized SVG with XML processing instructions + * + * @param svg SVG context + * @param stylesheets CSS stylesheets to include + */ + exports.serializeSVG = function(svg, engineSettings) { + if (!global.XMLSerializer) return; + var serializer = new XMLSerializer(); + var svgCSS = ''; + var stylesheets = engineSettings.stylesheets; + + //External stylesheets: Processing Instruction method + if (engineSettings.svgXMLStylesheet) { + var xml = DOM.createXML(); + //Add directives + for (var i = stylesheets.length - 1; i >= 0; i--) { + var csspi = xml.createProcessingInstruction('xml-stylesheet', 'href="' + stylesheets[i] + '" rel="stylesheet"'); + xml.insertBefore(csspi, xml.firstChild); + } + + xml.removeChild(xml.documentElement); + svgCSS = serializer.serializeToString(xml); + } + + var svgText = serializer.serializeToString(svg); + svgText = svgText.replace(/&(#[0-9]{2,};)/g, '&$1'); + return svgCSS + svgText; + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 9 */ +/***/ (function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Generic new DOM element function + * + * @param tag Tag to create + * @param namespace Optional namespace value + */ + exports.newEl = function(tag, namespace) { + if (!global.document) return; + + if (namespace == null) { + return global.document.createElement(tag); + } else { + return global.document.createElementNS(namespace, tag); + } + }; + + /** + * Generic setAttribute function + * + * @param el Reference to DOM element + * @param attrs Object with attribute keys and values + */ + exports.setAttr = function (el, attrs) { + for (var a in attrs) { + el.setAttribute(a, attrs[a]); + } + }; + + /** + * Creates a XML document + * @private + */ + exports.createXML = function() { + if (!global.DOMParser) return; + return new DOMParser().parseFromString('', 'application/xml'); + }; + + /** + * Converts a value into an array of DOM nodes + * + * @param val A string, a NodeList, a Node, or an HTMLCollection + */ + exports.getNodeArray = function(val) { + var retval = null; + if (typeof(val) == 'string') { + retval = document.querySelectorAll(val); + } else if (global.NodeList && val instanceof global.NodeList) { + retval = val; + } else if (global.Node && val instanceof global.Node) { + retval = [val]; + } else if (global.HTMLCollection && val instanceof global.HTMLCollection) { + retval = val; + } else if (val instanceof Array) { + retval = val; + } else if (val === null) { + retval = []; + } + + retval = Array.prototype.slice.call(retval); + + return retval; + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 10 */ +/***/ (function(module, exports) { + + var Color = function(color, options) { + //todo: support rgba, hsla, and rrggbbaa notation + //todo: use CIELAB internally + //todo: add clamp function (with sign) + if (typeof color !== 'string') return; + + this.original = color; + + if (color.charAt(0) === '#') { + color = color.slice(1); + } + + if (/[^a-f0-9]+/i.test(color)) return; + + if (color.length === 3) { + color = color.replace(/./g, '$&$&'); + } + + if (color.length !== 6) return; + + this.alpha = 1; + + if (options && options.alpha) { + this.alpha = options.alpha; + } + + this.set(parseInt(color, 16)); + }; + + //todo: jsdocs + Color.rgb2hex = function(r, g, b) { + function format (decimal) { + var hex = (decimal | 0).toString(16); + if (decimal < 16) { + hex = '0' + hex; + } + return hex; + } + + return [r, g, b].map(format).join(''); + }; + + //todo: jsdocs + Color.hsl2rgb = function (h, s, l) { + var H = h / 60; + var C = (1 - Math.abs(2 * l - 1)) * s; + var X = C * (1 - Math.abs(parseInt(H) % 2 - 1)); + var m = l - (C / 2); + + var r = 0, g = 0, b = 0; + + if (H >= 0 && H < 1) { + r = C; + g = X; + } else if (H >= 1 && H < 2) { + r = X; + g = C; + } else if (H >= 2 && H < 3) { + g = C; + b = X; + } else if (H >= 3 && H < 4) { + g = X; + b = C; + } else if (H >= 4 && H < 5) { + r = X; + b = C; + } else if (H >= 5 && H < 6) { + r = C; + b = X; + } + + r += m; + g += m; + b += m; + + r = parseInt(r * 255); + g = parseInt(g * 255); + b = parseInt(b * 255); + + return [r, g, b]; + }; + + /** + * Sets the color from a raw RGB888 integer + * @param raw RGB888 representation of color + */ + //todo: refactor into a static method + //todo: factor out individual color spaces + //todo: add HSL, CIELAB, and CIELUV + Color.prototype.set = function (val) { + this.raw = val; + + var r = (this.raw & 0xFF0000) >> 16; + var g = (this.raw & 0x00FF00) >> 8; + var b = (this.raw & 0x0000FF); + + // BT.709 + var y = 0.2126 * r + 0.7152 * g + 0.0722 * b; + var u = -0.09991 * r - 0.33609 * g + 0.436 * b; + var v = 0.615 * r - 0.55861 * g - 0.05639 * b; + + this.rgb = { + r: r, + g: g, + b: b + }; + + this.yuv = { + y: y, + u: u, + v: v + }; + + return this; + }; + + /** + * Lighten or darken a color + * @param multiplier Amount to lighten or darken (-1 to 1) + */ + Color.prototype.lighten = function(multiplier) { + var cm = Math.min(1, Math.max(0, Math.abs(multiplier))) * (multiplier < 0 ? -1 : 1); + var bm = (255 * cm) | 0; + var cr = Math.min(255, Math.max(0, this.rgb.r + bm)); + var cg = Math.min(255, Math.max(0, this.rgb.g + bm)); + var cb = Math.min(255, Math.max(0, this.rgb.b + bm)); + var hex = Color.rgb2hex(cr, cg, cb); + return new Color(hex); + }; + + /** + * Output color in hex format + * @param addHash Add a hash character to the beginning of the output + */ + Color.prototype.toHex = function(addHash) { + return (addHash ? '#' : '') + this.raw.toString(16); + }; + + /** + * Returns whether or not current color is lighter than another color + * @param color Color to compare against + */ + Color.prototype.lighterThan = function(color) { + if (!(color instanceof Color)) { + color = new Color(color); + } + + return this.yuv.y > color.yuv.y; + }; + + /** + * Returns the result of mixing current color with another color + * @param color Color to mix with + * @param multiplier How much to mix with the other color + */ + /* + Color.prototype.mix = function (color, multiplier) { + if (!(color instanceof Color)) { + color = new Color(color); + } + + var r = this.rgb.r; + var g = this.rgb.g; + var b = this.rgb.b; + var a = this.alpha; + + var m = typeof multiplier !== 'undefined' ? multiplier : 0.5; + + //todo: write a lerp function + r = r + m * (color.rgb.r - r); + g = g + m * (color.rgb.g - g); + b = b + m * (color.rgb.b - b); + a = a + m * (color.alpha - a); + + return new Color(Color.rgbToHex(r, g, b), { + 'alpha': a + }); + }; + */ + + /** + * Returns the result of blending another color on top of current color with alpha + * @param color Color to blend on top of current color, i.e. "Ca" + */ + //todo: see if .blendAlpha can be merged into .mix + Color.prototype.blendAlpha = function(color) { + if (!(color instanceof Color)) { + color = new Color(color); + } + + var Ca = color; + var Cb = this; + + //todo: write alpha blending function + var r = Ca.alpha * Ca.rgb.r + (1 - Ca.alpha) * Cb.rgb.r; + var g = Ca.alpha * Ca.rgb.g + (1 - Ca.alpha) * Cb.rgb.g; + var b = Ca.alpha * Ca.rgb.b + (1 - Ca.alpha) * Cb.rgb.b; + + return new Color(Color.rgb2hex(r, g, b)); + }; + + module.exports = Color; + + +/***/ }), +/* 11 */ +/***/ (function(module, exports) { + + module.exports = { + 'version': '2.9.9', + 'svg_ns': 'http://www.w3.org/2000/svg' + }; + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + + var shaven = __webpack_require__(13).default; + + var SVG = __webpack_require__(8); + var constants = __webpack_require__(11); + var utils = __webpack_require__(7); + + var SVG_NS = constants.svg_ns; + + var templates = { + 'element': function (options) { + var tag = options.tag; + var content = options.content || ''; + delete options.tag; + delete options.content; + return [tag, content, options]; + } + }; + + //todo: deprecate tag arg, infer tag from shape object + function convertShape (shape, tag) { + return templates.element({ + 'tag': tag, + 'width': shape.width, + 'height': shape.height, + 'fill': shape.properties.fill + }); + } + + function textCss (properties) { + return utils.cssProps({ + 'fill': properties.fill, + 'font-weight': properties.font.weight, + 'font-family': properties.font.family + ', monospace', + 'font-size': properties.font.size + properties.font.units + }); + } + + function outlinePath (bgWidth, bgHeight, outlineWidth) { + var outlineOffsetWidth = outlineWidth / 2; + + return [ + 'M', outlineOffsetWidth, outlineOffsetWidth, + 'H', bgWidth - outlineOffsetWidth, + 'V', bgHeight - outlineOffsetWidth, + 'H', outlineOffsetWidth, + 'V', 0, + 'M', 0, outlineOffsetWidth, + 'L', bgWidth, bgHeight - outlineOffsetWidth, + 'M', 0, bgHeight - outlineOffsetWidth, + 'L', bgWidth, outlineOffsetWidth + ].join(' '); + } + + module.exports = function (sceneGraph, renderSettings) { + var engineSettings = renderSettings.engineSettings; + var stylesheets = engineSettings.stylesheets; + var stylesheetXml = stylesheets.map(function (stylesheet) { + return ''; + }).join('\n'); + + var holderId = 'holder_' + Number(new Date()).toString(16); + + var root = sceneGraph.root; + var textGroup = root.children.holderTextGroup; + + var css = '#' + holderId + ' text { ' + textCss(textGroup.properties) + ' } '; + + // push text down to be equally vertically aligned with canvas renderer + textGroup.y += textGroup.textPositionData.boundingBox.height * 0.8; + + var wordTags = []; + + Object.keys(textGroup.children).forEach(function (lineKey) { + var line = textGroup.children[lineKey]; + + Object.keys(line.children).forEach(function (wordKey) { + var word = line.children[wordKey]; + var x = textGroup.x + line.x + word.x; + var y = textGroup.y + line.y + word.y; + var wordTag = templates.element({ + 'tag': 'text', + 'content': word.properties.text, + 'x': x, + 'y': y + }); + + wordTags.push(wordTag); + }); + }); + + var text = templates.element({ + 'tag': 'g', + 'content': wordTags + }); + + var outline = null; + + if (root.children.holderBg.properties.outline) { + var outlineProperties = root.children.holderBg.properties.outline; + outline = templates.element({ + 'tag': 'path', + 'd': outlinePath(root.children.holderBg.width, root.children.holderBg.height, outlineProperties.width), + 'stroke-width': outlineProperties.width, + 'stroke': outlineProperties.fill, + 'fill': 'none' + }); + } + + var bg = convertShape(root.children.holderBg, 'rect'); + + var sceneContent = []; + + sceneContent.push(bg); + if (outlineProperties) { + sceneContent.push(outline); + } + sceneContent.push(text); + + var scene = templates.element({ + 'tag': 'g', + 'id': holderId, + 'content': sceneContent + }); + + var style = templates.element({ + 'tag': 'style', + //todo: figure out how to add CDATA directive + 'content': css, + 'type': 'text/css' + }); + + var defs = templates.element({ + 'tag': 'defs', + 'content': style + }); + + var svg = templates.element({ + 'tag': 'svg', + 'content': [defs, scene], + 'width': root.properties.width, + 'height': root.properties.height, + 'xmlns': SVG_NS, + 'viewBox': [0, 0, root.properties.width, root.properties.height].join(' '), + 'preserveAspectRatio': 'none' + }); + + var output = String(shaven(svg)); + + if (/&(x)?#[0-9A-Fa-f]/.test(output[0])) { + output = output.replace(/&#/gm, '&#'); + } + + output = stylesheetXml + output; + + var svgString = SVG.svgStringToDataURI(output, renderSettings.mode === 'background'); + + return svgString; + }; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + // vendored shaven 1.3.0 due to published package.json including an outdated node engine + module.exports = __webpack_require__(14); + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.default = shaven; + + var _parseSugarString = __webpack_require__(15); + + var _parseSugarString2 = _interopRequireDefault(_parseSugarString); + + var _escape = __webpack_require__(16); + + var escape = _interopRequireWildcard(_escape); + + var _defaults = __webpack_require__(17); + + var _defaults2 = _interopRequireDefault(_defaults); + + var _mapAttributeValue = __webpack_require__(18); + + var _mapAttributeValue2 = _interopRequireDefault(_mapAttributeValue); + + var _assert = __webpack_require__(21); + + var _assert2 = _interopRequireDefault(_assert); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function shaven(arrayOrObject) { + var isArray = Array.isArray(arrayOrObject); + var objType = typeof arrayOrObject === 'undefined' ? 'undefined' : _typeof(arrayOrObject); + + if (!isArray && objType !== 'object') { + throw new Error('Argument must be either an array or an object ' + 'and not ' + JSON.stringify(arrayOrObject)); + } + + if (isArray && arrayOrObject.length === 0) { + // Ignore empty arrays + return {}; + } + + var config = {}; + var elementArray = []; + + if (Array.isArray(arrayOrObject)) { + elementArray = arrayOrObject.slice(0); + } else { + elementArray = arrayOrObject.elementArray.slice(0); + config = Object.assign(config, arrayOrObject); + delete config.elementArray; + } + + config = Object.assign({}, _defaults2.default, config, { + returnObject: { // Shaven object to return at last + ids: {}, + references: {} + } + }); + + function createElement(sugarString) { + var properties = (0, _parseSugarString2.default)(sugarString); + var element = { + tag: properties.tag, + attr: {}, + children: [] + }; + + if (properties.id) { + element.attr.id = properties.id; + (0, _assert2.default)(!config.returnObject.ids.hasOwnProperty(properties.id), 'Ids must be unique and "' + properties.id + '" is already assigned'); + config.returnObject.ids[properties.id] = element; + } + if (properties.class) { + element.attr.class = properties.class; + } + if (properties.reference) { + (0, _assert2.default)(!config.returnObject.ids.hasOwnProperty(properties.reference), 'References must be unique and "' + properties.id + '" is already assigned'); + config.returnObject.references[properties.reference] = element; + } + + config.escapeHTML = properties.escapeHTML != null ? properties.escapeHTML : config.escapeHTML; + + return element; + } + + function buildDom(elemArray) { + if (Array.isArray(elemArray) && elemArray.length === 0) { + // Ignore empty arrays + return {}; + } + + var index = 1; + var createdCallback = void 0; + var selfClosingHTMLTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']; + // Clone to avoid mutation problems + var array = elemArray.slice(0); + + if (typeof array[0] === 'string') { + array[0] = createElement(array[0]); + } else if (Array.isArray(array[0])) { + index = 0; + } else { + throw new Error('First element of array must be a string, ' + 'or an array and not ' + JSON.stringify(array[0])); + } + + for (; index < array.length; index++) { + + // Don't render element if value is false or null + if (array[index] === false || array[index] === null) { + array[0] = false; + break; + } + + // Continue with next array value if current value is undefined or true + else if (array[index] === undefined || array[index] === true) { + continue; + } else if (typeof array[index] === 'string') { + if (config.escapeHTML) { + // eslint-disable-next-line new-cap + array[index] = escape.HTML(array[index]); + } + + array[0].children.push(array[index]); + } else if (typeof array[index] === 'number') { + + array[0].children.push(array[index]); + } else if (Array.isArray(array[index])) { + + if (Array.isArray(array[index][0])) { + array[index].reverse().forEach(function (subArray) { + // eslint-disable-line no-loop-func + array.splice(index + 1, 0, subArray); + }); + + if (index !== 0) continue; + index++; + } + + array[index] = buildDom(array[index]); + + if (array[index][0]) { + array[0].children.push(array[index][0]); + } + } else if (typeof array[index] === 'function') { + createdCallback = array[index]; + } else if (_typeof(array[index]) === 'object') { + for (var attributeKey in array[index]) { + if (!array[index].hasOwnProperty(attributeKey)) continue; + + var attributeValue = array[index][attributeKey]; + + if (array[index].hasOwnProperty(attributeKey) && attributeValue !== null && attributeValue !== false) { + array[0].attr[attributeKey] = (0, _mapAttributeValue2.default)(attributeKey, attributeValue); + } + } + } else { + throw new TypeError('"' + array[index] + '" is not allowed as a value'); + } + } + + if (array[0] !== false) { + var HTMLString = '<' + array[0].tag; + + for (var key in array[0].attr) { + if (array[0].attr.hasOwnProperty(key)) { + var _attributeValue = escape.attribute(array[0].attr[key]); + var value = _attributeValue; + + if (config.quoteAttributes || /[ "'=<>]/.test(_attributeValue)) { + value = config.quotationMark + _attributeValue + config.quotationMark; + } + + HTMLString += ' ' + key + '=' + value; + } + } + + HTMLString += '>'; + + if (!(selfClosingHTMLTags.indexOf(array[0].tag) !== -1)) { + array[0].children.forEach(function (child) { + return HTMLString += child; + }); + + HTMLString += ''; + } + + array[0] = HTMLString; + } + + // Return root element on index 0 + config.returnObject[0] = array[0]; + config.returnObject.rootElement = array[0]; + + config.returnObject.toString = function () { + return array[0]; + }; + + if (createdCallback) createdCallback(array[0]); + + return config.returnObject; + } + + return buildDom(elementArray); + } + + shaven.setDefaults = function (object) { + Object.assign(_defaults2.default, object); + return shaven; + }; + + +/***/ }), +/* 15 */ +/***/ (function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + exports.default = function (sugarString) { + var tags = sugarString.match(/^[\w-]+/); + var properties = { + tag: tags ? tags[0] : 'div' + }; + var ids = sugarString.match(/#([\w-]+)/); + var classes = sugarString.match(/\.[\w-]+/g); + var references = sugarString.match(/\$([\w-]+)/); + + if (ids) properties.id = ids[1]; + + if (classes) { + properties.class = classes.join(' ').replace(/\./g, ''); + } + + if (references) properties.reference = references[1]; + + if (sugarString.endsWith('&') || sugarString.endsWith('!')) { + properties.escapeHTML = false; + } + + return properties; + }; + +/***/ }), +/* 16 */ +/***/ (function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.attribute = attribute; + exports.HTML = HTML; + function attribute(string) { + return string || string === 0 ? String(string).replace(/&/g, '&').replace(/"/g, '"') : ''; + } + + function HTML(string) { + return String(string).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>'); + } + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.default = { + namespace: 'xhtml', + autoNamespacing: true, + escapeHTML: true, + quotationMark: '"', + quoteAttributes: true, + convertTransformArray: true + }; + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + var _buildTransformString = __webpack_require__(19); + + var _buildTransformString2 = _interopRequireDefault(_buildTransformString); + + var _stringifyStyleObject = __webpack_require__(20); + + var _stringifyStyleObject2 = _interopRequireDefault(_stringifyStyleObject); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + exports.default = function (key, value) { + if (value === undefined) { + return ''; + } + + if (key === 'style' && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') { + return (0, _stringifyStyleObject2.default)(value); + } + + if (key === 'transform' && Array.isArray(value)) { + return (0, _buildTransformString2.default)(value); + } + + return value; + }; + +/***/ }), +/* 19 */ +/***/ (function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + // Create transform string from list transform objects + + exports.default = function (transformObjects) { + + return transformObjects.map(function (transformation) { + var values = []; + + if (transformation.type === 'rotate' && transformation.degrees) { + values.push(transformation.degrees); + } + if (transformation.x) values.push(transformation.x); + if (transformation.y) values.push(transformation.y); + + return transformation.type + '(' + values + ')'; + }).join(' '); + }; + +/***/ }), +/* 20 */ +/***/ (function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + function sanitizeProperties(key, value) { + if (value === null || value === false || value === undefined) return; + if (typeof value === 'string' || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') return value; + + return String(value); + } + + exports.default = function (styleObject) { + return JSON.stringify(styleObject, sanitizeProperties).slice(2, -2).replace(/","/g, ';').replace(/":"/g, ':').replace(/\\"/g, '\''); + }; + +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + var objectAssign = __webpack_require__(22); + + // compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js + // original notice: + + /*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ + function compare(a, b) { + if (a === b) { + return 0; + } + + var x = a.length; + var y = b.length; + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; + } + function isBuffer(b) { + if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { + return global.Buffer.isBuffer(b); + } + return !!(b != null && b._isBuffer); + } + + // based on node assert, original notice: + // NB: The URL to the CommonJS spec is kept just for tradition. + // node-assert has evolved a lot since then, both in API and behavior. + + // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 + // + // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! + // + // Originally from narwhal.js (http://narwhaljs.org) + // Copyright (c) 2009 Thomas Robinson <280north.com> + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the 'Software'), to + // deal in the Software without restriction, including without limitation the + // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + // sell copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + var util = __webpack_require__(23); + var hasOwn = Object.prototype.hasOwnProperty; + var pSlice = Array.prototype.slice; + var functionsHaveNames = (function () { + return function foo() {}.name === 'foo'; + }()); + function pToString (obj) { + return Object.prototype.toString.call(obj); + } + function isView(arrbuf) { + if (isBuffer(arrbuf)) { + return false; + } + if (typeof global.ArrayBuffer !== 'function') { + return false; + } + if (typeof ArrayBuffer.isView === 'function') { + return ArrayBuffer.isView(arrbuf); + } + if (!arrbuf) { + return false; + } + if (arrbuf instanceof DataView) { + return true; + } + if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { + return true; + } + return false; + } + // 1. The assert module provides functions that throw + // AssertionError's when particular conditions are not met. The + // assert module must conform to the following interface. + + var assert = module.exports = ok; + + // 2. The AssertionError is defined in assert. + // new assert.AssertionError({ message: message, + // actual: actual, + // expected: expected }) + + var regex = /\s*function\s+([^\(\s]*)\s*/; + // based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js + function getName(func) { + if (!util.isFunction(func)) { + return; + } + if (functionsHaveNames) { + return func.name; + } + var str = func.toString(); + var match = str.match(regex); + return match && match[1]; + } + assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = getName(stackStartFunction); + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } + }; + + // assert.AssertionError instanceof Error + util.inherits(assert.AssertionError, Error); + + function truncate(s, n) { + if (typeof s === 'string') { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } + } + function inspect(something) { + if (functionsHaveNames || !util.isFunction(something)) { + return util.inspect(something); + } + var rawname = getName(something); + var name = rawname ? ': ' + rawname : ''; + return '[Function' + name + ']'; + } + function getMessage(self) { + return truncate(inspect(self.actual), 128) + ' ' + + self.operator + ' ' + + truncate(inspect(self.expected), 128); + } + + // At present only the three keys mentioned above are used and + // understood by the spec. Implementations or sub modules can pass + // other keys to the AssertionError's constructor - they will be + // ignored. + + // 3. All of the following functions must throw an AssertionError + // when a corresponding condition is not met, with a message that + // may be undefined if not provided. All assertion methods provide + // both the actual and expected values to the assertion error for + // display purposes. + + function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); + } + + // EXTENSION! allows for well behaved errors defined elsewhere. + assert.fail = fail; + + // 4. Pure assertion tests whether a value is truthy, as determined + // by !!guard. + // assert.ok(guard, message_opt); + // This statement is equivalent to assert.equal(true, !!guard, + // message_opt);. To test strictly for the value true, use + // assert.strictEqual(true, guard, message_opt);. + + function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); + } + assert.ok = ok; + + // 5. The equality assertion tests shallow, coercive equality with + // ==. + // assert.equal(actual, expected, message_opt); + + assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); + }; + + // 6. The non-equality assertion tests for whether two objects are not equal + // with != assert.notEqual(actual, expected, message_opt); + + assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } + }; + + // 7. The equivalence assertion tests a deep equality relation. + // assert.deepEqual(actual, expected, message_opt); + + assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } + }; + + assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); + } + }; + + function _deepEqual(actual, expected, strict, memos) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if (isBuffer(actual) && isBuffer(expected)) { + return compare(actual, expected) === 0; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if ((actual === null || typeof actual !== 'object') && + (expected === null || typeof expected !== 'object')) { + return strict ? actual === expected : actual == expected; + + // If both values are instances of typed arrays, wrap their underlying + // ArrayBuffers in a Buffer each to increase performance + // This optimization requires the arrays to have the same type as checked by + // Object.prototype.toString (aka pToString). Never perform binary + // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their + // bit patterns are not identical. + } else if (isView(actual) && isView(expected) && + pToString(actual) === pToString(expected) && + !(actual instanceof Float32Array || + actual instanceof Float64Array)) { + return compare(new Uint8Array(actual.buffer), + new Uint8Array(expected.buffer)) === 0; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else if (isBuffer(actual) !== isBuffer(expected)) { + return false; + } else { + memos = memos || {actual: [], expected: []}; + + var actualIndex = memos.actual.indexOf(actual); + if (actualIndex !== -1) { + if (actualIndex === memos.expected.indexOf(expected)) { + return true; + } + } + + memos.actual.push(actual); + memos.expected.push(expected); + + return objEquiv(actual, expected, strict, memos); + } + } + + function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; + } + + function objEquiv(a, b, strict, actualVisitedObjects) { + if (a === null || a === undefined || b === null || b === undefined) + return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) + return a === b; + if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) + return false; + var aIsArgs = isArguments(a); + var bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b, strict); + } + var ka = objectKeys(a); + var kb = objectKeys(b); + var key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length !== kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] !== kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) + return false; + } + return true; + } + + // 8. The non-equivalence assertion tests for any deep inequality. + // assert.notDeepEqual(actual, expected, message_opt); + + assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } + }; + + assert.notDeepStrictEqual = notDeepStrictEqual; + function notDeepStrictEqual(actual, expected, message) { + if (_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); + } + } + + + // 9. The strict equality assertion tests strict equality, as determined by ===. + // assert.strictEqual(actual, expected, message_opt); + + assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } + }; + + // 10. The strict non-equality assertion tests for strict inequality, as + // determined by !==. assert.notStrictEqual(actual, expected, message_opt); + + assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } + }; + + function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } + + try { + if (actual instanceof expected) { + return true; + } + } catch (e) { + // Ignore. The instanceof check doesn't work for arrow functions. + } + + if (Error.isPrototypeOf(expected)) { + return false; + } + + return expected.call({}, actual) === true; + } + + function _tryBlock(block) { + var error; + try { + block(); + } catch (e) { + error = e; + } + return error; + } + + function _throws(shouldThrow, block, expected, message) { + var actual; + + if (typeof block !== 'function') { + throw new TypeError('"block" argument must be a function'); + } + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + actual = _tryBlock(block); + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + var userProvidedMessage = typeof message === 'string'; + var isUnwantedException = !shouldThrow && util.isError(actual); + var isUnexpectedException = !shouldThrow && actual && !expected; + + if ((isUnwantedException && + userProvidedMessage && + expectedException(actual, expected)) || + isUnexpectedException) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } + } + + // 11. Expected to throw an error: + // assert.throws(block, Error_opt, message_opt); + + assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws(true, block, error, message); + }; + + // EXTENSION! This is annoying to write outside this module. + assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws(false, block, error, message); + }; + + assert.ifError = function(err) { if (err) throw err; }; + + // Expose a strict only variant of assert + function strict(value, message) { + if (!value) fail(value, true, message, '==', strict); + } + assert.strict = objectAssign(strict, assert, { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + notEqual: assert.notStrictEqual, + notDeepEqual: assert.notDeepStrictEqual + }); + assert.strict.strict = assert.strict; + + var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }), +/* 22 */ +/***/ (function(module, exports) { + + /* + object-assign + (c) Sindre Sorhus + @license MIT + */ + + 'use strict'; + /* eslint-disable no-unused-vars */ + var getOwnPropertySymbols = Object.getOwnPropertySymbols; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var propIsEnumerable = Object.prototype.propertyIsEnumerable; + + function toObject(val) { + if (val === null || val === undefined) { + throw new TypeError('Object.assign cannot be called with null or undefined'); + } + + return Object(val); + } + + function shouldUseNative() { + try { + if (!Object.assign) { + return false; + } + + // Detect buggy property enumeration order in older V8 versions. + + // https://bugs.chromium.org/p/v8/issues/detail?id=4118 + var test1 = new String('abc'); // eslint-disable-line no-new-wrappers + test1[5] = 'de'; + if (Object.getOwnPropertyNames(test1)[0] === '5') { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test2 = {}; + for (var i = 0; i < 10; i++) { + test2['_' + String.fromCharCode(i)] = i; + } + var order2 = Object.getOwnPropertyNames(test2).map(function (n) { + return test2[n]; + }); + if (order2.join('') !== '0123456789') { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test3 = {}; + 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { + test3[letter] = letter; + }); + if (Object.keys(Object.assign({}, test3)).join('') !== + 'abcdefghijklmnopqrst') { + return false; + } + + return true; + } catch (err) { + // We don't expect any of the above to throw, but better to be safe. + return false; + } + } + + module.exports = shouldUseNative() ? Object.assign : function (target, source) { + var from; + var to = toObject(target); + var symbols; + + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); + + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + + if (getOwnPropertySymbols) { + symbols = getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } + + return to; + }; + + +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var formatRegExp = /%[sdj%]/g; + exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; + + + // Mark that a method should not be used. + // Returns a modified function which warns once by default. + // If --no-deprecation is set, then it is a no-op. + exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; + }; + + + var debugs = {}; + var debugEnviron; + exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; + }; + + + /** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ + /* legacy: obj, showHidden, depth, colors*/ + function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); + } + exports.inspect = inspect; + + + // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics + inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] + }; + + // Don't use 'blue' not visible on cmd.exe + inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' + }; + + + function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } + } + + + function stylizeNoColor(str, styleType) { + return str; + } + + + function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; + } + + + function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); + } + + + function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); + } + + + function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; + } + + + function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; + } + + + function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; + } + + + function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; + } + + + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { + return Array.isArray(ar); + } + exports.isArray = isArray; + + function isBoolean(arg) { + return typeof arg === 'boolean'; + } + exports.isBoolean = isBoolean; + + function isNull(arg) { + return arg === null; + } + exports.isNull = isNull; + + function isNullOrUndefined(arg) { + return arg == null; + } + exports.isNullOrUndefined = isNullOrUndefined; + + function isNumber(arg) { + return typeof arg === 'number'; + } + exports.isNumber = isNumber; + + function isString(arg) { + return typeof arg === 'string'; + } + exports.isString = isString; + + function isSymbol(arg) { + return typeof arg === 'symbol'; + } + exports.isSymbol = isSymbol; + + function isUndefined(arg) { + return arg === void 0; + } + exports.isUndefined = isUndefined; + + function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; + } + exports.isRegExp = isRegExp; + + function isObject(arg) { + return typeof arg === 'object' && arg !== null; + } + exports.isObject = isObject; + + function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; + } + exports.isDate = isDate; + + function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); + } + exports.isError = isError; + + function isFunction(arg) { + return typeof arg === 'function'; + } + exports.isFunction = isFunction; + + function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; + } + exports.isPrimitive = isPrimitive; + + exports.isBuffer = __webpack_require__(25); + + function objectToString(o) { + return Object.prototype.toString.call(o); + } + + + function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); + } + + + var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + + // 26 Feb 16:19:34 + function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); + } + + + // log is just a thin wrapper to console.log that prepends a timestamp + exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); + }; + + + /** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ + exports.inherits = __webpack_require__(26); + + exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; + }; + + function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(24))) + +/***/ }), +/* 24 */ +/***/ (function(module, exports) { + + // shim for using process in browser + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); + } + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + } ()) + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + + } + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + + } + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + process.prependListener = noop; + process.prependOnceListener = noop; + + process.listeners = function (name) { return [] } + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }), +/* 25 */ +/***/ (function(module, exports) { + + module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; + } + +/***/ }), +/* 26 */ +/***/ (function(module, exports) { + + if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + } else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } + + +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + + var DOM = __webpack_require__(9); + var utils = __webpack_require__(7); + + module.exports = (function() { + var canvas = DOM.newEl('canvas'); + var ctx = null; + + return function(sceneGraph) { + if (ctx == null) { + ctx = canvas.getContext('2d'); + } + + var dpr = utils.canvasRatio(); + var root = sceneGraph.root; + canvas.width = dpr * root.properties.width; + canvas.height = dpr * root.properties.height ; + ctx.textBaseline = 'middle'; + + var bg = root.children.holderBg; + var bgWidth = dpr * bg.width; + var bgHeight = dpr * bg.height; + //todo: parametrize outline width (e.g. in scene object) + var outlineWidth = 2; + var outlineOffsetWidth = outlineWidth / 2; + + ctx.fillStyle = bg.properties.fill; + ctx.fillRect(0, 0, bgWidth, bgHeight); + + if (bg.properties.outline) { + //todo: abstract this into a method + ctx.strokeStyle = bg.properties.outline.fill; + ctx.lineWidth = bg.properties.outline.width; + ctx.moveTo(outlineOffsetWidth, outlineOffsetWidth); + // TL, TR, BR, BL + ctx.lineTo(bgWidth - outlineOffsetWidth, outlineOffsetWidth); + ctx.lineTo(bgWidth - outlineOffsetWidth, bgHeight - outlineOffsetWidth); + ctx.lineTo(outlineOffsetWidth, bgHeight - outlineOffsetWidth); + ctx.lineTo(outlineOffsetWidth, outlineOffsetWidth); + // Diagonals + ctx.moveTo(0, outlineOffsetWidth); + ctx.lineTo(bgWidth, bgHeight - outlineOffsetWidth); + ctx.moveTo(0, bgHeight - outlineOffsetWidth); + ctx.lineTo(bgWidth, outlineOffsetWidth); + ctx.stroke(); + } + + var textGroup = root.children.holderTextGroup; + ctx.font = textGroup.properties.font.weight + ' ' + (dpr * textGroup.properties.font.size) + textGroup.properties.font.units + ' ' + textGroup.properties.font.family + ', monospace'; + ctx.fillStyle = textGroup.properties.fill; + + for (var lineKey in textGroup.children) { + var line = textGroup.children[lineKey]; + for (var wordKey in line.children) { + var word = line.children[wordKey]; + var x = dpr * (textGroup.x + line.x + word.x); + var y = dpr * (textGroup.y + line.y + word.y + (textGroup.properties.leading / 2)); + + ctx.fillText(word.properties.text, x, y); + } + } + + return canvas.toDataURL('image/png'); + }; + })(); + +/***/ }) +/******/ ]) +}); +; +(function(ctx, isMeteorPackage) { + if (isMeteorPackage) { + Holder = ctx.Holder; + } +})(this, typeof Meteor !== 'undefined' && typeof Package !== 'undefined'); diff --git a/resources/js/app.js b/resources/js/app.js index c2e652a..77b7716 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -25,6 +25,7 @@ document.querySelectorAll('.btn-clipboard').forEach((button) => { }); require('light-switch-bootstrap/switch'); +require('dropzone'); //window.lightswitchbootstrap(); //https://stackoverflow.com/questions/46643667/javascript-function-is-not-defined-with-laravel-mix diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js index 67fcfce..b2a2a71 100644 --- a/resources/js/bootstrap.js +++ b/resources/js/bootstrap.js @@ -1,7 +1,8 @@ window._ = require('lodash'); window.switch = require('light-switch-bootstrap/switch'); - +window.Dropzone = require('dropzone'); +Dropzone.autoDiscover = false; /** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the @@ -28,3 +29,5 @@ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // cluster: process.env.MIX_PUSHER_APP_CLUSTER, // forceTLS: true // }); + + diff --git a/resources/lang/de/messages-page.php b/resources/lang/de/messages-page.php index d7a5a2c..1170483 100644 --- a/resources/lang/de/messages-page.php +++ b/resources/lang/de/messages-page.php @@ -9,16 +9,20 @@ 'no_data' => 'Aktuell keine Geschenke zur Auswahl vorhanden', -'presenttable_image' => "Bild", - 'presenttable_title'=>"Titel", -'presenttable_status'=>"Status", -'presenttable_description'=>"Beschreibung", -'presenttable_links'=>"Links zum Geschenk bei Händlern oder Herstellern", -'presenttable_button_useit'=> "Geschenk wählen", -'presenttable_page_size'=>"Wähle die Anzahl Elemente auf einer Seite", -'presenttable_status_select'=>"Wähle einen Status", -'presenttable_reset_filters'=>"Filter zurücksetzen", -'presenttable_share'=>"Teilen", -'presenttable_button_details'=>"Details", + 'presenttable_image' => "Bild", + 'presenttable_title' => "Titel", + 'presenttable_status' => "Status", + 'presenttable_description' => "Beschreibung", + 'presenttable_links' => "Links zum Geschenk bei Händlern oder Herstellern", + 'presenttable_button_useit' => "Geschenk wählen", + 'presenttable_page_size' => "Wähle die Anzahl Elemente auf einer Seite", + 'presenttable_status_select' => "Wähle einen Status", + 'presenttable_reset_filters' => "Filter zurücksetzen", + 'presenttable_share' => "Teilen", + 'presenttable_button_details' => "Details", + + + 'menu_presents' => 'Geschenke', + 'menu_create_present' => 'Geschenk erstellen' ]; diff --git a/resources/lang/de/presents.php b/resources/lang/de/presents.php new file mode 100644 index 0000000..f18ebb8 --- /dev/null +++ b/resources/lang/de/presents.php @@ -0,0 +1,14 @@ + 'Geschenk Titel', + 'create_title' => ' Geschenk erstellen', + 'create_form_description' => 'Beschreibung', + 'form_button_remove_link' => 'Link löschen', + 'form_label_links' => 'Link zum Geschenk eintragen', + 'form_button_add_link' => 'Link hinzufügen', + 'create_form_links' => 'Links', + 'create_success' => 'Das Geschenkt ":Title" wurde erfolgreich unter der Nummer :Id angelegt', +]; diff --git a/resources/sass/app.scss b/resources/sass/app.scss index b2abbe6..4ee5ae8 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -1,2 +1,8 @@ @import "custom"; @import "~bootstrap/scss/bootstrap"; +@import '~dropzone/dist/dropzone.css'; + +.dropzone { + margin-bottom: 20px; + min-height: auto; +} diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 7dbda7c..3a4f79a 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -16,7 +16,8 @@ @livewireStyles - + + @if( !App::environment(['Production','prd','PROD'])) @endif @@ -39,7 +40,7 @@ @endif @if (isset($header)) - {{ $header }} + {{ $header }} @endif {{ $slot }} diff --git a/resources/views/navigation-menu.blade.php b/resources/views/navigation-menu.blade.php index e19f6d5..79f3d7f 100644 --- a/resources/views/navigation-menu.blade.php +++ b/resources/views/navigation-menu.blade.php @@ -36,7 +36,7 @@ class="navbar-toggler"