{"_id":"@whsmith/infrastructure","_rev":"40-2ad0f0747bb6cd4a5b2c38b4d7de02f6","name":"@whsmith/infrastructure","dist-tags":{"next":"3.0.5","latest":"3.5.8"},"versions":{"3.0.0":{"name":"@whsmith/infrastructure","version":"3.0.0","author":{"name":"WHSmith Development Team"},"bugs":{"url":"http://10.116.182.161/Applications/Infrastructure/issues"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"release":{"branch":"master","fallbackTags":{"next":"latest"}},"publishConfig":{"tag":"next"},"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"c570719b55daa72fd9f5610ee71ad7e4c813a85b","_id":"@whsmith/infrastructure@3.0.0","_shasum":"103528b5730aeb57569959aa0f5dad7d08f570b4","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.5.0","_npmUser":{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"},"dist":{"shasum":"103528b5730aeb57569959aa0f5dad7d08f570b4","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.0.tgz","fileCount":123,"unpackedSize":579685,"integrity":"sha512-gSI3cY1Y8izVCScXv3eJQsj1AhQ6Njgk3mg9nUARcLmjGVMGRqqQEidVGX3uSSzaSmn0Rr6zMTkhQG8Z497wFA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDXahiq80bmdeBLAHRylUHchS2NfjwYON+z2eiDZ6qhAIgOpKHdEoG7VAFogBRfIGWWMjue5WgmWnf+DUMr1aUu2Q="}]},"maintainers":[{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.0_1531482237740_0.6661787143474716"},"_hasShrinkwrap":false},"3.0.1":{"name":"@whsmith/infrastructure","version":"3.0.1","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"release":{"branch":"master","fallbackTags":{"next":"latest"}},"publishConfig":{"tag":"next"},"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"readme":"# Infrastructure\r\nInfrastructure is our core library for all SPA applications build within WHSmiths.  It provides a wide range of helper methods and tools as well as styles, directives and filters.\r\n\r\nAlso infrastructure has a range of extensions which you can read more about below.\r\n\r\n## Quick links\r\n- [Installation](#installation)\r\n- [Setup](#setup)\r\n- [Extensions](#extensions)\r\n  - [Custom ESLint Config](#eslint-config)\r\n  - [WHS Table](#whs-table)\r\n  - [WHS Datepicker](#whs-datepicker)\r\n  - [WHS Print](#whs-print)\r\n- [Support](#support)\r\n    - [FAQ](#faq)\r\n    - [Found a bug?](#found-a-bug)\r\n\r\n## Installation\r\n\r\nInstallation is easy as we have followed standards for all our plugin development.\r\n\r\n```sh\r\n$ npm install @whsmith/infrastructure --save\r\n```\r\n\r\nThis will install WHS Infrastructure to your node_modules.\r\n\r\nHowever it's likely infrastructure is already in your `package.json` file:\r\n\r\n```json\r\n\"@whsmith/infrastructure\": \"^3.0.0\",\r\n```\r\n\r\nVersion numbers use a sym versioning system so `major`.`minor`.`build`.\r\n\r\nBreaking changes will cause the `major` version update.\r\n\r\nNew features will cause a `minor` version update (unless the new feature causes a breaking change).\r\n\r\nBug fixes will cause a `build` version update.\r\n\r\nIt's important to take all this into account when selecting or changing your version It's also a good idea to check the `CHANGELOG.md` file for information on what has changed with breaking change information.\r\n\r\n# Setup\r\n\r\nNow you can import into your application (within your `app.js`):\r\n\r\n```js\r\nimport { infrastructure } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure]);\r\n```\r\n\r\nYou will also need to make sure that your `WHS.App.start` has the following line to load the config\r\n```js\r\nWHS.App.constant('loaderConfig', WHS.Loader);\r\n```\r\n\r\n# CORE\r\n\r\n## Directives\r\n### Error Box\r\nUsed to display error messages\r\n\r\n```html\r\n<error-box error=\"{errorObj}\" onDismiss=\"function()\"></error-box>\r\n```\r\n\r\n```js\r\nconst errorObj = {\r\n  ErrorMessage: 'Main message for error',\r\n  Errors: ['Multi pointed error', 'Listed below main message'],\r\n  HelpText: 'Error help text',\r\n  HelpLink: 'http://google.co.uk',\r\n  FullException: 'Full Stack Trace Exception',\r\n};\r\n```\r\n\r\n### ErrorSrc\r\nLoads a src if the original src fails to load.\r\n\r\n```html\r\n<img src=\"\" error-src=\"\" />\r\n```\r\n\r\n### Focus\r\nFocus element on load\r\n\r\n```html\r\n<input focus />\r\n<input focus-on />\r\n<input focus-me />\r\n```\r\n\r\n### Last Modified\r\n```html\r\n<last-modifed model=\"{obj}\"></last-modified>\r\n```\r\n\r\n```js\r\nconst obj = {\r\n  LastModifiedBy: 'User', // Display Name\r\n  LastModifiedOn: moment(), // moment date\r\n};\r\n```\r\n\r\n### Loading Wrapper\r\nUsed to wrap content loaded from a promise\r\n\r\n  - **Attributes**\r\n    - `busy` - Instance of the busy library.\r\n    - `content` - Model referance containing text or html to render while loading.\r\n    - `text` - Raw text to display while loading (`content` will overright this).\r\n    - `once` - If provided loading wrapper will load once and then ignore changes to the busy instance.\r\n\r\n```html\r\n<loading-wrapper busy=\"{busyInstance}\" content=\"htmlContentModel\" text=\"Loading Text\" once>\r\n  <!-- CONTENT -->\r\n</loading-wrapper>\r\n```\r\n\r\n### ngEnter\r\nAttaches an enter keybinding action to element that will trigger the referenced function or will remove enter actions if no function is provided.\r\n\r\n### numberInput\r\nRestrict input key press of anything but numbers.\r\n\r\nAllows: backspace, enter, escape, arrows, Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X.\r\n\r\n### ngPatternRestrict\r\nRestrict `ng-model` value to follow the `pattern` provided\r\n```html\r\n<input type=\"text\" ng-model=\"name\" pattern=\"([A-Z])\\w+\" ng-pattern-restrict>\r\n```\r\n\r\n### Spinner\r\nDisplays a simple spinner based on the expression or referenced value.\r\n```html\r\n<spin active=\"true/false\"></spin>\r\n```\r\n\r\n## Filters\r\n\r\n### checkOngoing\r\nDisplays `Ongoing`, `From {date}`, `Until {date}` or `Expired`.\r\n```html\r\n<span>{{date | checkOngoing:start:end</span>\r\n```\r\n\r\n### clearZero\r\nThis filter will clear any Zero (0) value.\r\n\r\n### company\r\nT = Travel, H = High Street, B = Both\r\n```html\r\n<span>{{data | company}}</span>\r\n```\r\n\r\n### dateUntil\r\nReturns date or `No End Date` if the date if same or after the extreme date `2999-12-31`.\r\n\r\n### groupBy\r\nBest used in the controller will group data by property with `key` and `data` mapping.\r\n\r\n```js\r\nconst grouped = $filter('groupBy')(data, 'myGroupProperty');\r\n// grouped.key = myGroupProperty\r\n// grouped.data = Array of grouped objects.\r\n```\r\n\r\n### htmlEscape\r\nEscape any html in value.\r\n\r\n### moment\r\nParse and return data in set format\r\n```html\r\n{{ '2017-04-01': moment:'DD/MM/YYYY':'YYYY/MM/DD' }}\r\n```\r\n`moment:format:parseFormat`\r\n\r\n### percent\r\nReturn the percent value from a decimal (defaults 1 decimal place & precision 1)\r\n\r\n`percent:decimals:precison`\r\n```html\r\n{{ 0.254242: percent:1 }}\r\n```\r\n\r\n### textReplace\r\nFind and replace text in value\r\n\r\n`textReplace:search:replace`\r\n```html\r\n{{ 'Some Text': textReplace:'Text':'Cake' }}\r\n```\r\n\r\n### titleCase\r\nTitle case input based on abbreviationsList\r\n\r\n`titleCase:abbreviationsList`\r\n\r\n### trim\r\nTrim value removing any whitespace\r\n\r\n### uniqueUrl\r\nAdds a unique queryString to the end of provided url value to prevent caching\r\n\r\n## Services\r\n\r\n### whsHelper\r\nThe helper is a must include for most application controllers as it inherits a lot of core services and useful functions.\r\n\r\n```js\r\n// Properties\r\nscope // Inherits $scope\r\nroot // Inherits $rootScope\r\nbusy // Inherits the busy service\r\nlogger // Inherits the whsLog service\r\nauth // Inherits the whsAuth service\r\ndialog // Inherits the whsDialog service\r\nutil // Inherits the whsUtil service\r\nconfig // Inherits the whsConfig service which inherits loaderConfig\r\nservice // Inherits the CommonService\r\n\r\n// Functions\r\nsendError(message, ...objs); // Will log error objs to console in DEBUG MODE otherwise will send email to developers with objs.\r\ngetSettings() // Returns all the application settings (DNN Settings)\r\ngetSetting(key) // Returns the selected application setting by key\r\ngetUser() // Returns current logged user details or null\r\nshowInfo(message) // Display information sweetalert with message\r\nshowQuestion(title, message) // Display question sweetalert with title and message\r\nshowSuccess(message) // Display success sweetalert with message\r\nshowSuccessToast(message, timer) // Display a successful toast with message and close after timer (ms) (Default: 3000)\r\nshowWarning(message) // Display warning (opps) sweetalert with message\r\nshowError(message, ...objs) // Display error sweetalert with message (first parameter), any additional parameters will be logged to console\r\nshowErrorToast(message, timer, ...objs) // Display a error toast with message and close after timer (ms) (Default: 3000)\r\nshowConfirm(options, promise) // Display a confirmation sweetalert giving the user Yes/No Options and executing a promise on yes.\r\ndebug(...) // Log all params to console\r\naddDebugVariable(name, value) // Add a variable to the debug page '/debug'\r\nlogError(...) // Log all params to console as error\r\n```\r\n\r\n### whsConfig\r\n```js\r\n// Properties\r\n. // Inherits the properties of loaderConfig\r\nappSettings[] // appSettings array of [key] = value (DNN Settings)\r\nDNNAccess // true/false based on if application running in DNN\r\n```\r\n\r\n### whsLog\r\nLog helper handles most console logging\r\n\r\n```js\r\nlog(...); // Prints all args to console as log/debug\r\ninfo(...); // Prints all args to console as info\r\nwarn(...); // Prints all args to console as warning\r\nerror(...); // Prints all args to console as error\r\ntable(...); // Exec console.table(data, [, columns])\r\ntrace(); // Print console trace to this point\r\nclear(); // Clear console\r\ntimeStart(key); // Start timer with key\r\ntimeStop(key); // End timer with key\r\n```\r\n\r\n### pageTitle\r\n\r\n```js\r\npageTitle(title, header, includeAppName) \r\n  // title = page name to display in window/tab title\r\n  // header = page header to display at top\r\n  // includeAppName = true/false - should application name be displayed after title in window/tab title.\r\n```\r\n\r\n### whsDialog\r\n\r\n```js\r\nconfirmModel(options) // Display a confirm model popup\r\n  // OPTIONS\r\n  //   - title: model title\r\n  //   - body: model body\r\n  //   - confirmButtonText: confirm button text, defaults : 'Confirm'\r\n  //   - cancelButtonText: cancel button text, defaults: 'Cancel' \r\n  //   - execute: promise function to run on confirm.\r\nalertMessageBox(options) // Display a alert model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nsuccessMessageBox(options) // Display a success model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nerrorMessageBox(options) // Display a error model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nmessageBox(caption, message, buttons) // Display a model popup message box\r\n  // caption: title of the model\r\n  // message: body of the model\r\n  // buttons: array of buttons\r\n  //   - cssClass: button classes\r\n  //   - result: button return result\r\n  //   - label: button text\r\nshowConfirm(options, promise) // Display a confirming SWAL (Sweetalert)\r\n  // OPTIONS\r\n  //   - title: title of the swal (defaults: 'Are You Sure')\r\n  //   - html: body of the swal (defaults: 'Please confirm you wish to complete this action')\r\n  //   - confirmButtonText (defaults: 'Yes')\r\n  //   - cancelButtonText (defaults: 'No')\r\n  // promise: promise function to run on confirm\r\ngetSwal()                 // Get an instance of `swal`\r\nshowSwal(options)         // Open a swal with the povided options\r\ncloseSwal()               // Close any open swal dialogs\r\ngetMixin(options)         // Create a swal mixin\r\nrunSwalQueue(items)       // Run array of swal options\r\nshowSwalError(message)    // Display validation error on swal\r\ntriggerSwal(options)      // Trigger a swal with defaults\r\nshowWarning(options)      // Display a 'Opps!' warning swal\r\nshowError(options)        // Display a 'Error!' error swal\r\nshowSuccess(options)      // Display a 'Success!' success swal\r\nshowInfo(options)         // Display a `Info` info swal\r\nshowQuestion(options)     // Display a question swal.\r\n```\r\n\r\n### whsLog\r\n\r\n```js\r\nlog(...)          // Prints log message to console\r\ninfo(...)         // Prints info messge to console\r\nwarn(...)         // Prints warn message to console\r\nerror(...)        // Prints error console message\r\ntable(...)        // Displays tabular data as a table\r\ntrace()           // Outputs a stack trace\r\nclear()           // Clear the Console\r\ntimeStart(name)   // Starts a timer with the name specified\r\ntimeStop(name)    // Stops the specified timer and logs the elapsed time\r\n\r\n```\r\n\r\n\r\n### whsUtil\r\n\r\n```js\r\nescapeHTML(string) // Escaped any html found in string\r\ntoBoolean(string)  // returns true/false based on string value\r\ngoBack() // Return to previous state or `app.default`\r\ncalculateWeekNumber(dateString) // Return the week number of date\r\ncalculateMonthNumber(dateString) // Return the month number of date\r\ncalculateYearNumber(dateString) // Return the year number of date\r\ncheckCalender(dateString) // Returns moment date plus the week, month and year numbers of the provided date.\r\n```\r\n\r\n### whsGateway\r\n\r\n```js\r\n// To initialize the gateway you must first create a new instance\r\nconst Gateway = whsGateway.create(clientName, version); // Client name is required, version will default to 'v1'.\r\n\r\n// Using the newly created instance you have access to the following methods.\r\nsetupUrl(url, client, version) // Return gateway url based on the url, client and version.  (client and version will use the instance values if not provided here).\r\n\r\nget(url, config)          // Trigger a HTTP GET Request\r\npost(url, data, config)   // Trigger a HTTP POST Request\r\nput(url, data, config)    // Trigger a HTTP PUT Request\r\ndelete(url, config)       // Trigger a HTTP DELETE Request\r\n```\r\n\r\n### whsAuth\r\n\r\n```js\r\ngetUser()  // Get current user object or null.\r\ngetUserIpAddress() // Return current user IP or ''.\r\ngetPermissions() // Return current application permissions\r\ndirectToLogin(target, targetParams) // Redirect to login page with the set return target & params.\r\ncheckAuth(required) // Check if current user is authenticated and redirect to login if not authenticated.\r\n```\r\n\r\n### busy\r\n\r\n```js\r\n// First initialize a busy instance\r\nconst isLoading = busy.create;\r\n\r\n// With your new instance you can add a promise(s)\r\nisLoading.promise(myPromiseFunction)\r\nisLoading.promises([promiseFunction1, promiseFunction2]);\r\n// When a promise is running the `isLoading.busy` state will update.\r\n\r\n// You can also force a loading error with:\r\nisLoading.showError(message) // Display message as error\r\nisLoading.showErrors(message, errors) // Display error message and errors array\r\nisLoading.clearError() // Will clear any errors\r\n```\r\n\r\n### pollLoop\r\n\r\n```js\r\n// First initialize a pollLoop instance\r\nconst loop = pollLoop.create($scope, () => functionThatReturnsAPromise(), 4000);\r\n//  Instance of angular scope\r\n//  Function that returns a promise to trigger on polling\r\n//  Time between polls (optional defaults to 4000/4secs)\r\n\r\n// PoolLoop Methods\r\nstart() // Start polling after the timer\r\nstartImmediately() // Start now\r\nstop() // Stop current instance\r\n```\r\n\r\n\r\n### CommonService\r\n\r\n```js\r\ngetTitleCaseDictionary() // Return `Common/TitleCaseDictionary` API method\r\nsendEmail(data) // Send email using `Common/SendEmail` API method\r\nsendErrorEmail(body, fromUser) // Sends error email to developers\r\n  // body: HTML Enabled email body\r\n  // fromUser: user object.\r\n```\r\n\r\n## Routing And Permissions\r\n\r\nWhen adding states you are giving a bunch of additional options you can add to extend your route.\r\n\r\n```js\r\n$stateProvider.state('app.sample', {\r\n  url: '/',\r\n  redirectTo: 'app.default', // Redirect to the desired state\r\n  defaultSubRoute: 'app.sample.page', // Redirect to the desired sub route (will retain parameters)\r\n  layout: {\r\n    title: 'Infrastructure', // Browser title to display for page\r\n    header: 'Infrastructure' // Page header to display for page\r\n  },\r\n  allowAnonymous: true // Flags that the page doesn't require authentication\r\n});\r\n```\r\n\r\nIn regards to permission it will closely relate to roles and inheritance:\r\n```json\r\n[\r\n  {\r\n      \"Name\": \"User\",\r\n      \"Description\": \"Groups that have default access to use and view the application\",\r\n      \"Roles\": [\"All\"],\r\n      \"Inherit\": \"Admin\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n      \"Name\": \"Admin\",\r\n      \"Description\": \"\",\r\n      \"Roles\": [\"Administrator\"],\r\n      \"Inherit\": \"\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n    \"Name\": \"StoresAdmin\",\r\n    \"Description\": \"Roles that have access to change there store\",\r\n    \"Roles\": [\"IntranetDeveloper\"],\r\n    \"Inherit\": \"Admin\",\r\n    \"AllowedAccess\": true\r\n  }\r\n]\r\n```\r\nIn the sample above: \r\n - `All` & `Administrator` will get the role `User`\r\n - `Administrator` will get the role `User`\r\n - `IntranetDeveloper` & `Administrator` will get the role `StoresAdmin`\r\n\r\n-----\r\n\r\n## Extensions\r\n\r\nThe Infrastructure plugin has an extendable feature, by default `whs-infrastructure` will use the `core` component this contains all the components we feel are required / commonly used in all our applications.\r\n\r\nYou then have the options of adding extra components to your application.\r\n\r\nBy using this method instead of just adding everything to the core plugin it means we can only include (and build) components that we require which will lower the size of our final bundled application.\r\n\r\nAlso when implementing extensions you can either attach them in your `app.js` so the extension is available app wide, or you can attach them to a specific area so only that area can use a specific extension.\r\n\r\n------------\r\n\r\n## ESLint Config\r\nOur custom ESLint config is simple to implement when using infrastructure and will allow for easily following our coding standards.\r\n\r\n### Installation\r\nOpen your `.eslintrc` file and replace content with the following:\r\n\r\n```json\r\n{\r\n  \"extends\": \"./node_modules/@whsmith/infrastructure/lint.js\"\r\n}\r\n```\r\n\r\nAnd your done.\r\n\r\n-------------\r\n\r\n## WHS Table\r\n\r\n### Usage\r\nImplementing the table extension is very simple by updating your current implementation of your infrastructure to the following:\r\n\r\n```js\r\nimport { infrastructure, whsTable } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsTable]);\r\n```\r\n\r\nOnce loaded you have access to all the directives avaliable for WHS Table.\r\n\r\n#### Basic Usage\r\n\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nrowCollection = [\r\n  {name: 'Test', number: 1},\r\n  {name: 'Test 2', number: 2},\r\n];\r\n```\r\n\r\nIt is recommended however to use `safe-src` to load your data and then loop from a separate generated array:\r\n\r\n```html\r\n<table whs-table=\"gridData\" safe-src=\"rowCollection\">\r\n  <tr ng-repeat=\"row in gridData\"></tr>\r\n</table>\r\n```\r\n\r\n#### Removing Row\r\n```js\r\nfunction removeRow(row) {\r\n  var index = rowCollection.indexOf(row);\r\n  if (index !== -1) {\r\n    rowCollection.splice(index, 1);\r\n  }\r\n}\r\n```\r\n\r\n#### Sorting Data\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nfunction searchNameFunc(value) {\r\n  // Value will equal the row object\r\n  // this will sort by the length of the name string\r\n  return value.name.length;\r\n}\r\n```\r\n\r\n#### Searching / Filtering Data\r\n```html\r\n<table whs-table=\"rowCollection\" ng-enter=\"\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"search for number\" whs-table-search=\"number\" /></th>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"global search\" whs-table-search /></th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\nAs shown above you can add a search for both single column searching and a global table search.\r\n\r\n#### Selecting Rows\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\" whs-table-select-row=\"row\" select-mode=\"single/multiple\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n````\r\nWhen a row is selected the row object with have a `isSelected` property set to true.\r\n\r\n#### Pagination\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n  <tfoot>\r\n    <tr>\r\n      <td colspan=\"2\">\r\n        <div whs-pagination items-per-page=\"10\" displayed-pages=\"5\" items-options=\"[5,10,20]\" current-page=\"1\" page-change=\"changeTriggerFunc()\"></div>\r\n      </td>\r\n    </tr>\r\n  </tfoot>\r\n</table>\r\n```\r\n\r\n#### Sticky Header\r\nYou can now fix your table headers allowing you to scroll large tables while keeping the header at the top of the screen.\r\n\r\n```html\r\n<div class=\"table-box\" whs-table=\"rowCollection\">\r\n  <div class=\"table-responsive\">\r\n    <table sticky-header=\"true/false\">\r\n      <thead>\r\n        <tr>\r\n          <th>Name</th>\r\n          <th>Number</th>\r\n        </tr>\r\n      </thead>\r\n      <tbody>\r\n        <tr ng-repeat=\"row in rowCollection\">\r\n          <td>{{ row.number }}</td>\r\n          <td>{{ row.name }}</td>\r\n        </tr>\r\n      </tbody>\r\n    </table>\r\n  </div>\r\n</div>\r\n```\r\n\r\n**NOTE** There are some factors to take note of when using the fixed header.\r\n\r\n2. If you plan to use a filter on the table you must move the `whs-table` directive above the table element (like in the example above).  If you don't plan to use filtering then it can be added to the table element as before.\r\n\r\n3. Your notice you have pass `true/false` into the directive this is so you can disable and enable the fixed header.  For example when you open search boxes you should disable to fixed header, then you can re-enable when you hide them again.\r\n\r\n-------------\r\n\r\n## WHS Datepicker\r\n\r\n```js\r\nimport { infrastructure, whsDatepicker } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsDatepicker]);\r\n```\r\n\r\n### Using Datepicker\r\n\r\n```html\r\n<div class=\"input-group\">\r\n  <input type=\"text\"\r\n    name=\"dateFieldName\"\r\n    id=\"dateFieldId\"\r\n    class=\"form-control\"\r\n    ng-model=\"modelProperty\"\r\n    whs-datepicker\r\n    format=\"DD/MM/YYYY\"\r\n    min-date=\"00/00/0000\"\r\n    max-date=\"00/00/0000\"\r\n    disabled=\"true/false\"\r\n    icon-btn=\"#dateField_btn\"\r\n    required />\r\n  <div class=\"input-group-btn\">\r\n    <button type=\"button\" class=\"btn btn-secondary\" id=\"dateField_btn\"><i class=\"fa fa-calendar\"></i></button>\r\n  </div>\r\n</div>\r\n```\r\n\r\n### Filters\r\n\r\n#### Moment Parse\r\n```html\r\n{{ date | momentParse : 'DD/MM/YYYY' }}\r\n```\r\n\r\n#### Moment UTC\r\n```html\r\n{{ date | momentUtc }}\r\n```\r\n\r\n#### Date Differance\r\n```html\r\n{{ fromDate | dateDifference : toDate : unit : usePrecision }}\r\n```\r\n\r\n#### Date Format\r\n```html\r\n{{ date | dateFormat : 'DD/MM/YYYY' }}\r\n```\r\n\r\n----\r\n\r\n## WHS Print\r\n\r\n### Installing WHS Print\r\n\r\nTo load whs-print you can extend your current loading instance of infrastructure.\r\n\r\n```js\r\nimport { infrastructure, whsPrint } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsPrint]);\r\n```\r\n\r\n### Using WHS Print\r\n\r\n#### printSection\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children visible for printing\r\n\r\n  ```html\r\n  <div>\r\n      <div print-section>\r\n        I'll print\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>I won't</div>\r\n  </div>\r\n  ```\r\n\r\n#### printOnly\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children only visible for printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-only>\r\n        I'll print, but until then nobody wants me\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>Me, too! Except that people still want to look at me in the meantime...</div>\r\n  </div>\r\n  ```\r\n#### printHide\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing, but it is replaced by blank space\r\n  \r\n  ```html\r\n  <div print-section>\r\n      <div print-hide>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, but those bozos upstairs are still taking up space</div>\r\n  </div>\r\n  ```\r\n\r\n#### printRemove\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-remove>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, and those bozos upstairs will finally stop making such a ruckus</div>\r\n  </div>\r\n  ```\r\n\r\n#### printIf\r\n  - **Directive type:** Attribute\r\n  - **Description**: Toggles print-visibility based on expression\r\n\r\n  ```html\r\n  <!--Pigs do not yet fly, so this div, despite having print-section, will not print-->\r\n  <div print-section print-if=\"pigsFly\"></div>\r\n  <!--ET IS the best, so this div will print, despite not having print-section-->\r\n  <div print-if=\"ETIsTheBest\"></div>\r\n  ```\r\n\r\n#### printBtn\r\n  - **Directive type:** Attribute\r\n  - **Description**: Adds onClick callback to element that will trigger printing\r\n\r\n  ```html\r\n  <button print-btn>Doesn't matter where you put me</button>\r\n  <span print-btn>I will make anything cause a print</span>\r\n  <p print-btn>to happen if you click me</p>\r\n  ```\r\n  \r\n#### printLandscape\r\n  - **Directive type:** Attribute\r\n  - **Description**: Will cause printing to be landscape instead of portrait\r\n\r\n  ```html\r\n  <button print-landscape>Doesn't matter where you put me</button>\r\n  <span print-landscape>I will cause any print</span>\r\n  <p print-landscape>to be landscape</p>\r\n  ```\r\n\r\n#### printAvoidBreak\r\n  - **Directive type:** Attribute\r\n  - **Description**: Prevents page breaks on element\r\n\r\n  ```html\r\n  <button print-avoid-break>This element won't get split by page breaks</button>\r\n  ```\r\n\r\n#### printBreakBefore\r\n  - **Directive type:** Attribute\r\n  - **Description**: Causes a page break before rendering the current element\r\n  \r\n  ```html\r\n  <div print-break-before>Print page will break before this element</div>\r\n  ```\r\n\r\n#### pageBreak\r\n  - **Directive type:** Element\r\n  - **Description**: Element to create a fixed point of a page break, dosn't render anything.\r\n\r\n  ```html\r\n  <div>Page 1</div>\r\n  <page-break></page-break>\r\n  <div>Page 2</div>\r\n  ```\r\n\r\n#### printTable\r\n  - **Directive type:** Attribute\r\n  - **Description**:\r\n    - Optimizes table for printing. This includes keeping 'td' cell content from being cut-off by page breaks.\r\n    - Must be passed an array scope object representing the data presented by the table.\r\n    - Column headers will persist between pages only if the ```<thead>``` and ```<tbody>``` tags are used correctly.\r\n\r\n  This example shows adjustments to an already-visible table in order to tailor it for printing\r\n\r\n  ```html\r\n  <table print-table=\"people\">\r\n    <thead>\r\n      <tr>\r\n        <td print-remove>Unwanted field</td>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td print-remove>{{person.unwantedInfo}}</td>\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n  This example shows a table made to only be visible during printing\r\n\r\n  ```html\r\n  <table print-table=\"people\" print-only>\r\n    <thead>\r\n      <tr>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n### PDFPrinter Service\r\nThe PDFPrinter service is a great tool for building and creating pdfs either to open, print or download.\r\n\r\nYou can either provide the document definitions yourself or load the default and use inbuild functions to build your pdf.\r\n\r\n#### Implementation\r\n\r\n````js\r\nclass SomeClass {\r\n  constructor(PDFPrinter) {\r\n    this.myPDF = PDFPrinter.create(); // Default\r\n\r\n    this.customPDF = PDFPrinter.create(documentDef); // Custom\r\n  }\r\n}\r\n````\r\n`documentDef` is a object using pdfmake, more information about properties you can use are on their website: http://pdfmake.org/playground.html\r\n\r\n\r\n#### Service Methods\r\n\r\nFrom your created PDF instance you will have access to the following methods which will effect the instance you are running from.\r\n\r\n##### print\r\n```js\r\nthis.myPDF.print(); // This will print the current PDF.\r\n```\r\n\r\n##### open\r\n```js\r\nthis.myPDF.open(); // This will open the current PDF.\r\n```\r\n\r\n##### download\r\n```js\r\nthis.myPDF.download(); // This will trigger a download for the current PDF.\r\n```\r\n\r\n##### AddElement\r\n  - **Parameters:** \r\n    - `element` - String or Object to add to the documentDef content.\r\n```js\r\nthis.myPdf.AddElement({\r\n  text: 'sub header text element with page break',\r\n  pageBreak: 'before',\r\n  style: 'subheader'\r\n});\r\n```\r\n\r\n##### AddPageHeader\r\n  - **Parameters:** \r\n    - `text` - Header text\r\n```js\r\nthis.myPdf.AddPageHeader('My PDF');\r\n```\r\n\r\n##### AddFooterPageCount\r\nAdd a page count at the footer of the PDF pages.\r\n```js\r\nthis.myPdf.AddFooterPageCount();\r\n```\r\n\r\n##### AddStyle\r\nAdds a style object to the pdf document.\r\n```js\r\nthis.myPdf.AddStyle('myStyle', {\r\n  bold: true;\r\n  alignment: 'center'\r\n});\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n\r\n```js\r\nthis.myPDF.AddHeader('A Header'); // Add a header to pdf content.\r\n```\r\n\r\n##### AddSubHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a sub header\r\n\r\n```js\r\nthis.myPDF.AddSubHeader('A Sub Header'); // Add a sub header to pdf content.\r\n```\r\n\r\n##### AddText\r\n  - **Parameters:** \r\n    - `text` - Text to display\r\n\r\n```js\r\nthis.myPDF.AddText('I want to write some text on my pdf.'); // Add text to pdf content.\r\n```\r\n\r\n##### AddList\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n    - `bold` - Should the list items be bold\r\n```js\r\nthis.myPDF.AddList(['item 1', 'item 2', 'item 3'], false);\r\n```\r\n\r\n##### AddSimpleTable\r\n   - **Parameters:** \r\n      - `tableBody` - Body array for the table object.\r\n\r\n```js\r\nvar data = [\r\n  ['Header1', 'Header2', 'Header3'], // First colum is the header\r\n  ['row1col1', 'row1col2', 'row2col3']\r\n];\r\nthis.myPDF.AddSimpleTable(data); // Creates a simple table based on the data provided\r\n```\r\n\r\n##### AddRawTable\r\n  - **Parameters:** \r\n    - `tableObj` - Table object.\r\n\r\n```js\r\nvar table = {\r\n  widths: ['*', 200, 50],\r\n  body: [\r\n    [\r\n      { text: 'Auto Width Column', style: 'tableHeader', alignment: 'left' },\r\n      { text: 'Smaller Column', style: 'tableHeader' },\r\n      { text: 'Tiny Column', style: 'tableHeader' }\r\n    ],\r\n    ['row1col1', 'row1col2', 'row2col3']\r\n  ]\r\n};\r\nthis.myPDF.AddRawTable(table); // Creates a table from the raw table object\r\n```\r\n\r\n##### CreateTableObj\r\nBy creating a table object you will gain a bunch of extensions methods to help build your table obj.  All methods can be chained and then ending with `Build()` to return the finished table obj use `getTable()`.\r\n\r\n  - **Parameters:**\r\n    - `tableObj` - Core table object, default will be provided if undefined\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj({\r\n  headerRows: 1,\r\n  style: 'table'\r\n});\r\n\r\n// Add methods here to build your table (see below)\r\ntableBuilder\r\n  .SetWidths([100, 200])\r\n  .AddHeader(['Header 1', 'Header 2'])\r\n  .AddRow(['Row1 Col1', 'Row1 Col2'])\r\n  .Build();\r\n\r\n// Add table to PDF\r\nthis.myPDF.AddRawTable(tableBuilder.getTable());\r\n```\r\n\r\nMethods available within the tableBuilder are as follows:\r\n\r\n##### SetWidths\r\n  - **Parameters:**\r\n    - `widths` - array of widths for each column, pass `'*'` for auto width.\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.SetWidths([100, 200, '*']);\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:**\r\n    - `header` - header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeader({ text: 'Header 1', style: 'tableHeader' });\r\n```\r\n\r\n##### AddHeaders\r\n  - **Parameters:**\r\n    - `headers` - array of header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeaders([\r\n  'Header 1',\r\n  { text: 'Header 2', alignment: 'center' }\r\n]);\r\n```\r\n\r\n##### AddRow\r\n  - **Parameters:**\r\n    - `row` - array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRow([\r\n  { text: 'Col1 Text', style: 'someStyle' },\r\n  { text: 'Col2 Text', bold: true },\r\n]);\r\n```\r\n\r\n##### AddRowSmart\r\nAdding a smart row will allow you to pass the raw object and then define how each column should be handled.\r\n  - **Parameters:**\r\n    - `data` - data objects (model)\r\n    - `columnOptions` - array of column options\r\n      - Object used the core pdfmake properties and also the following extension properties:\r\n        - `property` - property name from data object to use for column\r\n        - `run` - function to run property value through and display function response for column.\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRowSmart(obj, [\r\n  { property: 'id', bold: true }, // Column 1, load obj.id\r\n  { property: 'name' }, // Column 2, load obj.name\r\n  { text: 'NEW', style: 'statusGreen' } // Column 3 forced text value\r\n]);\r\n```\r\n\r\n##### AddRows\r\n  - **Parameters:**\r\n    - `rows` - array of array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRows([\r\n  ['Row1 Col1', 'Row1 Col2'],\r\n  [{ text: 'Row2 Col1', bold: true }, 'Row2 Col2' ]\r\n])\r\n```\r\n\r\n##### AddRowsSmart\r\nLike the `AddRowSmart` however it will allow you to pass an entire array of data with and single set of column options and it will add all the rows.\r\n  - **Parameters:**\r\n    - `data` - Array of objects\r\n    - `columnOptions` - array of column options\r\n\r\n##### AddImage\r\n  - **Parameters:** \r\n    - `imageUrl` - Url / Location of the image you want to display\r\n    - `width` - [*Optional*] Width to scale the image to.\r\n\r\n```js\r\nthis.myPDF.AddImage('myimage.jpg', 200);\r\n```\r\n","readmeFilename":"README.md","gitHead":"d055f7e779292d3035b3180befe20fda71ea60d8","_id":"@whsmith/infrastructure@3.0.1","_shasum":"fb09690dba3a85aaa5a857531e7556a953468d12","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.5.0","_npmUser":{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"},"dist":{"shasum":"fb09690dba3a85aaa5a857531e7556a953468d12","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.1.tgz","fileCount":121,"unpackedSize":587533,"integrity":"sha512-3/Uiy61v6VTUVNNcFI9/wqq769T0XRa6cfkO7ImQGyguCJ5s6CjibP2EK0AlgfAn3P91TNAkVxNNnnckDqet0Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZ+OvVtWpiYwmFj9GxCf91bxEZAa60vIXVqXW229dtiAIhAJf0QSzv+IznhPM6FoLR2Zif5eiwL3qQ1eJAnnwo/e6Q"}]},"maintainers":[{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.1_1531747276637_0.6176001602559935"},"_hasShrinkwrap":false},"3.0.2":{"name":"@whsmith/infrastructure","version":"3.0.2","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"release":{"branch":"master","fallbackTags":{"next":"latest"}},"publishConfig":{"tag":"next"},"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"readme":"# Infrastructure\r\nInfrastructure is our core library for all SPA applications build within WHSmiths.  It provides a wide range of helper methods and tools as well as styles, directives and filters.\r\n\r\nAlso infrastructure has a range of extensions which you can read more about below.\r\n\r\n## Quick links\r\n- [Installation](#installation)\r\n- [Setup](#setup)\r\n- [Extensions](#extensions)\r\n  - [Custom ESLint Config](#eslint-config)\r\n  - [WHS Table](#whs-table)\r\n  - [WHS Datepicker](#whs-datepicker)\r\n  - [WHS Print](#whs-print)\r\n- [Support](#support)\r\n    - [FAQ](#faq)\r\n    - [Found a bug?](#found-a-bug)\r\n\r\n## Installation\r\n\r\nInstallation is easy as we have followed standards for all our plugin development.\r\n\r\n```sh\r\n$ npm install @whsmith/infrastructure --save\r\n```\r\n\r\nThis will install WHS Infrastructure to your node_modules.\r\n\r\nHowever it's likely infrastructure is already in your `package.json` file:\r\n\r\n```json\r\n\"@whsmith/infrastructure\": \"^3.0.0\",\r\n```\r\n\r\nVersion numbers use a sym versioning system so `major`.`minor`.`build`.\r\n\r\nBreaking changes will cause the `major` version update.\r\n\r\nNew features will cause a `minor` version update (unless the new feature causes a breaking change).\r\n\r\nBug fixes will cause a `build` version update.\r\n\r\nIt's important to take all this into account when selecting or changing your version It's also a good idea to check the `CHANGELOG.md` file for information on what has changed with breaking change information.\r\n\r\n# Setup\r\n\r\nNow you can import into your application (within your `app.js`):\r\n\r\n```js\r\nimport { infrastructure } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure]);\r\n```\r\n\r\nYou will also need to make sure that your `WHS.App.start` has the following line to load the config\r\n```js\r\nWHS.App.constant('loaderConfig', WHS.Loader);\r\n```\r\n\r\n# CORE\r\n\r\n## Directives\r\n### Error Box\r\nUsed to display error messages\r\n\r\n```html\r\n<error-box error=\"{errorObj}\" onDismiss=\"function()\"></error-box>\r\n```\r\n\r\n```js\r\nconst errorObj = {\r\n  ErrorMessage: 'Main message for error',\r\n  Errors: ['Multi pointed error', 'Listed below main message'],\r\n  HelpText: 'Error help text',\r\n  HelpLink: 'http://google.co.uk',\r\n  FullException: 'Full Stack Trace Exception',\r\n};\r\n```\r\n\r\n### ErrorSrc\r\nLoads a src if the original src fails to load.\r\n\r\n```html\r\n<img src=\"\" error-src=\"\" />\r\n```\r\n\r\n### Focus\r\nFocus element on load\r\n\r\n```html\r\n<input focus />\r\n<input focus-on />\r\n<input focus-me />\r\n```\r\n\r\n### Last Modified\r\n```html\r\n<last-modifed model=\"{obj}\"></last-modified>\r\n```\r\n\r\n```js\r\nconst obj = {\r\n  LastModifiedBy: 'User', // Display Name\r\n  LastModifiedOn: moment(), // moment date\r\n};\r\n```\r\n\r\n### Loading Wrapper\r\nUsed to wrap content loaded from a promise\r\n\r\n  - **Attributes**\r\n    - `busy` - Instance of the busy library.\r\n    - `content` - Model referance containing text or html to render while loading.\r\n    - `text` - Raw text to display while loading (`content` will overright this).\r\n    - `once` - If provided loading wrapper will load once and then ignore changes to the busy instance.\r\n\r\n```html\r\n<loading-wrapper busy=\"{busyInstance}\" content=\"htmlContentModel\" text=\"Loading Text\" once>\r\n  <!-- CONTENT -->\r\n</loading-wrapper>\r\n```\r\n\r\n### ngEnter\r\nAttaches an enter keybinding action to element that will trigger the referenced function or will remove enter actions if no function is provided.\r\n\r\n### numberInput\r\nRestrict input key press of anything but numbers.\r\n\r\nAllows: backspace, enter, escape, arrows, Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X.\r\n\r\n### ngPatternRestrict\r\nRestrict `ng-model` value to follow the `pattern` provided\r\n```html\r\n<input type=\"text\" ng-model=\"name\" pattern=\"([A-Z])\\w+\" ng-pattern-restrict>\r\n```\r\n\r\n### Spinner\r\nDisplays a simple spinner based on the expression or referenced value.\r\n```html\r\n<spin active=\"true/false\"></spin>\r\n```\r\n\r\n## Filters\r\n\r\n### checkOngoing\r\nDisplays `Ongoing`, `From {date}`, `Until {date}` or `Expired`.\r\n```html\r\n<span>{{date | checkOngoing:start:end</span>\r\n```\r\n\r\n### clearZero\r\nThis filter will clear any Zero (0) value.\r\n\r\n### company\r\nT = Travel, H = High Street, B = Both\r\n```html\r\n<span>{{data | company}}</span>\r\n```\r\n\r\n### dateUntil\r\nReturns date or `No End Date` if the date if same or after the extreme date `2999-12-31`.\r\n\r\n### groupBy\r\nBest used in the controller will group data by property with `key` and `data` mapping.\r\n\r\n```js\r\nconst grouped = $filter('groupBy')(data, 'myGroupProperty');\r\n// grouped.key = myGroupProperty\r\n// grouped.data = Array of grouped objects.\r\n```\r\n\r\n### htmlEscape\r\nEscape any html in value.\r\n\r\n### moment\r\nParse and return data in set format\r\n```html\r\n{{ '2017-04-01': moment:'DD/MM/YYYY':'YYYY/MM/DD' }}\r\n```\r\n`moment:format:parseFormat`\r\n\r\n### percent\r\nReturn the percent value from a decimal (defaults 1 decimal place & precision 1)\r\n\r\n`percent:decimals:precison`\r\n```html\r\n{{ 0.254242: percent:1 }}\r\n```\r\n\r\n### textReplace\r\nFind and replace text in value\r\n\r\n`textReplace:search:replace`\r\n```html\r\n{{ 'Some Text': textReplace:'Text':'Cake' }}\r\n```\r\n\r\n### titleCase\r\nTitle case input based on abbreviationsList\r\n\r\n`titleCase:abbreviationsList`\r\n\r\n### trim\r\nTrim value removing any whitespace\r\n\r\n### uniqueUrl\r\nAdds a unique queryString to the end of provided url value to prevent caching\r\n\r\n## Services\r\n\r\n### whsHelper\r\nThe helper is a must include for most application controllers as it inherits a lot of core services and useful functions.\r\n\r\n```js\r\n// Properties\r\nscope // Inherits $scope\r\nroot // Inherits $rootScope\r\nbusy // Inherits the busy service\r\nlogger // Inherits the whsLog service\r\nauth // Inherits the whsAuth service\r\ndialog // Inherits the whsDialog service\r\nutil // Inherits the whsUtil service\r\nconfig // Inherits the whsConfig service which inherits loaderConfig\r\nservice // Inherits the CommonService\r\n\r\n// Functions\r\nsendError(message, ...objs); // Will log error objs to console in DEBUG MODE otherwise will send email to developers with objs.\r\ngetSettings() // Returns all the application settings (DNN Settings)\r\ngetSetting(key) // Returns the selected application setting by key\r\ngetUser() // Returns current logged user details or null\r\nshowInfo(message) // Display information sweetalert with message\r\nshowQuestion(title, message) // Display question sweetalert with title and message\r\nshowSuccess(message) // Display success sweetalert with message\r\nshowSuccessToast(message, timer) // Display a successful toast with message and close after timer (ms) (Default: 3000)\r\nshowWarning(message) // Display warning (opps) sweetalert with message\r\nshowError(message, ...objs) // Display error sweetalert with message (first parameter), any additional parameters will be logged to console\r\nshowErrorToast(message, timer, ...objs) // Display a error toast with message and close after timer (ms) (Default: 3000)\r\nshowConfirm(options, promise) // Display a confirmation sweetalert giving the user Yes/No Options and executing a promise on yes.\r\ndebug(...) // Log all params to console\r\naddDebugVariable(name, value) // Add a variable to the debug page '/debug'\r\nlogError(...) // Log all params to console as error\r\n```\r\n\r\n### whsConfig\r\n```js\r\n// Properties\r\n. // Inherits the properties of loaderConfig\r\nappSettings[] // appSettings array of [key] = value (DNN Settings)\r\nDNNAccess // true/false based on if application running in DNN\r\n```\r\n\r\n### whsLog\r\nLog helper handles most console logging\r\n\r\n```js\r\nlog(...); // Prints all args to console as log/debug\r\ninfo(...); // Prints all args to console as info\r\nwarn(...); // Prints all args to console as warning\r\nerror(...); // Prints all args to console as error\r\ntable(...); // Exec console.table(data, [, columns])\r\ntrace(); // Print console trace to this point\r\nclear(); // Clear console\r\ntimeStart(key); // Start timer with key\r\ntimeStop(key); // End timer with key\r\n```\r\n\r\n### pageTitle\r\n\r\n```js\r\npageTitle(title, header, includeAppName) \r\n  // title = page name to display in window/tab title\r\n  // header = page header to display at top\r\n  // includeAppName = true/false - should application name be displayed after title in window/tab title.\r\n```\r\n\r\n### whsDialog\r\n\r\n```js\r\nconfirmModel(options) // Display a confirm model popup\r\n  // OPTIONS\r\n  //   - title: model title\r\n  //   - body: model body\r\n  //   - confirmButtonText: confirm button text, defaults : 'Confirm'\r\n  //   - cancelButtonText: cancel button text, defaults: 'Cancel' \r\n  //   - execute: promise function to run on confirm.\r\nalertMessageBox(options) // Display a alert model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nsuccessMessageBox(options) // Display a success model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nerrorMessageBox(options) // Display a error model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nmessageBox(caption, message, buttons) // Display a model popup message box\r\n  // caption: title of the model\r\n  // message: body of the model\r\n  // buttons: array of buttons\r\n  //   - cssClass: button classes\r\n  //   - result: button return result\r\n  //   - label: button text\r\nshowConfirm(options, promise) // Display a confirming SWAL (Sweetalert)\r\n  // OPTIONS\r\n  //   - title: title of the swal (defaults: 'Are You Sure')\r\n  //   - html: body of the swal (defaults: 'Please confirm you wish to complete this action')\r\n  //   - confirmButtonText (defaults: 'Yes')\r\n  //   - cancelButtonText (defaults: 'No')\r\n  // promise: promise function to run on confirm\r\ngetSwal()                 // Get an instance of `swal`\r\nshowSwal(options)         // Open a swal with the povided options\r\ncloseSwal()               // Close any open swal dialogs\r\ngetMixin(options)         // Create a swal mixin\r\nrunSwalQueue(items)       // Run array of swal options\r\nshowSwalError(message)    // Display validation error on swal\r\ntriggerSwal(options)      // Trigger a swal with defaults\r\nshowWarning(options)      // Display a 'Opps!' warning swal\r\nshowError(options)        // Display a 'Error!' error swal\r\nshowSuccess(options)      // Display a 'Success!' success swal\r\nshowInfo(options)         // Display a `Info` info swal\r\nshowQuestion(options)     // Display a question swal.\r\n```\r\n\r\n### whsLog\r\n\r\n```js\r\nlog(...)          // Prints log message to console\r\ninfo(...)         // Prints info messge to console\r\nwarn(...)         // Prints warn message to console\r\nerror(...)        // Prints error console message\r\ntable(...)        // Displays tabular data as a table\r\ntrace()           // Outputs a stack trace\r\nclear()           // Clear the Console\r\ntimeStart(name)   // Starts a timer with the name specified\r\ntimeStop(name)    // Stops the specified timer and logs the elapsed time\r\n\r\n```\r\n\r\n\r\n### whsUtil\r\n\r\n```js\r\nescapeHTML(string) // Escaped any html found in string\r\ntoBoolean(string)  // returns true/false based on string value\r\ngoBack() // Return to previous state or `app.default`\r\ncalculateWeekNumber(dateString) // Return the week number of date\r\ncalculateMonthNumber(dateString) // Return the month number of date\r\ncalculateYearNumber(dateString) // Return the year number of date\r\ncheckCalender(dateString) // Returns moment date plus the week, month and year numbers of the provided date.\r\n```\r\n\r\n### whsGateway\r\n\r\n```js\r\n// To initialize the gateway you must first create a new instance\r\nconst Gateway = whsGateway.create(clientName, version); // Client name is required, version will default to 'v1'.\r\n\r\n// Using the newly created instance you have access to the following methods.\r\nsetupUrl(url, client, version) // Return gateway url based on the url, client and version.  (client and version will use the instance values if not provided here).\r\n\r\nget(url, config)          // Trigger a HTTP GET Request\r\npost(url, data, config)   // Trigger a HTTP POST Request\r\nput(url, data, config)    // Trigger a HTTP PUT Request\r\ndelete(url, config)       // Trigger a HTTP DELETE Request\r\n```\r\n\r\n### whsAuth\r\n\r\n```js\r\ngetUser()  // Get current user object or null.\r\ngetUserIpAddress() // Return current user IP or ''.\r\ngetPermissions() // Return current application permissions\r\ndirectToLogin(target, targetParams) // Redirect to login page with the set return target & params.\r\ncheckAuth(required) // Check if current user is authenticated and redirect to login if not authenticated.\r\n```\r\n\r\n### busy\r\n\r\n```js\r\n// First initialize a busy instance\r\nconst isLoading = busy.create;\r\n\r\n// With your new instance you can add a promise(s)\r\nisLoading.promise(myPromiseFunction)\r\nisLoading.promises([promiseFunction1, promiseFunction2]);\r\n// When a promise is running the `isLoading.busy` state will update.\r\n\r\n// You can also force a loading error with:\r\nisLoading.showError(message) // Display message as error\r\nisLoading.showErrors(message, errors) // Display error message and errors array\r\nisLoading.clearError() // Will clear any errors\r\n```\r\n\r\n### pollLoop\r\n\r\n```js\r\n// First initialize a pollLoop instance\r\nconst loop = pollLoop.create($scope, () => functionThatReturnsAPromise(), 4000);\r\n//  Instance of angular scope\r\n//  Function that returns a promise to trigger on polling\r\n//  Time between polls (optional defaults to 4000/4secs)\r\n\r\n// PoolLoop Methods\r\nstart() // Start polling after the timer\r\nstartImmediately() // Start now\r\nstop() // Stop current instance\r\n```\r\n\r\n\r\n### CommonService\r\n\r\n```js\r\ngetTitleCaseDictionary() // Return `Common/TitleCaseDictionary` API method\r\nsendEmail(data) // Send email using `Common/SendEmail` API method\r\nsendErrorEmail(body, fromUser) // Sends error email to developers\r\n  // body: HTML Enabled email body\r\n  // fromUser: user object.\r\n```\r\n\r\n## Routing And Permissions\r\n\r\nWhen adding states you are giving a bunch of additional options you can add to extend your route.\r\n\r\n```js\r\n$stateProvider.state('app.sample', {\r\n  url: '/',\r\n  redirectTo: 'app.default', // Redirect to the desired state\r\n  defaultSubRoute: 'app.sample.page', // Redirect to the desired sub route (will retain parameters)\r\n  layout: {\r\n    title: 'Infrastructure', // Browser title to display for page\r\n    header: 'Infrastructure' // Page header to display for page\r\n  },\r\n  allowAnonymous: true // Flags that the page doesn't require authentication\r\n});\r\n```\r\n\r\nIn regards to permission it will closely relate to roles and inheritance:\r\n```json\r\n[\r\n  {\r\n      \"Name\": \"User\",\r\n      \"Description\": \"Groups that have default access to use and view the application\",\r\n      \"Roles\": [\"All\"],\r\n      \"Inherit\": \"Admin\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n      \"Name\": \"Admin\",\r\n      \"Description\": \"\",\r\n      \"Roles\": [\"Administrator\"],\r\n      \"Inherit\": \"\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n    \"Name\": \"StoresAdmin\",\r\n    \"Description\": \"Roles that have access to change there store\",\r\n    \"Roles\": [\"IntranetDeveloper\"],\r\n    \"Inherit\": \"Admin\",\r\n    \"AllowedAccess\": true\r\n  }\r\n]\r\n```\r\nIn the sample above: \r\n - `All` & `Administrator` will get the role `User`\r\n - `Administrator` will get the role `User`\r\n - `IntranetDeveloper` & `Administrator` will get the role `StoresAdmin`\r\n\r\n-----\r\n\r\n## Extensions\r\n\r\nThe Infrastructure plugin has an extendable feature, by default `whs-infrastructure` will use the `core` component this contains all the components we feel are required / commonly used in all our applications.\r\n\r\nYou then have the options of adding extra components to your application.\r\n\r\nBy using this method instead of just adding everything to the core plugin it means we can only include (and build) components that we require which will lower the size of our final bundled application.\r\n\r\nAlso when implementing extensions you can either attach them in your `app.js` so the extension is available app wide, or you can attach them to a specific area so only that area can use a specific extension.\r\n\r\n------------\r\n\r\n## ESLint Config\r\nOur custom ESLint config is simple to implement when using infrastructure and will allow for easily following our coding standards.\r\n\r\n### Installation\r\nOpen your `.eslintrc` file and replace content with the following:\r\n\r\n```json\r\n{\r\n  \"extends\": \"./node_modules/@whsmith/infrastructure/lint.js\"\r\n}\r\n```\r\n\r\nAnd your done.\r\n\r\n-------------\r\n\r\n## WHS Table\r\n\r\n### Usage\r\nImplementing the table extension is very simple by updating your current implementation of your infrastructure to the following:\r\n\r\n```js\r\nimport { infrastructure, whsTable } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsTable]);\r\n```\r\n\r\nOnce loaded you have access to all the directives avaliable for WHS Table.\r\n\r\n#### Basic Usage\r\n\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nrowCollection = [\r\n  {name: 'Test', number: 1},\r\n  {name: 'Test 2', number: 2},\r\n];\r\n```\r\n\r\nIt is recommended however to use `safe-src` to load your data and then loop from a separate generated array:\r\n\r\n```html\r\n<table whs-table=\"gridData\" safe-src=\"rowCollection\">\r\n  <tr ng-repeat=\"row in gridData\"></tr>\r\n</table>\r\n```\r\n\r\n#### Removing Row\r\n```js\r\nfunction removeRow(row) {\r\n  var index = rowCollection.indexOf(row);\r\n  if (index !== -1) {\r\n    rowCollection.splice(index, 1);\r\n  }\r\n}\r\n```\r\n\r\n#### Sorting Data\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nfunction searchNameFunc(value) {\r\n  // Value will equal the row object\r\n  // this will sort by the length of the name string\r\n  return value.name.length;\r\n}\r\n```\r\n\r\n#### Searching / Filtering Data\r\n```html\r\n<table whs-table=\"rowCollection\" ng-enter=\"\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"search for number\" whs-table-search=\"number\" /></th>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"global search\" whs-table-search /></th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\nAs shown above you can add a search for both single column searching and a global table search.\r\n\r\n#### Selecting Rows\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\" whs-table-select-row=\"row\" select-mode=\"single/multiple\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n````\r\nWhen a row is selected the row object with have a `isSelected` property set to true.\r\n\r\n#### Pagination\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n  <tfoot>\r\n    <tr>\r\n      <td colspan=\"2\">\r\n        <div whs-pagination items-per-page=\"10\" displayed-pages=\"5\" items-options=\"[5,10,20]\" current-page=\"1\" page-change=\"changeTriggerFunc()\"></div>\r\n      </td>\r\n    </tr>\r\n  </tfoot>\r\n</table>\r\n```\r\n\r\n#### Sticky Header\r\nYou can now fix your table headers allowing you to scroll large tables while keeping the header at the top of the screen.\r\n\r\n```html\r\n<div class=\"table-box\" whs-table=\"rowCollection\">\r\n  <div class=\"table-responsive\">\r\n    <table sticky-header=\"true/false\">\r\n      <thead>\r\n        <tr>\r\n          <th>Name</th>\r\n          <th>Number</th>\r\n        </tr>\r\n      </thead>\r\n      <tbody>\r\n        <tr ng-repeat=\"row in rowCollection\">\r\n          <td>{{ row.number }}</td>\r\n          <td>{{ row.name }}</td>\r\n        </tr>\r\n      </tbody>\r\n    </table>\r\n  </div>\r\n</div>\r\n```\r\n\r\n**NOTE** There are some factors to take note of when using the fixed header.\r\n\r\n2. If you plan to use a filter on the table you must move the `whs-table` directive above the table element (like in the example above).  If you don't plan to use filtering then it can be added to the table element as before.\r\n\r\n3. Your notice you have pass `true/false` into the directive this is so you can disable and enable the fixed header.  For example when you open search boxes you should disable to fixed header, then you can re-enable when you hide them again.\r\n\r\n-------------\r\n\r\n## WHS Datepicker\r\n\r\n```js\r\nimport { infrastructure, whsDatepicker } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsDatepicker]);\r\n```\r\n\r\n### Using Datepicker\r\n\r\n```html\r\n<div class=\"input-group\">\r\n  <input type=\"text\"\r\n    name=\"dateFieldName\"\r\n    id=\"dateFieldId\"\r\n    class=\"form-control\"\r\n    ng-model=\"modelProperty\"\r\n    whs-datepicker\r\n    format=\"DD/MM/YYYY\"\r\n    min-date=\"00/00/0000\"\r\n    max-date=\"00/00/0000\"\r\n    disabled=\"true/false\"\r\n    icon-btn=\"#dateField_btn\"\r\n    required />\r\n  <div class=\"input-group-btn\">\r\n    <button type=\"button\" class=\"btn btn-secondary\" id=\"dateField_btn\"><i class=\"fa fa-calendar\"></i></button>\r\n  </div>\r\n</div>\r\n```\r\n\r\n### Filters\r\n\r\n#### Moment Parse\r\n```html\r\n{{ date | momentParse : 'DD/MM/YYYY' }}\r\n```\r\n\r\n#### Moment UTC\r\n```html\r\n{{ date | momentUtc }}\r\n```\r\n\r\n#### Date Differance\r\n```html\r\n{{ fromDate | dateDifference : toDate : unit : usePrecision }}\r\n```\r\n\r\n#### Date Format\r\n```html\r\n{{ date | dateFormat : 'DD/MM/YYYY' }}\r\n```\r\n\r\n----\r\n\r\n## WHS Print\r\n\r\n### Installing WHS Print\r\n\r\nTo load whs-print you can extend your current loading instance of infrastructure.\r\n\r\n```js\r\nimport { infrastructure, whsPrint } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsPrint]);\r\n```\r\n\r\n### Using WHS Print\r\n\r\n#### printSection\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children visible for printing\r\n\r\n  ```html\r\n  <div>\r\n      <div print-section>\r\n        I'll print\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>I won't</div>\r\n  </div>\r\n  ```\r\n\r\n#### printOnly\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children only visible for printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-only>\r\n        I'll print, but until then nobody wants me\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>Me, too! Except that people still want to look at me in the meantime...</div>\r\n  </div>\r\n  ```\r\n#### printHide\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing, but it is replaced by blank space\r\n  \r\n  ```html\r\n  <div print-section>\r\n      <div print-hide>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, but those bozos upstairs are still taking up space</div>\r\n  </div>\r\n  ```\r\n\r\n#### printRemove\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-remove>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, and those bozos upstairs will finally stop making such a ruckus</div>\r\n  </div>\r\n  ```\r\n\r\n#### printIf\r\n  - **Directive type:** Attribute\r\n  - **Description**: Toggles print-visibility based on expression\r\n\r\n  ```html\r\n  <!--Pigs do not yet fly, so this div, despite having print-section, will not print-->\r\n  <div print-section print-if=\"pigsFly\"></div>\r\n  <!--ET IS the best, so this div will print, despite not having print-section-->\r\n  <div print-if=\"ETIsTheBest\"></div>\r\n  ```\r\n\r\n#### printBtn\r\n  - **Directive type:** Attribute\r\n  - **Description**: Adds onClick callback to element that will trigger printing\r\n\r\n  ```html\r\n  <button print-btn>Doesn't matter where you put me</button>\r\n  <span print-btn>I will make anything cause a print</span>\r\n  <p print-btn>to happen if you click me</p>\r\n  ```\r\n  \r\n#### printLandscape\r\n  - **Directive type:** Attribute\r\n  - **Description**: Will cause printing to be landscape instead of portrait\r\n\r\n  ```html\r\n  <button print-landscape>Doesn't matter where you put me</button>\r\n  <span print-landscape>I will cause any print</span>\r\n  <p print-landscape>to be landscape</p>\r\n  ```\r\n\r\n#### printAvoidBreak\r\n  - **Directive type:** Attribute\r\n  - **Description**: Prevents page breaks on element\r\n\r\n  ```html\r\n  <button print-avoid-break>This element won't get split by page breaks</button>\r\n  ```\r\n\r\n#### printBreakBefore\r\n  - **Directive type:** Attribute\r\n  - **Description**: Causes a page break before rendering the current element\r\n  \r\n  ```html\r\n  <div print-break-before>Print page will break before this element</div>\r\n  ```\r\n\r\n#### pageBreak\r\n  - **Directive type:** Element\r\n  - **Description**: Element to create a fixed point of a page break, dosn't render anything.\r\n\r\n  ```html\r\n  <div>Page 1</div>\r\n  <page-break></page-break>\r\n  <div>Page 2</div>\r\n  ```\r\n\r\n#### printTable\r\n  - **Directive type:** Attribute\r\n  - **Description**:\r\n    - Optimizes table for printing. This includes keeping 'td' cell content from being cut-off by page breaks.\r\n    - Must be passed an array scope object representing the data presented by the table.\r\n    - Column headers will persist between pages only if the ```<thead>``` and ```<tbody>``` tags are used correctly.\r\n\r\n  This example shows adjustments to an already-visible table in order to tailor it for printing\r\n\r\n  ```html\r\n  <table print-table=\"people\">\r\n    <thead>\r\n      <tr>\r\n        <td print-remove>Unwanted field</td>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td print-remove>{{person.unwantedInfo}}</td>\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n  This example shows a table made to only be visible during printing\r\n\r\n  ```html\r\n  <table print-table=\"people\" print-only>\r\n    <thead>\r\n      <tr>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n### PDFPrinter Service\r\nThe PDFPrinter service is a great tool for building and creating pdfs either to open, print or download.\r\n\r\nYou can either provide the document definitions yourself or load the default and use inbuild functions to build your pdf.\r\n\r\n#### Implementation\r\n\r\n````js\r\nclass SomeClass {\r\n  constructor(PDFPrinter) {\r\n    this.myPDF = PDFPrinter.create(); // Default\r\n\r\n    this.customPDF = PDFPrinter.create(documentDef); // Custom\r\n  }\r\n}\r\n````\r\n`documentDef` is a object using pdfmake, more information about properties you can use are on their website: http://pdfmake.org/playground.html\r\n\r\n\r\n#### Service Methods\r\n\r\nFrom your created PDF instance you will have access to the following methods which will effect the instance you are running from.\r\n\r\n##### print\r\n```js\r\nthis.myPDF.print(); // This will print the current PDF.\r\n```\r\n\r\n##### open\r\n```js\r\nthis.myPDF.open(); // This will open the current PDF.\r\n```\r\n\r\n##### download\r\n```js\r\nthis.myPDF.download(); // This will trigger a download for the current PDF.\r\n```\r\n\r\n##### AddElement\r\n  - **Parameters:** \r\n    - `element` - String or Object to add to the documentDef content.\r\n```js\r\nthis.myPdf.AddElement({\r\n  text: 'sub header text element with page break',\r\n  pageBreak: 'before',\r\n  style: 'subheader'\r\n});\r\n```\r\n\r\n##### AddPageHeader\r\n  - **Parameters:** \r\n    - `text` - Header text\r\n```js\r\nthis.myPdf.AddPageHeader('My PDF');\r\n```\r\n\r\n##### AddFooterPageCount\r\nAdd a page count at the footer of the PDF pages.\r\n```js\r\nthis.myPdf.AddFooterPageCount();\r\n```\r\n\r\n##### AddStyle\r\nAdds a style object to the pdf document.\r\n```js\r\nthis.myPdf.AddStyle('myStyle', {\r\n  bold: true;\r\n  alignment: 'center'\r\n});\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n\r\n```js\r\nthis.myPDF.AddHeader('A Header'); // Add a header to pdf content.\r\n```\r\n\r\n##### AddSubHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a sub header\r\n\r\n```js\r\nthis.myPDF.AddSubHeader('A Sub Header'); // Add a sub header to pdf content.\r\n```\r\n\r\n##### AddText\r\n  - **Parameters:** \r\n    - `text` - Text to display\r\n\r\n```js\r\nthis.myPDF.AddText('I want to write some text on my pdf.'); // Add text to pdf content.\r\n```\r\n\r\n##### AddList\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n    - `bold` - Should the list items be bold\r\n```js\r\nthis.myPDF.AddList(['item 1', 'item 2', 'item 3'], false);\r\n```\r\n\r\n##### AddSimpleTable\r\n   - **Parameters:** \r\n      - `tableBody` - Body array for the table object.\r\n\r\n```js\r\nvar data = [\r\n  ['Header1', 'Header2', 'Header3'], // First colum is the header\r\n  ['row1col1', 'row1col2', 'row2col3']\r\n];\r\nthis.myPDF.AddSimpleTable(data); // Creates a simple table based on the data provided\r\n```\r\n\r\n##### AddRawTable\r\n  - **Parameters:** \r\n    - `tableObj` - Table object.\r\n\r\n```js\r\nvar table = {\r\n  widths: ['*', 200, 50],\r\n  body: [\r\n    [\r\n      { text: 'Auto Width Column', style: 'tableHeader', alignment: 'left' },\r\n      { text: 'Smaller Column', style: 'tableHeader' },\r\n      { text: 'Tiny Column', style: 'tableHeader' }\r\n    ],\r\n    ['row1col1', 'row1col2', 'row2col3']\r\n  ]\r\n};\r\nthis.myPDF.AddRawTable(table); // Creates a table from the raw table object\r\n```\r\n\r\n##### CreateTableObj\r\nBy creating a table object you will gain a bunch of extensions methods to help build your table obj.  All methods can be chained and then ending with `Build()` to return the finished table obj use `getTable()`.\r\n\r\n  - **Parameters:**\r\n    - `tableObj` - Core table object, default will be provided if undefined\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj({\r\n  headerRows: 1,\r\n  style: 'table'\r\n});\r\n\r\n// Add methods here to build your table (see below)\r\ntableBuilder\r\n  .SetWidths([100, 200])\r\n  .AddHeader(['Header 1', 'Header 2'])\r\n  .AddRow(['Row1 Col1', 'Row1 Col2'])\r\n  .Build();\r\n\r\n// Add table to PDF\r\nthis.myPDF.AddRawTable(tableBuilder.getTable());\r\n```\r\n\r\nMethods available within the tableBuilder are as follows:\r\n\r\n##### SetWidths\r\n  - **Parameters:**\r\n    - `widths` - array of widths for each column, pass `'*'` for auto width.\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.SetWidths([100, 200, '*']);\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:**\r\n    - `header` - header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeader({ text: 'Header 1', style: 'tableHeader' });\r\n```\r\n\r\n##### AddHeaders\r\n  - **Parameters:**\r\n    - `headers` - array of header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeaders([\r\n  'Header 1',\r\n  { text: 'Header 2', alignment: 'center' }\r\n]);\r\n```\r\n\r\n##### AddRow\r\n  - **Parameters:**\r\n    - `row` - array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRow([\r\n  { text: 'Col1 Text', style: 'someStyle' },\r\n  { text: 'Col2 Text', bold: true },\r\n]);\r\n```\r\n\r\n##### AddRowSmart\r\nAdding a smart row will allow you to pass the raw object and then define how each column should be handled.\r\n  - **Parameters:**\r\n    - `data` - data objects (model)\r\n    - `columnOptions` - array of column options\r\n      - Object used the core pdfmake properties and also the following extension properties:\r\n        - `property` - property name from data object to use for column\r\n        - `run` - function to run property value through and display function response for column.\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRowSmart(obj, [\r\n  { property: 'id', bold: true }, // Column 1, load obj.id\r\n  { property: 'name' }, // Column 2, load obj.name\r\n  { text: 'NEW', style: 'statusGreen' } // Column 3 forced text value\r\n]);\r\n```\r\n\r\n##### AddRows\r\n  - **Parameters:**\r\n    - `rows` - array of array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRows([\r\n  ['Row1 Col1', 'Row1 Col2'],\r\n  [{ text: 'Row2 Col1', bold: true }, 'Row2 Col2' ]\r\n])\r\n```\r\n\r\n##### AddRowsSmart\r\nLike the `AddRowSmart` however it will allow you to pass an entire array of data with and single set of column options and it will add all the rows.\r\n  - **Parameters:**\r\n    - `data` - Array of objects\r\n    - `columnOptions` - array of column options\r\n\r\n##### AddImage\r\n  - **Parameters:** \r\n    - `imageUrl` - Url / Location of the image you want to display\r\n    - `width` - [*Optional*] Width to scale the image to.\r\n\r\n```js\r\nthis.myPDF.AddImage('myimage.jpg', 200);\r\n```\r\n","readmeFilename":"README.md","gitHead":"d055f7e779292d3035b3180befe20fda71ea60d8","_id":"@whsmith/infrastructure@3.0.2","_shasum":"a665fdf2f9e27dd4d33691f226ace8f79c2e971e","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.5.0","_npmUser":{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"},"dist":{"shasum":"a665fdf2f9e27dd4d33691f226ace8f79c2e971e","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.2.tgz","fileCount":121,"unpackedSize":587447,"integrity":"sha512-lc8Uy5NxVrOZmKh04ocKsERjkVoyki6glQs+ldu2NT/75fO8gxKZAFOd5bkNceRXXBpQRS+EHIldpq0X/VXqOA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpUAHO6L2gZvel6t1B0oMPAuBELWZWEvcPN9KDSS7ucwIhAONHZ3OeJp79yfZUB4zn2gysht0bw04TPmy7usGDeTkE"}]},"maintainers":[{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.2_1531748294993_0.17706307704696833"},"_hasShrinkwrap":false},"3.0.3":{"name":"@whsmith/infrastructure","version":"3.0.3","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"release":{"branch":"master","fallbackTags":{"next":"latest"}},"publishConfig":{"tag":"next"},"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"8c4b9b1d502dde34269da16dc6bfe9f6e28844c6","_id":"@whsmith/infrastructure@3.0.3","_shasum":"3317d9c0e33a1abdc77f597138df5a0e3ab79630","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.5.0","_npmUser":{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"},"dist":{"shasum":"3317d9c0e33a1abdc77f597138df5a0e3ab79630","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.3.tgz","fileCount":121,"unpackedSize":587447,"integrity":"sha512-bkyz95K0UOdU3PtNc+5x6EWVapIy4wKmOaQKKHqz1fW6ClVPJ4/s7f6f19MI8zg76FUtnkOWv4LZSG7/a7ZWgw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDoslGScijHcb3daEDUQEaXcD1m+5gkHi18CWCqy2xxSwIhAO9XUZvCVn0iiMqo0gBXRpyUHOTckWOw4XZy81exk/U8"}]},"maintainers":[{"name":"aaron.barnaby","email":"aaron.barnaby@whsmith.co.uk"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.3_1531748548390_0.9321297657452656"},"_hasShrinkwrap":false},"3.0.4":{"name":"@whsmith/infrastructure","version":"3.0.4","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"release":{"branch":"master","fallbackTags":{"next":"latest"}},"publishConfig":{"tag":"next"},"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"64c5ee83576264f1ef3def680dbcde70b4eed97d","readme":"# Infrastructure\r\nInfrastructure is our core library for all SPA applications build within WHSmiths.  It provides a wide range of helper methods and tools as well as styles, directives and filters.\r\n\r\nAlso infrastructure has a range of extensions which you can read more about below.\r\n\r\n## Quick links\r\n- [Installation](#installation)\r\n- [Setup](#setup)\r\n- [Extensions](#extensions)\r\n  - [Custom ESLint Config](#eslint-config)\r\n  - [WHS Table](#whs-table)\r\n  - [WHS Datepicker](#whs-datepicker)\r\n  - [WHS Print](#whs-print)\r\n- [Support](#support)\r\n    - [FAQ](#faq)\r\n    - [Found a bug?](#found-a-bug)\r\n\r\n## Installation\r\n\r\nInstallation is easy as we have followed standards for all our plugin development.\r\n\r\n```sh\r\n$ npm install @whsmith/infrastructure --save\r\n```\r\n\r\nThis will install WHS Infrastructure to your node_modules.\r\n\r\nHowever it's likely infrastructure is already in your `package.json` file:\r\n\r\n```json\r\n\"@whsmith/infrastructure\": \"^3.0.0\",\r\n```\r\n\r\nVersion numbers use a sym versioning system so `major`.`minor`.`build`.\r\n\r\nBreaking changes will cause the `major` version update.\r\n\r\nNew features will cause a `minor` version update (unless the new feature causes a breaking change).\r\n\r\nBug fixes will cause a `build` version update.\r\n\r\nIt's important to take all this into account when selecting or changing your version It's also a good idea to check the `CHANGELOG.md` file for information on what has changed with breaking change information.\r\n\r\n# Setup\r\n\r\nNow you can import into your application (within your `app.js`):\r\n\r\n```js\r\nimport { infrastructure } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure]);\r\n```\r\n\r\nYou will also need to make sure that your `WHS.App.start` has the following line to load the config\r\n```js\r\nWHS.App.constant('loaderConfig', WHS.Loader);\r\n```\r\n\r\n# CORE\r\n\r\n## Directives\r\n### Error Box\r\nUsed to display error messages\r\n\r\n```html\r\n<error-box error=\"{errorObj}\" onDismiss=\"function()\"></error-box>\r\n```\r\n\r\n```js\r\nconst errorObj = {\r\n  ErrorMessage: 'Main message for error',\r\n  Errors: ['Multi pointed error', 'Listed below main message'],\r\n  HelpText: 'Error help text',\r\n  HelpLink: 'http://google.co.uk',\r\n  FullException: 'Full Stack Trace Exception',\r\n};\r\n```\r\n\r\n### ErrorSrc\r\nLoads a src if the original src fails to load.\r\n\r\n```html\r\n<img src=\"\" error-src=\"\" />\r\n```\r\n\r\n### Focus\r\nFocus element on load\r\n\r\n```html\r\n<input focus />\r\n<input focus-on />\r\n<input focus-me />\r\n```\r\n\r\n### Last Modified\r\n```html\r\n<last-modifed model=\"{obj}\"></last-modified>\r\n```\r\n\r\n```js\r\nconst obj = {\r\n  LastModifiedBy: 'User', // Display Name\r\n  LastModifiedOn: moment(), // moment date\r\n};\r\n```\r\n\r\n### Loading Wrapper\r\nUsed to wrap content loaded from a promise\r\n\r\n  - **Attributes**\r\n    - `busy` - Instance of the busy library.\r\n    - `content` - Model referance containing text or html to render while loading.\r\n    - `text` - Raw text to display while loading (`content` will overright this).\r\n    - `once` - If provided loading wrapper will load once and then ignore changes to the busy instance.\r\n\r\n```html\r\n<loading-wrapper busy=\"{busyInstance}\" content=\"htmlContentModel\" text=\"Loading Text\" once>\r\n  <!-- CONTENT -->\r\n</loading-wrapper>\r\n```\r\n\r\n### ngEnter\r\nAttaches an enter keybinding action to element that will trigger the referenced function or will remove enter actions if no function is provided.\r\n\r\n### numberInput\r\nRestrict input key press of anything but numbers.\r\n\r\nAllows: backspace, enter, escape, arrows, Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X.\r\n\r\n### ngPatternRestrict\r\nRestrict `ng-model` value to follow the `pattern` provided\r\n```html\r\n<input type=\"text\" ng-model=\"name\" pattern=\"([A-Z])\\w+\" ng-pattern-restrict>\r\n```\r\n\r\n### Spinner\r\nDisplays a simple spinner based on the expression or referenced value.\r\n```html\r\n<spin active=\"true/false\"></spin>\r\n```\r\n\r\n## Filters\r\n\r\n### checkOngoing\r\nDisplays `Ongoing`, `From {date}`, `Until {date}` or `Expired`.\r\n```html\r\n<span>{{date | checkOngoing:start:end</span>\r\n```\r\n\r\n### clearZero\r\nThis filter will clear any Zero (0) value.\r\n\r\n### company\r\nT = Travel, H = High Street, B = Both\r\n```html\r\n<span>{{data | company}}</span>\r\n```\r\n\r\n### dateUntil\r\nReturns date or `No End Date` if the date if same or after the extreme date `2999-12-31`.\r\n\r\n### groupBy\r\nBest used in the controller will group data by property with `key` and `data` mapping.\r\n\r\n```js\r\nconst grouped = $filter('groupBy')(data, 'myGroupProperty');\r\n// grouped.key = myGroupProperty\r\n// grouped.data = Array of grouped objects.\r\n```\r\n\r\n### htmlEscape\r\nEscape any html in value.\r\n\r\n### moment\r\nParse and return data in set format\r\n```html\r\n{{ '2017-04-01': moment:'DD/MM/YYYY':'YYYY/MM/DD' }}\r\n```\r\n`moment:format:parseFormat`\r\n\r\n### percent\r\nReturn the percent value from a decimal (defaults 1 decimal place & precision 1)\r\n\r\n`percent:decimals:precison`\r\n```html\r\n{{ 0.254242: percent:1 }}\r\n```\r\n\r\n### textReplace\r\nFind and replace text in value\r\n\r\n`textReplace:search:replace`\r\n```html\r\n{{ 'Some Text': textReplace:'Text':'Cake' }}\r\n```\r\n\r\n### titleCase\r\nTitle case input based on abbreviationsList\r\n\r\n`titleCase:abbreviationsList`\r\n\r\n### trim\r\nTrim value removing any whitespace\r\n\r\n### uniqueUrl\r\nAdds a unique queryString to the end of provided url value to prevent caching\r\n\r\n## Services\r\n\r\n### whsHelper\r\nThe helper is a must include for most application controllers as it inherits a lot of core services and useful functions.\r\n\r\n```js\r\n// Properties\r\nscope // Inherits $scope\r\nroot // Inherits $rootScope\r\nbusy // Inherits the busy service\r\nlogger // Inherits the whsLog service\r\nauth // Inherits the whsAuth service\r\ndialog // Inherits the whsDialog service\r\nutil // Inherits the whsUtil service\r\nconfig // Inherits the whsConfig service which inherits loaderConfig\r\nservice // Inherits the CommonService\r\n\r\n// Functions\r\nsendError(message, ...objs); // Will log error objs to console in DEBUG MODE otherwise will send email to developers with objs.\r\ngetSettings() // Returns all the application settings (DNN Settings)\r\ngetSetting(key) // Returns the selected application setting by key\r\ngetUser() // Returns current logged user details or null\r\nshowInfo(message) // Display information sweetalert with message\r\nshowQuestion(title, message) // Display question sweetalert with title and message\r\nshowSuccess(message) // Display success sweetalert with message\r\nshowSuccessToast(message, timer) // Display a successful toast with message and close after timer (ms) (Default: 3000)\r\nshowWarning(message) // Display warning (opps) sweetalert with message\r\nshowError(message, ...objs) // Display error sweetalert with message (first parameter), any additional parameters will be logged to console\r\nshowErrorToast(message, timer, ...objs) // Display a error toast with message and close after timer (ms) (Default: 3000)\r\nshowConfirm(options, promise) // Display a confirmation sweetalert giving the user Yes/No Options and executing a promise on yes.\r\ndebug(...) // Log all params to console\r\naddDebugVariable(name, value) // Add a variable to the debug page '/debug'\r\nlogError(...) // Log all params to console as error\r\n```\r\n\r\n### whsConfig\r\n```js\r\n// Properties\r\n. // Inherits the properties of loaderConfig\r\nappSettings[] // appSettings array of [key] = value (DNN Settings)\r\nDNNAccess // true/false based on if application running in DNN\r\n```\r\n\r\n### whsLog\r\nLog helper handles most console logging\r\n\r\n```js\r\nlog(...); // Prints all args to console as log/debug\r\ninfo(...); // Prints all args to console as info\r\nwarn(...); // Prints all args to console as warning\r\nerror(...); // Prints all args to console as error\r\ntable(...); // Exec console.table(data, [, columns])\r\ntrace(); // Print console trace to this point\r\nclear(); // Clear console\r\ntimeStart(key); // Start timer with key\r\ntimeStop(key); // End timer with key\r\n```\r\n\r\n### pageTitle\r\n\r\n```js\r\npageTitle(title, header, includeAppName) \r\n  // title = page name to display in window/tab title\r\n  // header = page header to display at top\r\n  // includeAppName = true/false - should application name be displayed after title in window/tab title.\r\n```\r\n\r\n### whsDialog\r\n\r\n```js\r\nconfirmModel(options) // Display a confirm model popup\r\n  // OPTIONS\r\n  //   - title: model title\r\n  //   - body: model body\r\n  //   - confirmButtonText: confirm button text, defaults : 'Confirm'\r\n  //   - cancelButtonText: cancel button text, defaults: 'Cancel' \r\n  //   - execute: promise function to run on confirm.\r\nalertMessageBox(options) // Display a alert model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nsuccessMessageBox(options) // Display a success model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nerrorMessageBox(options) // Display a error model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nmessageBox(caption, message, buttons) // Display a model popup message box\r\n  // caption: title of the model\r\n  // message: body of the model\r\n  // buttons: array of buttons\r\n  //   - cssClass: button classes\r\n  //   - result: button return result\r\n  //   - label: button text\r\nshowConfirm(options, promise) // Display a confirming SWAL (Sweetalert)\r\n  // OPTIONS\r\n  //   - title: title of the swal (defaults: 'Are You Sure')\r\n  //   - html: body of the swal (defaults: 'Please confirm you wish to complete this action')\r\n  //   - confirmButtonText (defaults: 'Yes')\r\n  //   - cancelButtonText (defaults: 'No')\r\n  // promise: promise function to run on confirm\r\ngetSwal()                 // Get an instance of `swal`\r\nshowSwal(options)         // Open a swal with the povided options\r\ncloseSwal()               // Close any open swal dialogs\r\ngetMixin(options)         // Create a swal mixin\r\nrunSwalQueue(items)       // Run array of swal options\r\nshowSwalError(message)    // Display validation error on swal\r\ntriggerSwal(options)      // Trigger a swal with defaults\r\nshowWarning(options)      // Display a 'Opps!' warning swal\r\nshowError(options)        // Display a 'Error!' error swal\r\nshowSuccess(options)      // Display a 'Success!' success swal\r\nshowInfo(options)         // Display a `Info` info swal\r\nshowQuestion(options)     // Display a question swal.\r\n```\r\n\r\n### whsLog\r\n\r\n```js\r\nlog(...)          // Prints log message to console\r\ninfo(...)         // Prints info messge to console\r\nwarn(...)         // Prints warn message to console\r\nerror(...)        // Prints error console message\r\ntable(...)        // Displays tabular data as a table\r\ntrace()           // Outputs a stack trace\r\nclear()           // Clear the Console\r\ntimeStart(name)   // Starts a timer with the name specified\r\ntimeStop(name)    // Stops the specified timer and logs the elapsed time\r\n\r\n```\r\n\r\n\r\n### whsUtil\r\n\r\n```js\r\nescapeHTML(string) // Escaped any html found in string\r\ntoBoolean(string)  // returns true/false based on string value\r\ngoBack() // Return to previous state or `app.default`\r\ncalculateWeekNumber(dateString) // Return the week number of date\r\ncalculateMonthNumber(dateString) // Return the month number of date\r\ncalculateYearNumber(dateString) // Return the year number of date\r\ncheckCalender(dateString) // Returns moment date plus the week, month and year numbers of the provided date.\r\n```\r\n\r\n### whsGateway\r\n\r\n```js\r\n// To initialize the gateway you must first create a new instance\r\nconst Gateway = whsGateway.create(clientName, version); // Client name is required, version will default to 'v1'.\r\n\r\n// Using the newly created instance you have access to the following methods.\r\nsetupUrl(url, client, version) // Return gateway url based on the url, client and version.  (client and version will use the instance values if not provided here).\r\n\r\nget(url, config)          // Trigger a HTTP GET Request\r\npost(url, data, config)   // Trigger a HTTP POST Request\r\nput(url, data, config)    // Trigger a HTTP PUT Request\r\ndelete(url, config)       // Trigger a HTTP DELETE Request\r\n```\r\n\r\n### whsAuth\r\n\r\n```js\r\ngetUser()  // Get current user object or null.\r\ngetUserIpAddress() // Return current user IP or ''.\r\ngetPermissions() // Return current application permissions\r\ndirectToLogin(target, targetParams) // Redirect to login page with the set return target & params.\r\ncheckAuth(required) // Check if current user is authenticated and redirect to login if not authenticated.\r\n```\r\n\r\n### busy\r\n\r\n```js\r\n// First initialize a busy instance\r\nconst isLoading = busy.create;\r\n\r\n// With your new instance you can add a promise(s)\r\nisLoading.promise(myPromiseFunction)\r\nisLoading.promises([promiseFunction1, promiseFunction2]);\r\n// When a promise is running the `isLoading.busy` state will update.\r\n\r\n// You can also force a loading error with:\r\nisLoading.showError(message) // Display message as error\r\nisLoading.showErrors(message, errors) // Display error message and errors array\r\nisLoading.clearError() // Will clear any errors\r\n```\r\n\r\n### pollLoop\r\n\r\n```js\r\n// First initialize a pollLoop instance\r\nconst loop = pollLoop.create($scope, () => functionThatReturnsAPromise(), 4000);\r\n//  Instance of angular scope\r\n//  Function that returns a promise to trigger on polling\r\n//  Time between polls (optional defaults to 4000/4secs)\r\n\r\n// PoolLoop Methods\r\nstart() // Start polling after the timer\r\nstartImmediately() // Start now\r\nstop() // Stop current instance\r\n```\r\n\r\n\r\n### CommonService\r\n\r\n```js\r\ngetTitleCaseDictionary() // Return `Common/TitleCaseDictionary` API method\r\nsendEmail(data) // Send email using `Common/SendEmail` API method\r\nsendErrorEmail(body, fromUser) // Sends error email to developers\r\n  // body: HTML Enabled email body\r\n  // fromUser: user object.\r\n```\r\n\r\n## Routing And Permissions\r\n\r\nWhen adding states you are giving a bunch of additional options you can add to extend your route.\r\n\r\n```js\r\n$stateProvider.state('app.sample', {\r\n  url: '/',\r\n  redirectTo: 'app.default', // Redirect to the desired state\r\n  defaultSubRoute: 'app.sample.page', // Redirect to the desired sub route (will retain parameters)\r\n  layout: {\r\n    title: 'Infrastructure', // Browser title to display for page\r\n    header: 'Infrastructure' // Page header to display for page\r\n  },\r\n  allowAnonymous: true // Flags that the page doesn't require authentication\r\n});\r\n```\r\n\r\nIn regards to permission it will closely relate to roles and inheritance:\r\n```json\r\n[\r\n  {\r\n      \"Name\": \"User\",\r\n      \"Description\": \"Groups that have default access to use and view the application\",\r\n      \"Roles\": [\"All\"],\r\n      \"Inherit\": \"Admin\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n      \"Name\": \"Admin\",\r\n      \"Description\": \"\",\r\n      \"Roles\": [\"Administrator\"],\r\n      \"Inherit\": \"\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n    \"Name\": \"StoresAdmin\",\r\n    \"Description\": \"Roles that have access to change there store\",\r\n    \"Roles\": [\"IntranetDeveloper\"],\r\n    \"Inherit\": \"Admin\",\r\n    \"AllowedAccess\": true\r\n  }\r\n]\r\n```\r\nIn the sample above: \r\n - `All` & `Administrator` will get the role `User`\r\n - `Administrator` will get the role `User`\r\n - `IntranetDeveloper` & `Administrator` will get the role `StoresAdmin`\r\n\r\n-----\r\n\r\n## Extensions\r\n\r\nThe Infrastructure plugin has an extendable feature, by default `whs-infrastructure` will use the `core` component this contains all the components we feel are required / commonly used in all our applications.\r\n\r\nYou then have the options of adding extra components to your application.\r\n\r\nBy using this method instead of just adding everything to the core plugin it means we can only include (and build) components that we require which will lower the size of our final bundled application.\r\n\r\nAlso when implementing extensions you can either attach them in your `app.js` so the extension is available app wide, or you can attach them to a specific area so only that area can use a specific extension.\r\n\r\n------------\r\n\r\n## ESLint Config\r\nOur custom ESLint config is simple to implement when using infrastructure and will allow for easily following our coding standards.\r\n\r\n### Installation\r\nOpen your `.eslintrc` file and replace content with the following:\r\n\r\n```json\r\n{\r\n  \"extends\": \"./node_modules/@whsmith/infrastructure/lint.js\"\r\n}\r\n```\r\n\r\nAnd your done.\r\n\r\n-------------\r\n\r\n## WHS Table\r\n\r\n### Usage\r\nImplementing the table extension is very simple by updating your current implementation of your infrastructure to the following:\r\n\r\n```js\r\nimport { infrastructure, whsTable } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsTable]);\r\n```\r\n\r\nOnce loaded you have access to all the directives avaliable for WHS Table.\r\n\r\n#### Basic Usage\r\n\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nrowCollection = [\r\n  {name: 'Test', number: 1},\r\n  {name: 'Test 2', number: 2},\r\n];\r\n```\r\n\r\nIt is recommended however to use `safe-src` to load your data and then loop from a separate generated array:\r\n\r\n```html\r\n<table whs-table=\"gridData\" safe-src=\"rowCollection\">\r\n  <tr ng-repeat=\"row in gridData\"></tr>\r\n</table>\r\n```\r\n\r\n#### Removing Row\r\n```js\r\nfunction removeRow(row) {\r\n  var index = rowCollection.indexOf(row);\r\n  if (index !== -1) {\r\n    rowCollection.splice(index, 1);\r\n  }\r\n}\r\n```\r\n\r\n#### Sorting Data\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nfunction searchNameFunc(value) {\r\n  // Value will equal the row object\r\n  // this will sort by the length of the name string\r\n  return value.name.length;\r\n}\r\n```\r\n\r\n#### Searching / Filtering Data\r\n```html\r\n<table whs-table=\"rowCollection\" ng-enter=\"\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"search for number\" whs-table-search=\"number\" /></th>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"global search\" whs-table-search /></th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\nAs shown above you can add a search for both single column searching and a global table search.\r\n\r\n#### Selecting Rows\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\" whs-table-select-row=\"row\" select-mode=\"single/multiple\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n````\r\nWhen a row is selected the row object with have a `isSelected` property set to true.\r\n\r\n#### Pagination\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n  <tfoot>\r\n    <tr>\r\n      <td colspan=\"2\">\r\n        <div whs-pagination items-per-page=\"10\" displayed-pages=\"5\" items-options=\"[5,10,20]\" current-page=\"1\" page-change=\"changeTriggerFunc()\"></div>\r\n      </td>\r\n    </tr>\r\n  </tfoot>\r\n</table>\r\n```\r\n\r\n#### Sticky Header\r\nYou can now fix your table headers allowing you to scroll large tables while keeping the header at the top of the screen.\r\n\r\n```html\r\n<div class=\"table-box\" whs-table=\"rowCollection\">\r\n  <div class=\"table-responsive\">\r\n    <table sticky-header=\"true/false\">\r\n      <thead>\r\n        <tr>\r\n          <th>Name</th>\r\n          <th>Number</th>\r\n        </tr>\r\n      </thead>\r\n      <tbody>\r\n        <tr ng-repeat=\"row in rowCollection\">\r\n          <td>{{ row.number }}</td>\r\n          <td>{{ row.name }}</td>\r\n        </tr>\r\n      </tbody>\r\n    </table>\r\n  </div>\r\n</div>\r\n```\r\n\r\n**NOTE** There are some factors to take note of when using the fixed header.\r\n\r\n2. If you plan to use a filter on the table you must move the `whs-table` directive above the table element (like in the example above).  If you don't plan to use filtering then it can be added to the table element as before.\r\n\r\n3. Your notice you have pass `true/false` into the directive this is so you can disable and enable the fixed header.  For example when you open search boxes you should disable to fixed header, then you can re-enable when you hide them again.\r\n\r\n-------------\r\n\r\n## WHS Datepicker\r\n\r\n```js\r\nimport { infrastructure, whsDatepicker } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsDatepicker]);\r\n```\r\n\r\n### Using Datepicker\r\n\r\n```html\r\n<div class=\"input-group\">\r\n  <input type=\"text\"\r\n    name=\"dateFieldName\"\r\n    id=\"dateFieldId\"\r\n    class=\"form-control\"\r\n    ng-model=\"modelProperty\"\r\n    whs-datepicker\r\n    format=\"DD/MM/YYYY\"\r\n    min-date=\"00/00/0000\"\r\n    max-date=\"00/00/0000\"\r\n    disabled=\"true/false\"\r\n    icon-btn=\"#dateField_btn\"\r\n    required />\r\n  <div class=\"input-group-btn\">\r\n    <button type=\"button\" class=\"btn btn-secondary\" id=\"dateField_btn\"><i class=\"fa fa-calendar\"></i></button>\r\n  </div>\r\n</div>\r\n```\r\n\r\n### Filters\r\n\r\n#### Moment Parse\r\n```html\r\n{{ date | momentParse : 'DD/MM/YYYY' }}\r\n```\r\n\r\n#### Moment UTC\r\n```html\r\n{{ date | momentUtc }}\r\n```\r\n\r\n#### Date Differance\r\n```html\r\n{{ fromDate | dateDifference : toDate : unit : usePrecision }}\r\n```\r\n\r\n#### Date Format\r\n```html\r\n{{ date | dateFormat : 'DD/MM/YYYY' }}\r\n```\r\n\r\n----\r\n\r\n## WHS Print\r\n\r\n### Installing WHS Print\r\n\r\nTo load whs-print you can extend your current loading instance of infrastructure.\r\n\r\n```js\r\nimport { infrastructure, whsPrint } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsPrint]);\r\n```\r\n\r\n### Using WHS Print\r\n\r\n#### printSection\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children visible for printing\r\n\r\n  ```html\r\n  <div>\r\n      <div print-section>\r\n        I'll print\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>I won't</div>\r\n  </div>\r\n  ```\r\n\r\n#### printOnly\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children only visible for printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-only>\r\n        I'll print, but until then nobody wants me\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>Me, too! Except that people still want to look at me in the meantime...</div>\r\n  </div>\r\n  ```\r\n#### printHide\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing, but it is replaced by blank space\r\n  \r\n  ```html\r\n  <div print-section>\r\n      <div print-hide>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, but those bozos upstairs are still taking up space</div>\r\n  </div>\r\n  ```\r\n\r\n#### printRemove\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-remove>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, and those bozos upstairs will finally stop making such a ruckus</div>\r\n  </div>\r\n  ```\r\n\r\n#### printIf\r\n  - **Directive type:** Attribute\r\n  - **Description**: Toggles print-visibility based on expression\r\n\r\n  ```html\r\n  <!--Pigs do not yet fly, so this div, despite having print-section, will not print-->\r\n  <div print-section print-if=\"pigsFly\"></div>\r\n  <!--ET IS the best, so this div will print, despite not having print-section-->\r\n  <div print-if=\"ETIsTheBest\"></div>\r\n  ```\r\n\r\n#### printBtn\r\n  - **Directive type:** Attribute\r\n  - **Description**: Adds onClick callback to element that will trigger printing\r\n\r\n  ```html\r\n  <button print-btn>Doesn't matter where you put me</button>\r\n  <span print-btn>I will make anything cause a print</span>\r\n  <p print-btn>to happen if you click me</p>\r\n  ```\r\n  \r\n#### printLandscape\r\n  - **Directive type:** Attribute\r\n  - **Description**: Will cause printing to be landscape instead of portrait\r\n\r\n  ```html\r\n  <button print-landscape>Doesn't matter where you put me</button>\r\n  <span print-landscape>I will cause any print</span>\r\n  <p print-landscape>to be landscape</p>\r\n  ```\r\n\r\n#### printAvoidBreak\r\n  - **Directive type:** Attribute\r\n  - **Description**: Prevents page breaks on element\r\n\r\n  ```html\r\n  <button print-avoid-break>This element won't get split by page breaks</button>\r\n  ```\r\n\r\n#### printBreakBefore\r\n  - **Directive type:** Attribute\r\n  - **Description**: Causes a page break before rendering the current element\r\n  \r\n  ```html\r\n  <div print-break-before>Print page will break before this element</div>\r\n  ```\r\n\r\n#### pageBreak\r\n  - **Directive type:** Element\r\n  - **Description**: Element to create a fixed point of a page break, dosn't render anything.\r\n\r\n  ```html\r\n  <div>Page 1</div>\r\n  <page-break></page-break>\r\n  <div>Page 2</div>\r\n  ```\r\n\r\n#### printTable\r\n  - **Directive type:** Attribute\r\n  - **Description**:\r\n    - Optimizes table for printing. This includes keeping 'td' cell content from being cut-off by page breaks.\r\n    - Must be passed an array scope object representing the data presented by the table.\r\n    - Column headers will persist between pages only if the ```<thead>``` and ```<tbody>``` tags are used correctly.\r\n\r\n  This example shows adjustments to an already-visible table in order to tailor it for printing\r\n\r\n  ```html\r\n  <table print-table=\"people\">\r\n    <thead>\r\n      <tr>\r\n        <td print-remove>Unwanted field</td>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td print-remove>{{person.unwantedInfo}}</td>\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n  This example shows a table made to only be visible during printing\r\n\r\n  ```html\r\n  <table print-table=\"people\" print-only>\r\n    <thead>\r\n      <tr>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n### PDFPrinter Service\r\nThe PDFPrinter service is a great tool for building and creating pdfs either to open, print or download.\r\n\r\nYou can either provide the document definitions yourself or load the default and use inbuild functions to build your pdf.\r\n\r\n#### Implementation\r\n\r\n````js\r\nclass SomeClass {\r\n  constructor(PDFPrinter) {\r\n    this.myPDF = PDFPrinter.create(); // Default\r\n\r\n    this.customPDF = PDFPrinter.create(documentDef); // Custom\r\n  }\r\n}\r\n````\r\n`documentDef` is a object using pdfmake, more information about properties you can use are on their website: http://pdfmake.org/playground.html\r\n\r\n\r\n#### Service Methods\r\n\r\nFrom your created PDF instance you will have access to the following methods which will effect the instance you are running from.\r\n\r\n##### print\r\n```js\r\nthis.myPDF.print(); // This will print the current PDF.\r\n```\r\n\r\n##### open\r\n```js\r\nthis.myPDF.open(); // This will open the current PDF.\r\n```\r\n\r\n##### download\r\n```js\r\nthis.myPDF.download(); // This will trigger a download for the current PDF.\r\n```\r\n\r\n##### AddElement\r\n  - **Parameters:** \r\n    - `element` - String or Object to add to the documentDef content.\r\n```js\r\nthis.myPdf.AddElement({\r\n  text: 'sub header text element with page break',\r\n  pageBreak: 'before',\r\n  style: 'subheader'\r\n});\r\n```\r\n\r\n##### AddPageHeader\r\n  - **Parameters:** \r\n    - `text` - Header text\r\n```js\r\nthis.myPdf.AddPageHeader('My PDF');\r\n```\r\n\r\n##### AddFooterPageCount\r\nAdd a page count at the footer of the PDF pages.\r\n```js\r\nthis.myPdf.AddFooterPageCount();\r\n```\r\n\r\n##### AddStyle\r\nAdds a style object to the pdf document.\r\n```js\r\nthis.myPdf.AddStyle('myStyle', {\r\n  bold: true;\r\n  alignment: 'center'\r\n});\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n\r\n```js\r\nthis.myPDF.AddHeader('A Header'); // Add a header to pdf content.\r\n```\r\n\r\n##### AddSubHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a sub header\r\n\r\n```js\r\nthis.myPDF.AddSubHeader('A Sub Header'); // Add a sub header to pdf content.\r\n```\r\n\r\n##### AddText\r\n  - **Parameters:** \r\n    - `text` - Text to display\r\n\r\n```js\r\nthis.myPDF.AddText('I want to write some text on my pdf.'); // Add text to pdf content.\r\n```\r\n\r\n##### AddList\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n    - `bold` - Should the list items be bold\r\n```js\r\nthis.myPDF.AddList(['item 1', 'item 2', 'item 3'], false);\r\n```\r\n\r\n##### AddSimpleTable\r\n   - **Parameters:** \r\n      - `tableBody` - Body array for the table object.\r\n\r\n```js\r\nvar data = [\r\n  ['Header1', 'Header2', 'Header3'], // First colum is the header\r\n  ['row1col1', 'row1col2', 'row2col3']\r\n];\r\nthis.myPDF.AddSimpleTable(data); // Creates a simple table based on the data provided\r\n```\r\n\r\n##### AddRawTable\r\n  - **Parameters:** \r\n    - `tableObj` - Table object.\r\n\r\n```js\r\nvar table = {\r\n  widths: ['*', 200, 50],\r\n  body: [\r\n    [\r\n      { text: 'Auto Width Column', style: 'tableHeader', alignment: 'left' },\r\n      { text: 'Smaller Column', style: 'tableHeader' },\r\n      { text: 'Tiny Column', style: 'tableHeader' }\r\n    ],\r\n    ['row1col1', 'row1col2', 'row2col3']\r\n  ]\r\n};\r\nthis.myPDF.AddRawTable(table); // Creates a table from the raw table object\r\n```\r\n\r\n##### CreateTableObj\r\nBy creating a table object you will gain a bunch of extensions methods to help build your table obj.  All methods can be chained and then ending with `Build()` to return the finished table obj use `getTable()`.\r\n\r\n  - **Parameters:**\r\n    - `tableObj` - Core table object, default will be provided if undefined\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj({\r\n  headerRows: 1,\r\n  style: 'table'\r\n});\r\n\r\n// Add methods here to build your table (see below)\r\ntableBuilder\r\n  .SetWidths([100, 200])\r\n  .AddHeader(['Header 1', 'Header 2'])\r\n  .AddRow(['Row1 Col1', 'Row1 Col2'])\r\n  .Build();\r\n\r\n// Add table to PDF\r\nthis.myPDF.AddRawTable(tableBuilder.getTable());\r\n```\r\n\r\nMethods available within the tableBuilder are as follows:\r\n\r\n##### SetWidths\r\n  - **Parameters:**\r\n    - `widths` - array of widths for each column, pass `'*'` for auto width.\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.SetWidths([100, 200, '*']);\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:**\r\n    - `header` - header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeader({ text: 'Header 1', style: 'tableHeader' });\r\n```\r\n\r\n##### AddHeaders\r\n  - **Parameters:**\r\n    - `headers` - array of header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeaders([\r\n  'Header 1',\r\n  { text: 'Header 2', alignment: 'center' }\r\n]);\r\n```\r\n\r\n##### AddRow\r\n  - **Parameters:**\r\n    - `row` - array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRow([\r\n  { text: 'Col1 Text', style: 'someStyle' },\r\n  { text: 'Col2 Text', bold: true },\r\n]);\r\n```\r\n\r\n##### AddRowSmart\r\nAdding a smart row will allow you to pass the raw object and then define how each column should be handled.\r\n  - **Parameters:**\r\n    - `data` - data objects (model)\r\n    - `columnOptions` - array of column options\r\n      - Object used the core pdfmake properties and also the following extension properties:\r\n        - `property` - property name from data object to use for column\r\n        - `run` - function to run property value through and display function response for column.\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRowSmart(obj, [\r\n  { property: 'id', bold: true }, // Column 1, load obj.id\r\n  { property: 'name' }, // Column 2, load obj.name\r\n  { text: 'NEW', style: 'statusGreen' } // Column 3 forced text value\r\n]);\r\n```\r\n\r\n##### AddRows\r\n  - **Parameters:**\r\n    - `rows` - array of array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRows([\r\n  ['Row1 Col1', 'Row1 Col2'],\r\n  [{ text: 'Row2 Col1', bold: true }, 'Row2 Col2' ]\r\n])\r\n```\r\n\r\n##### AddRowsSmart\r\nLike the `AddRowSmart` however it will allow you to pass an entire array of data with and single set of column options and it will add all the rows.\r\n  - **Parameters:**\r\n    - `data` - Array of objects\r\n    - `columnOptions` - array of column options\r\n\r\n##### AddImage\r\n  - **Parameters:** \r\n    - `imageUrl` - Url / Location of the image you want to display\r\n    - `width` - [*Optional*] Width to scale the image to.\r\n\r\n```js\r\nthis.myPDF.AddImage('myimage.jpg', 200);\r\n```\r\n","readmeFilename":"README.md","_id":"@whsmith/infrastructure@3.0.4","_shasum":"c32e357415b6458a89c541250eb52eee531c801b","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"c32e357415b6458a89c541250eb52eee531c801b","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.4.tgz","fileCount":122,"unpackedSize":578302,"integrity":"sha512-U0qQ86Ln8dqM49QcWSoNJo7zbL9s2BJx1cmpcnYiMLpYwhwolbV8WqwCk81eu6YohAlecGzkxch1ImVZwIWRWQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGbrX5/Zj6uEixCPrYPPJJ5eiWShJFavDrvP3aLhG3HaAiBEKZxkEfYJlJbVZJzNzco7m5Sp1iIsy4Q+IyMGnUBMEQ=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.4_1531752297273_0.14738940595738348"},"_hasShrinkwrap":false},"3.0.5":{"name":"@whsmith/infrastructure","version":"3.0.5","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"release":{"branch":"master","fallbackTags":{"next":"latest"}},"publishConfig":{"tag":"next"},"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"dc239945a406ecf864d9323875841aed4d830c12","readme":"# Infrastructure\r\nInfrastructure is our core library for all SPA applications build within WHSmiths.  It provides a wide range of helper methods and tools as well as styles, directives and filters.\r\n\r\nAlso infrastructure has a range of extensions which you can read more about below.\r\n\r\n## Quick links\r\n- [Installation](#installation)\r\n- [Setup](#setup)\r\n- [Extensions](#extensions)\r\n  - [Custom ESLint Config](#eslint-config)\r\n  - [WHS Table](#whs-table)\r\n  - [WHS Datepicker](#whs-datepicker)\r\n  - [WHS Print](#whs-print)\r\n- [Support](#support)\r\n    - [FAQ](#faq)\r\n    - [Found a bug?](#found-a-bug)\r\n\r\n## Installation\r\n\r\nInstallation is easy as we have followed standards for all our plugin development.\r\n\r\n```sh\r\n$ npm install @whsmith/infrastructure --save\r\n```\r\n\r\nThis will install WHS Infrastructure to your node_modules.\r\n\r\nHowever it's likely infrastructure is already in your `package.json` file:\r\n\r\n```json\r\n\"@whsmith/infrastructure\": \"^3.0.0\",\r\n```\r\n\r\nVersion numbers use a sym versioning system so `major`.`minor`.`build`.\r\n\r\nBreaking changes will cause the `major` version update.\r\n\r\nNew features will cause a `minor` version update (unless the new feature causes a breaking change).\r\n\r\nBug fixes will cause a `build` version update.\r\n\r\nIt's important to take all this into account when selecting or changing your version It's also a good idea to check the `CHANGELOG.md` file for information on what has changed with breaking change information.\r\n\r\n# Setup\r\n\r\nNow you can import into your application (within your `app.js`):\r\n\r\n```js\r\nimport { infrastructure } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure]);\r\n```\r\n\r\nYou will also need to make sure that your `WHS.App.start` has the following line to load the config\r\n```js\r\nWHS.App.constant('loaderConfig', WHS.Loader);\r\n```\r\n\r\n# CORE\r\n\r\n## Directives\r\n### Error Box\r\nUsed to display error messages\r\n\r\n```html\r\n<error-box error=\"{errorObj}\" onDismiss=\"function()\"></error-box>\r\n```\r\n\r\n```js\r\nconst errorObj = {\r\n  ErrorMessage: 'Main message for error',\r\n  Errors: ['Multi pointed error', 'Listed below main message'],\r\n  HelpText: 'Error help text',\r\n  HelpLink: 'http://google.co.uk',\r\n  FullException: 'Full Stack Trace Exception',\r\n};\r\n```\r\n\r\n### ErrorSrc\r\nLoads a src if the original src fails to load.\r\n\r\n```html\r\n<img src=\"\" error-src=\"\" />\r\n```\r\n\r\n### Focus\r\nFocus element on load\r\n\r\n```html\r\n<input focus />\r\n<input focus-on />\r\n<input focus-me />\r\n```\r\n\r\n### Last Modified\r\n```html\r\n<last-modifed model=\"{obj}\"></last-modified>\r\n```\r\n\r\n```js\r\nconst obj = {\r\n  LastModifiedBy: 'User', // Display Name\r\n  LastModifiedOn: moment(), // moment date\r\n};\r\n```\r\n\r\n### Loading Wrapper\r\nUsed to wrap content loaded from a promise\r\n\r\n  - **Attributes**\r\n    - `busy` - Instance of the busy library.\r\n    - `content` - Model referance containing text or html to render while loading.\r\n    - `text` - Raw text to display while loading (`content` will overright this).\r\n    - `once` - If provided loading wrapper will load once and then ignore changes to the busy instance.\r\n\r\n```html\r\n<loading-wrapper busy=\"{busyInstance}\" content=\"htmlContentModel\" text=\"Loading Text\" once>\r\n  <!-- CONTENT -->\r\n</loading-wrapper>\r\n```\r\n\r\n### ngEnter\r\nAttaches an enter keybinding action to element that will trigger the referenced function or will remove enter actions if no function is provided.\r\n\r\n### numberInput\r\nRestrict input key press of anything but numbers.\r\n\r\nAllows: backspace, enter, escape, arrows, Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X.\r\n\r\n### ngPatternRestrict\r\nRestrict `ng-model` value to follow the `pattern` provided\r\n```html\r\n<input type=\"text\" ng-model=\"name\" pattern=\"([A-Z])\\w+\" ng-pattern-restrict>\r\n```\r\n\r\n### Spinner\r\nDisplays a simple spinner based on the expression or referenced value.\r\n```html\r\n<spin active=\"true/false\"></spin>\r\n```\r\n\r\n## Filters\r\n\r\n### checkOngoing\r\nDisplays `Ongoing`, `From {date}`, `Until {date}` or `Expired`.\r\n```html\r\n<span>{{date | checkOngoing:start:end</span>\r\n```\r\n\r\n### clearZero\r\nThis filter will clear any Zero (0) value.\r\n\r\n### company\r\nT = Travel, H = High Street, B = Both\r\n```html\r\n<span>{{data | company}}</span>\r\n```\r\n\r\n### dateUntil\r\nReturns date or `No End Date` if the date if same or after the extreme date `2999-12-31`.\r\n\r\n### groupBy\r\nBest used in the controller will group data by property with `key` and `data` mapping.\r\n\r\n```js\r\nconst grouped = $filter('groupBy')(data, 'myGroupProperty');\r\n// grouped.key = myGroupProperty\r\n// grouped.data = Array of grouped objects.\r\n```\r\n\r\n### htmlEscape\r\nEscape any html in value.\r\n\r\n### moment\r\nParse and return data in set format\r\n```html\r\n{{ '2017-04-01': moment:'DD/MM/YYYY':'YYYY/MM/DD' }}\r\n```\r\n`moment:format:parseFormat`\r\n\r\n### percent\r\nReturn the percent value from a decimal (defaults 1 decimal place & precision 1)\r\n\r\n`percent:decimals:precison`\r\n```html\r\n{{ 0.254242: percent:1 }}\r\n```\r\n\r\n### textReplace\r\nFind and replace text in value\r\n\r\n`textReplace:search:replace`\r\n```html\r\n{{ 'Some Text': textReplace:'Text':'Cake' }}\r\n```\r\n\r\n### titleCase\r\nTitle case input based on abbreviationsList\r\n\r\n`titleCase:abbreviationsList`\r\n\r\n### trim\r\nTrim value removing any whitespace\r\n\r\n### uniqueUrl\r\nAdds a unique queryString to the end of provided url value to prevent caching\r\n\r\n## Services\r\n\r\n### whsHelper\r\nThe helper is a must include for most application controllers as it inherits a lot of core services and useful functions.\r\n\r\n```js\r\n// Properties\r\nscope // Inherits $scope\r\nroot // Inherits $rootScope\r\nbusy // Inherits the busy service\r\nlogger // Inherits the whsLog service\r\nauth // Inherits the whsAuth service\r\ndialog // Inherits the whsDialog service\r\nutil // Inherits the whsUtil service\r\nconfig // Inherits the whsConfig service which inherits loaderConfig\r\nservice // Inherits the CommonService\r\n\r\n// Functions\r\nsendError(message, ...objs); // Will log error objs to console in DEBUG MODE otherwise will send email to developers with objs.\r\ngetSettings() // Returns all the application settings (DNN Settings)\r\ngetSetting(key) // Returns the selected application setting by key\r\ngetUser() // Returns current logged user details or null\r\nshowInfo(message) // Display information sweetalert with message\r\nshowQuestion(title, message) // Display question sweetalert with title and message\r\nshowSuccess(message) // Display success sweetalert with message\r\nshowSuccessToast(message, timer) // Display a successful toast with message and close after timer (ms) (Default: 3000)\r\nshowWarning(message) // Display warning (opps) sweetalert with message\r\nshowError(message, ...objs) // Display error sweetalert with message (first parameter), any additional parameters will be logged to console\r\nshowErrorToast(message, timer, ...objs) // Display a error toast with message and close after timer (ms) (Default: 3000)\r\nshowConfirm(options, promise) // Display a confirmation sweetalert giving the user Yes/No Options and executing a promise on yes.\r\ndebug(...) // Log all params to console\r\naddDebugVariable(name, value) // Add a variable to the debug page '/debug'\r\nlogError(...) // Log all params to console as error\r\n```\r\n\r\n### whsConfig\r\n```js\r\n// Properties\r\n. // Inherits the properties of loaderConfig\r\nappSettings[] // appSettings array of [key] = value (DNN Settings)\r\nDNNAccess // true/false based on if application running in DNN\r\n```\r\n\r\n### whsLog\r\nLog helper handles most console logging\r\n\r\n```js\r\nlog(...); // Prints all args to console as log/debug\r\ninfo(...); // Prints all args to console as info\r\nwarn(...); // Prints all args to console as warning\r\nerror(...); // Prints all args to console as error\r\ntable(...); // Exec console.table(data, [, columns])\r\ntrace(); // Print console trace to this point\r\nclear(); // Clear console\r\ntimeStart(key); // Start timer with key\r\ntimeStop(key); // End timer with key\r\n```\r\n\r\n### pageTitle\r\n\r\n```js\r\npageTitle(title, header, includeAppName) \r\n  // title = page name to display in window/tab title\r\n  // header = page header to display at top\r\n  // includeAppName = true/false - should application name be displayed after title in window/tab title.\r\n```\r\n\r\n### whsDialog\r\n\r\n```js\r\nconfirmModel(options) // Display a confirm model popup\r\n  // OPTIONS\r\n  //   - title: model title\r\n  //   - body: model body\r\n  //   - confirmButtonText: confirm button text, defaults : 'Confirm'\r\n  //   - cancelButtonText: cancel button text, defaults: 'Cancel' \r\n  //   - execute: promise function to run on confirm.\r\nalertMessageBox(options) // Display a alert model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nsuccessMessageBox(options) // Display a success model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nerrorMessageBox(options) // Display a error model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nmessageBox(caption, message, buttons) // Display a model popup message box\r\n  // caption: title of the model\r\n  // message: body of the model\r\n  // buttons: array of buttons\r\n  //   - cssClass: button classes\r\n  //   - result: button return result\r\n  //   - label: button text\r\nshowConfirm(options, promise) // Display a confirming SWAL (Sweetalert)\r\n  // OPTIONS\r\n  //   - title: title of the swal (defaults: 'Are You Sure')\r\n  //   - html: body of the swal (defaults: 'Please confirm you wish to complete this action')\r\n  //   - confirmButtonText (defaults: 'Yes')\r\n  //   - cancelButtonText (defaults: 'No')\r\n  // promise: promise function to run on confirm\r\ngetSwal()                 // Get an instance of `swal`\r\nshowSwal(options)         // Open a swal with the povided options\r\ncloseSwal()               // Close any open swal dialogs\r\ngetMixin(options)         // Create a swal mixin\r\nrunSwalQueue(items)       // Run array of swal options\r\nshowSwalError(message)    // Display validation error on swal\r\ntriggerSwal(options)      // Trigger a swal with defaults\r\nshowWarning(options)      // Display a 'Opps!' warning swal\r\nshowError(options)        // Display a 'Error!' error swal\r\nshowSuccess(options)      // Display a 'Success!' success swal\r\nshowInfo(options)         // Display a `Info` info swal\r\nshowQuestion(options)     // Display a question swal.\r\n```\r\n\r\n### whsLog\r\n\r\n```js\r\nlog(...)          // Prints log message to console\r\ninfo(...)         // Prints info messge to console\r\nwarn(...)         // Prints warn message to console\r\nerror(...)        // Prints error console message\r\ntable(...)        // Displays tabular data as a table\r\ntrace()           // Outputs a stack trace\r\nclear()           // Clear the Console\r\ntimeStart(name)   // Starts a timer with the name specified\r\ntimeStop(name)    // Stops the specified timer and logs the elapsed time\r\n\r\n```\r\n\r\n\r\n### whsUtil\r\n\r\n```js\r\nescapeHTML(string) // Escaped any html found in string\r\ntoBoolean(string)  // returns true/false based on string value\r\ngoBack() // Return to previous state or `app.default`\r\ncalculateWeekNumber(dateString) // Return the week number of date\r\ncalculateMonthNumber(dateString) // Return the month number of date\r\ncalculateYearNumber(dateString) // Return the year number of date\r\ncheckCalender(dateString) // Returns moment date plus the week, month and year numbers of the provided date.\r\n```\r\n\r\n### whsGateway\r\n\r\n```js\r\n// To initialize the gateway you must first create a new instance\r\nconst Gateway = whsGateway.create(clientName, version); // Client name is required, version will default to 'v1'.\r\n\r\n// Using the newly created instance you have access to the following methods.\r\nsetupUrl(url, client, version) // Return gateway url based on the url, client and version.  (client and version will use the instance values if not provided here).\r\n\r\nget(url, config)          // Trigger a HTTP GET Request\r\npost(url, data, config)   // Trigger a HTTP POST Request\r\nput(url, data, config)    // Trigger a HTTP PUT Request\r\ndelete(url, config)       // Trigger a HTTP DELETE Request\r\n```\r\n\r\n### whsAuth\r\n\r\n```js\r\ngetUser()  // Get current user object or null.\r\ngetUserIpAddress() // Return current user IP or ''.\r\ngetPermissions() // Return current application permissions\r\ndirectToLogin(target, targetParams) // Redirect to login page with the set return target & params.\r\ncheckAuth(required) // Check if current user is authenticated and redirect to login if not authenticated.\r\n```\r\n\r\n### busy\r\n\r\n```js\r\n// First initialize a busy instance\r\nconst isLoading = busy.create;\r\n\r\n// With your new instance you can add a promise(s)\r\nisLoading.promise(myPromiseFunction)\r\nisLoading.promises([promiseFunction1, promiseFunction2]);\r\n// When a promise is running the `isLoading.busy` state will update.\r\n\r\n// You can also force a loading error with:\r\nisLoading.showError(message) // Display message as error\r\nisLoading.showErrors(message, errors) // Display error message and errors array\r\nisLoading.clearError() // Will clear any errors\r\n```\r\n\r\n### pollLoop\r\n\r\n```js\r\n// First initialize a pollLoop instance\r\nconst loop = pollLoop.create($scope, () => functionThatReturnsAPromise(), 4000);\r\n//  Instance of angular scope\r\n//  Function that returns a promise to trigger on polling\r\n//  Time between polls (optional defaults to 4000/4secs)\r\n\r\n// PoolLoop Methods\r\nstart() // Start polling after the timer\r\nstartImmediately() // Start now\r\nstop() // Stop current instance\r\n```\r\n\r\n\r\n### CommonService\r\n\r\n```js\r\ngetTitleCaseDictionary() // Return `Common/TitleCaseDictionary` API method\r\nsendEmail(data) // Send email using `Common/SendEmail` API method\r\nsendErrorEmail(body, fromUser) // Sends error email to developers\r\n  // body: HTML Enabled email body\r\n  // fromUser: user object.\r\n```\r\n\r\n## Routing And Permissions\r\n\r\nWhen adding states you are giving a bunch of additional options you can add to extend your route.\r\n\r\n```js\r\n$stateProvider.state('app.sample', {\r\n  url: '/',\r\n  redirectTo: 'app.default', // Redirect to the desired state\r\n  defaultSubRoute: 'app.sample.page', // Redirect to the desired sub route (will retain parameters)\r\n  layout: {\r\n    title: 'Infrastructure', // Browser title to display for page\r\n    header: 'Infrastructure' // Page header to display for page\r\n  },\r\n  allowAnonymous: true // Flags that the page doesn't require authentication\r\n});\r\n```\r\n\r\nIn regards to permission it will closely relate to roles and inheritance:\r\n```json\r\n[\r\n  {\r\n      \"Name\": \"User\",\r\n      \"Description\": \"Groups that have default access to use and view the application\",\r\n      \"Roles\": [\"All\"],\r\n      \"Inherit\": \"Admin\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n      \"Name\": \"Admin\",\r\n      \"Description\": \"\",\r\n      \"Roles\": [\"Administrator\"],\r\n      \"Inherit\": \"\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n    \"Name\": \"StoresAdmin\",\r\n    \"Description\": \"Roles that have access to change there store\",\r\n    \"Roles\": [\"IntranetDeveloper\"],\r\n    \"Inherit\": \"Admin\",\r\n    \"AllowedAccess\": true\r\n  }\r\n]\r\n```\r\nIn the sample above: \r\n - `All` & `Administrator` will get the role `User`\r\n - `Administrator` will get the role `User`\r\n - `IntranetDeveloper` & `Administrator` will get the role `StoresAdmin`\r\n\r\n-----\r\n\r\n## Extensions\r\n\r\nThe Infrastructure plugin has an extendable feature, by default `whs-infrastructure` will use the `core` component this contains all the components we feel are required / commonly used in all our applications.\r\n\r\nYou then have the options of adding extra components to your application.\r\n\r\nBy using this method instead of just adding everything to the core plugin it means we can only include (and build) components that we require which will lower the size of our final bundled application.\r\n\r\nAlso when implementing extensions you can either attach them in your `app.js` so the extension is available app wide, or you can attach them to a specific area so only that area can use a specific extension.\r\n\r\n------------\r\n\r\n## ESLint Config\r\nOur custom ESLint config is simple to implement when using infrastructure and will allow for easily following our coding standards.\r\n\r\n### Installation\r\nOpen your `.eslintrc` file and replace content with the following:\r\n\r\n```json\r\n{\r\n  \"extends\": \"./node_modules/@whsmith/infrastructure/lint.js\"\r\n}\r\n```\r\n\r\nAnd your done.\r\n\r\n-------------\r\n\r\n## WHS Table\r\n\r\n### Usage\r\nImplementing the table extension is very simple by updating your current implementation of your infrastructure to the following:\r\n\r\n```js\r\nimport { infrastructure, whsTable } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsTable]);\r\n```\r\n\r\nOnce loaded you have access to all the directives avaliable for WHS Table.\r\n\r\n#### Basic Usage\r\n\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nrowCollection = [\r\n  {name: 'Test', number: 1},\r\n  {name: 'Test 2', number: 2},\r\n];\r\n```\r\n\r\nIt is recommended however to use `safe-src` to load your data and then loop from a separate generated array:\r\n\r\n```html\r\n<table whs-table=\"gridData\" safe-src=\"rowCollection\">\r\n  <tr ng-repeat=\"row in gridData\"></tr>\r\n</table>\r\n```\r\n\r\n#### Removing Row\r\n```js\r\nfunction removeRow(row) {\r\n  var index = rowCollection.indexOf(row);\r\n  if (index !== -1) {\r\n    rowCollection.splice(index, 1);\r\n  }\r\n}\r\n```\r\n\r\n#### Sorting Data\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nfunction searchNameFunc(value) {\r\n  // Value will equal the row object\r\n  // this will sort by the length of the name string\r\n  return value.name.length;\r\n}\r\n```\r\n\r\n#### Searching / Filtering Data\r\n```html\r\n<table whs-table=\"rowCollection\" ng-enter=\"\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"search for number\" whs-table-search=\"number\" /></th>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"global search\" whs-table-search /></th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\nAs shown above you can add a search for both single column searching and a global table search.\r\n\r\n#### Selecting Rows\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\" whs-table-select-row=\"row\" select-mode=\"single/multiple\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n````\r\nWhen a row is selected the row object with have a `isSelected` property set to true.\r\n\r\n#### Pagination\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n  <tfoot>\r\n    <tr>\r\n      <td colspan=\"2\">\r\n        <div whs-pagination items-per-page=\"10\" displayed-pages=\"5\" items-options=\"[5,10,20]\" current-page=\"1\" page-change=\"changeTriggerFunc()\"></div>\r\n      </td>\r\n    </tr>\r\n  </tfoot>\r\n</table>\r\n```\r\n\r\n#### Sticky Header\r\nYou can now fix your table headers allowing you to scroll large tables while keeping the header at the top of the screen.\r\n\r\n```html\r\n<div class=\"table-box\" whs-table=\"rowCollection\">\r\n  <div class=\"table-responsive\">\r\n    <table sticky-header=\"true/false\">\r\n      <thead>\r\n        <tr>\r\n          <th>Name</th>\r\n          <th>Number</th>\r\n        </tr>\r\n      </thead>\r\n      <tbody>\r\n        <tr ng-repeat=\"row in rowCollection\">\r\n          <td>{{ row.number }}</td>\r\n          <td>{{ row.name }}</td>\r\n        </tr>\r\n      </tbody>\r\n    </table>\r\n  </div>\r\n</div>\r\n```\r\n\r\n**NOTE** There are some factors to take note of when using the fixed header.\r\n\r\n2. If you plan to use a filter on the table you must move the `whs-table` directive above the table element (like in the example above).  If you don't plan to use filtering then it can be added to the table element as before.\r\n\r\n3. Your notice you have pass `true/false` into the directive this is so you can disable and enable the fixed header.  For example when you open search boxes you should disable to fixed header, then you can re-enable when you hide them again.\r\n\r\n-------------\r\n\r\n## WHS Datepicker\r\n\r\n```js\r\nimport { infrastructure, whsDatepicker } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsDatepicker]);\r\n```\r\n\r\n### Using Datepicker\r\n\r\n```html\r\n<div class=\"input-group\">\r\n  <input type=\"text\"\r\n    name=\"dateFieldName\"\r\n    id=\"dateFieldId\"\r\n    class=\"form-control\"\r\n    ng-model=\"modelProperty\"\r\n    whs-datepicker\r\n    format=\"DD/MM/YYYY\"\r\n    min-date=\"00/00/0000\"\r\n    max-date=\"00/00/0000\"\r\n    disabled=\"true/false\"\r\n    icon-btn=\"#dateField_btn\"\r\n    required />\r\n  <div class=\"input-group-btn\">\r\n    <button type=\"button\" class=\"btn btn-secondary\" id=\"dateField_btn\"><i class=\"fa fa-calendar\"></i></button>\r\n  </div>\r\n</div>\r\n```\r\n\r\n### Filters\r\n\r\n#### Moment Parse\r\n```html\r\n{{ date | momentParse : 'DD/MM/YYYY' }}\r\n```\r\n\r\n#### Moment UTC\r\n```html\r\n{{ date | momentUtc }}\r\n```\r\n\r\n#### Date Differance\r\n```html\r\n{{ fromDate | dateDifference : toDate : unit : usePrecision }}\r\n```\r\n\r\n#### Date Format\r\n```html\r\n{{ date | dateFormat : 'DD/MM/YYYY' }}\r\n```\r\n\r\n----\r\n\r\n## WHS Print\r\n\r\n### Installing WHS Print\r\n\r\nTo load whs-print you can extend your current loading instance of infrastructure.\r\n\r\n```js\r\nimport { infrastructure, whsPrint } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsPrint]);\r\n```\r\n\r\n### Using WHS Print\r\n\r\n#### printSection\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children visible for printing\r\n\r\n  ```html\r\n  <div>\r\n      <div print-section>\r\n        I'll print\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>I won't</div>\r\n  </div>\r\n  ```\r\n\r\n#### printOnly\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children only visible for printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-only>\r\n        I'll print, but until then nobody wants me\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>Me, too! Except that people still want to look at me in the meantime...</div>\r\n  </div>\r\n  ```\r\n#### printHide\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing, but it is replaced by blank space\r\n  \r\n  ```html\r\n  <div print-section>\r\n      <div print-hide>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, but those bozos upstairs are still taking up space</div>\r\n  </div>\r\n  ```\r\n\r\n#### printRemove\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-remove>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, and those bozos upstairs will finally stop making such a ruckus</div>\r\n  </div>\r\n  ```\r\n\r\n#### printIf\r\n  - **Directive type:** Attribute\r\n  - **Description**: Toggles print-visibility based on expression\r\n\r\n  ```html\r\n  <!--Pigs do not yet fly, so this div, despite having print-section, will not print-->\r\n  <div print-section print-if=\"pigsFly\"></div>\r\n  <!--ET IS the best, so this div will print, despite not having print-section-->\r\n  <div print-if=\"ETIsTheBest\"></div>\r\n  ```\r\n\r\n#### printBtn\r\n  - **Directive type:** Attribute\r\n  - **Description**: Adds onClick callback to element that will trigger printing\r\n\r\n  ```html\r\n  <button print-btn>Doesn't matter where you put me</button>\r\n  <span print-btn>I will make anything cause a print</span>\r\n  <p print-btn>to happen if you click me</p>\r\n  ```\r\n  \r\n#### printLandscape\r\n  - **Directive type:** Attribute\r\n  - **Description**: Will cause printing to be landscape instead of portrait\r\n\r\n  ```html\r\n  <button print-landscape>Doesn't matter where you put me</button>\r\n  <span print-landscape>I will cause any print</span>\r\n  <p print-landscape>to be landscape</p>\r\n  ```\r\n\r\n#### printAvoidBreak\r\n  - **Directive type:** Attribute\r\n  - **Description**: Prevents page breaks on element\r\n\r\n  ```html\r\n  <button print-avoid-break>This element won't get split by page breaks</button>\r\n  ```\r\n\r\n#### printBreakBefore\r\n  - **Directive type:** Attribute\r\n  - **Description**: Causes a page break before rendering the current element\r\n  \r\n  ```html\r\n  <div print-break-before>Print page will break before this element</div>\r\n  ```\r\n\r\n#### pageBreak\r\n  - **Directive type:** Element\r\n  - **Description**: Element to create a fixed point of a page break, dosn't render anything.\r\n\r\n  ```html\r\n  <div>Page 1</div>\r\n  <page-break></page-break>\r\n  <div>Page 2</div>\r\n  ```\r\n\r\n#### printTable\r\n  - **Directive type:** Attribute\r\n  - **Description**:\r\n    - Optimizes table for printing. This includes keeping 'td' cell content from being cut-off by page breaks.\r\n    - Must be passed an array scope object representing the data presented by the table.\r\n    - Column headers will persist between pages only if the ```<thead>``` and ```<tbody>``` tags are used correctly.\r\n\r\n  This example shows adjustments to an already-visible table in order to tailor it for printing\r\n\r\n  ```html\r\n  <table print-table=\"people\">\r\n    <thead>\r\n      <tr>\r\n        <td print-remove>Unwanted field</td>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td print-remove>{{person.unwantedInfo}}</td>\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n  This example shows a table made to only be visible during printing\r\n\r\n  ```html\r\n  <table print-table=\"people\" print-only>\r\n    <thead>\r\n      <tr>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n### PDFPrinter Service\r\nThe PDFPrinter service is a great tool for building and creating pdfs either to open, print or download.\r\n\r\nYou can either provide the document definitions yourself or load the default and use inbuild functions to build your pdf.\r\n\r\n#### Implementation\r\n\r\n````js\r\nclass SomeClass {\r\n  constructor(PDFPrinter) {\r\n    this.myPDF = PDFPrinter.create(); // Default\r\n\r\n    this.customPDF = PDFPrinter.create(documentDef); // Custom\r\n  }\r\n}\r\n````\r\n`documentDef` is a object using pdfmake, more information about properties you can use are on their website: http://pdfmake.org/playground.html\r\n\r\n\r\n#### Service Methods\r\n\r\nFrom your created PDF instance you will have access to the following methods which will effect the instance you are running from.\r\n\r\n##### print\r\n```js\r\nthis.myPDF.print(); // This will print the current PDF.\r\n```\r\n\r\n##### open\r\n```js\r\nthis.myPDF.open(); // This will open the current PDF.\r\n```\r\n\r\n##### download\r\n```js\r\nthis.myPDF.download(); // This will trigger a download for the current PDF.\r\n```\r\n\r\n##### AddElement\r\n  - **Parameters:** \r\n    - `element` - String or Object to add to the documentDef content.\r\n```js\r\nthis.myPdf.AddElement({\r\n  text: 'sub header text element with page break',\r\n  pageBreak: 'before',\r\n  style: 'subheader'\r\n});\r\n```\r\n\r\n##### AddPageHeader\r\n  - **Parameters:** \r\n    - `text` - Header text\r\n```js\r\nthis.myPdf.AddPageHeader('My PDF');\r\n```\r\n\r\n##### AddFooterPageCount\r\nAdd a page count at the footer of the PDF pages.\r\n```js\r\nthis.myPdf.AddFooterPageCount();\r\n```\r\n\r\n##### AddStyle\r\nAdds a style object to the pdf document.\r\n```js\r\nthis.myPdf.AddStyle('myStyle', {\r\n  bold: true;\r\n  alignment: 'center'\r\n});\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n\r\n```js\r\nthis.myPDF.AddHeader('A Header'); // Add a header to pdf content.\r\n```\r\n\r\n##### AddSubHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a sub header\r\n\r\n```js\r\nthis.myPDF.AddSubHeader('A Sub Header'); // Add a sub header to pdf content.\r\n```\r\n\r\n##### AddText\r\n  - **Parameters:** \r\n    - `text` - Text to display\r\n\r\n```js\r\nthis.myPDF.AddText('I want to write some text on my pdf.'); // Add text to pdf content.\r\n```\r\n\r\n##### AddList\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n    - `bold` - Should the list items be bold\r\n```js\r\nthis.myPDF.AddList(['item 1', 'item 2', 'item 3'], false);\r\n```\r\n\r\n##### AddSimpleTable\r\n   - **Parameters:** \r\n      - `tableBody` - Body array for the table object.\r\n\r\n```js\r\nvar data = [\r\n  ['Header1', 'Header2', 'Header3'], // First colum is the header\r\n  ['row1col1', 'row1col2', 'row2col3']\r\n];\r\nthis.myPDF.AddSimpleTable(data); // Creates a simple table based on the data provided\r\n```\r\n\r\n##### AddRawTable\r\n  - **Parameters:** \r\n    - `tableObj` - Table object.\r\n\r\n```js\r\nvar table = {\r\n  widths: ['*', 200, 50],\r\n  body: [\r\n    [\r\n      { text: 'Auto Width Column', style: 'tableHeader', alignment: 'left' },\r\n      { text: 'Smaller Column', style: 'tableHeader' },\r\n      { text: 'Tiny Column', style: 'tableHeader' }\r\n    ],\r\n    ['row1col1', 'row1col2', 'row2col3']\r\n  ]\r\n};\r\nthis.myPDF.AddRawTable(table); // Creates a table from the raw table object\r\n```\r\n\r\n##### CreateTableObj\r\nBy creating a table object you will gain a bunch of extensions methods to help build your table obj.  All methods can be chained and then ending with `Build()` to return the finished table obj use `getTable()`.\r\n\r\n  - **Parameters:**\r\n    - `tableObj` - Core table object, default will be provided if undefined\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj({\r\n  headerRows: 1,\r\n  style: 'table'\r\n});\r\n\r\n// Add methods here to build your table (see below)\r\ntableBuilder\r\n  .SetWidths([100, 200])\r\n  .AddHeader(['Header 1', 'Header 2'])\r\n  .AddRow(['Row1 Col1', 'Row1 Col2'])\r\n  .Build();\r\n\r\n// Add table to PDF\r\nthis.myPDF.AddRawTable(tableBuilder.getTable());\r\n```\r\n\r\nMethods available within the tableBuilder are as follows:\r\n\r\n##### SetWidths\r\n  - **Parameters:**\r\n    - `widths` - array of widths for each column, pass `'*'` for auto width.\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.SetWidths([100, 200, '*']);\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:**\r\n    - `header` - header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeader({ text: 'Header 1', style: 'tableHeader' });\r\n```\r\n\r\n##### AddHeaders\r\n  - **Parameters:**\r\n    - `headers` - array of header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeaders([\r\n  'Header 1',\r\n  { text: 'Header 2', alignment: 'center' }\r\n]);\r\n```\r\n\r\n##### AddRow\r\n  - **Parameters:**\r\n    - `row` - array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRow([\r\n  { text: 'Col1 Text', style: 'someStyle' },\r\n  { text: 'Col2 Text', bold: true },\r\n]);\r\n```\r\n\r\n##### AddRowSmart\r\nAdding a smart row will allow you to pass the raw object and then define how each column should be handled.\r\n  - **Parameters:**\r\n    - `data` - data objects (model)\r\n    - `columnOptions` - array of column options\r\n      - Object used the core pdfmake properties and also the following extension properties:\r\n        - `property` - property name from data object to use for column\r\n        - `run` - function to run property value through and display function response for column.\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRowSmart(obj, [\r\n  { property: 'id', bold: true }, // Column 1, load obj.id\r\n  { property: 'name' }, // Column 2, load obj.name\r\n  { text: 'NEW', style: 'statusGreen' } // Column 3 forced text value\r\n]);\r\n```\r\n\r\n##### AddRows\r\n  - **Parameters:**\r\n    - `rows` - array of array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRows([\r\n  ['Row1 Col1', 'Row1 Col2'],\r\n  [{ text: 'Row2 Col1', bold: true }, 'Row2 Col2' ]\r\n])\r\n```\r\n\r\n##### AddRowsSmart\r\nLike the `AddRowSmart` however it will allow you to pass an entire array of data with and single set of column options and it will add all the rows.\r\n  - **Parameters:**\r\n    - `data` - Array of objects\r\n    - `columnOptions` - array of column options\r\n\r\n##### AddImage\r\n  - **Parameters:** \r\n    - `imageUrl` - Url / Location of the image you want to display\r\n    - `width` - [*Optional*] Width to scale the image to.\r\n\r\n```js\r\nthis.myPDF.AddImage('myimage.jpg', 200);\r\n```\r\n","readmeFilename":"README.md","_id":"@whsmith/infrastructure@3.0.5","_shasum":"e6a0a87f441d64dab474f92a3f240827eab91641","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"e6a0a87f441d64dab474f92a3f240827eab91641","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.5.tgz","fileCount":122,"unpackedSize":578302,"integrity":"sha512-FKh+k3aGq55jhbXSqEwNe/cDCdrMh51dBnX7zhlZKW7vFLWK4YoRKoPIqldu47xzmhaS3mI5iKsOrXG4gQ/dzA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCC6S/4IRPlAp06ibQR+jKOY0up3neq1EiytTNVn+eSagIhANs8rzSpVDKN5OKH5gbm7l5n7YrhGFTEXbh5cYBeMJnW"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.5_1531752502291_0.3446345847713641"},"_hasShrinkwrap":false},"3.0.6":{"name":"@whsmith/infrastructure","version":"3.0.6","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"bdfd9df3aa0d13d1aeea2e1c20221e794c7cdfe9","_id":"@whsmith/infrastructure@3.0.6","_shasum":"199407bcf5e3818e32cb5f24705d72d60ac90ac4","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"199407bcf5e3818e32cb5f24705d72d60ac90ac4","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.6.tgz","fileCount":122,"unpackedSize":578154,"integrity":"sha512-5RwXWPbsr73F0J8cMPOs/upaO9EkAUENSFIN/NH8J7dyUNGBDz+qDlrc/EeSKsZe6gEFoYrsNikBGI35jya1RQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH9AgtdNzvnluEVFI6yCYFB060n7795CkrhqUthSn/uTAiEAltEoCbKfZxDNWg4qSTqhCheKueV7ioZO3hpFIGPaNG8="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.6_1531753167909_0.033467804909740995"},"_hasShrinkwrap":false},"3.0.7":{"name":"@whsmith/infrastructure","version":"3.0.7","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"d404196eafc8e0a0f476e981f4cfc1473cf6f960","_id":"@whsmith/infrastructure@3.0.7","_shasum":"a7e382a8a572a9f773a6fa65344ae462118c805c","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"a7e382a8a572a9f773a6fa65344ae462118c805c","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.7.tgz","fileCount":122,"unpackedSize":578204,"integrity":"sha512-MQQ983HNVjWXN3Q6Dq8pTIKUmGtvz0wfE2ugnrrUedMN5v5KcmyNeOp9qARn02QYLGvfJopg9LVkR+netzKxcw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDL9n95fH96cKZEyPj4ffhQJLKXsO/8Y1qfJmChwV4u9QIhAOggdBXAPOw4oa/65QsRloq3cuNt+BNuzYpeag71gTgo"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.7_1531824670195_0.08006200700066457"},"_hasShrinkwrap":false},"3.0.8":{"name":"@whsmith/infrastructure","version":"3.0.8","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"4f9d7a899ee9eb592c8f80ee2627295cfdca7d85","_id":"@whsmith/infrastructure@3.0.8","_shasum":"46025bf68fcc9a3fb8293c6ebb27f7e785a0f3a1","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"46025bf68fcc9a3fb8293c6ebb27f7e785a0f3a1","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.8.tgz","fileCount":122,"unpackedSize":578140,"integrity":"sha512-PxeIT7Gr0cynBcPBT4IuhsyFpxZ6pa4oi9zxT4dznmkcO4nnze4TO8c2/qRqDFF2qmgmX/MUUH+frgDlcyes4Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHiRPNpgSQQrwoJZ9qMP26Hb8ZF9x1VNgMuV0mqIdN+1AiBeNcMVA42TswHXc5aV3qfPQfe+mczSDOcgmFO36gVT9w=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.8_1531824977959_0.7490365759495767"},"_hasShrinkwrap":false},"3.0.9":{"name":"@whsmith/infrastructure","version":"3.0.9","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"a2132d55414f09fb4b70df74ad877db06c1605b8","_id":"@whsmith/infrastructure@3.0.9","_shasum":"ab297cd041f0e69495cd09f7b1ea0a71bfb43976","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"ab297cd041f0e69495cd09f7b1ea0a71bfb43976","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.9.tgz","fileCount":122,"unpackedSize":578224,"integrity":"sha512-mOR5VHYKbNo2h60wngNluJZgDB1MDzkWc/yspkhK4AVbwYxpiTnflx1qnvjvMUPDxX9XxBMreHEcOZRwOCC4Lw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID/KIm4O/+tFuywSmFl2IZ2CoOMyL+V91+agFM8KCKmhAiEAmGFdlDfzxDGTYrzvzHwjaQtf5aHmF/1rlSYHOGJVFQc="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.9_1531825378076_0.012341090701337043"},"_hasShrinkwrap":false},"3.0.10":{"name":"@whsmith/infrastructure","version":"3.0.10","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"655df9800ba71ccc3d5446b01827907215339063","_id":"@whsmith/infrastructure@3.0.10","_shasum":"83bf3efb552e77b939effb707e0fcd64877c6f04","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"83bf3efb552e77b939effb707e0fcd64877c6f04","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.10.tgz","fileCount":122,"unpackedSize":578257,"integrity":"sha512-kWXE2QUHMC4ct2t46vCLKa4imwbqjHC14l2V8MOi5PQu6uzUuD95mrURwYUxPerbmcLcBNfrQBc8dkj+YBvg6A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFC03rOt4H0epmaTcGlulXDxvSuPzLBsAwCBMZK1inwlAiB/Y8JBjvIS9ILH66CbceySjxLwdcowqsXLifibL7ZcGg=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.10_1531826388106_0.48122854605098286"},"_hasShrinkwrap":false},"3.0.11":{"name":"@whsmith/infrastructure","version":"3.0.11","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"871c34bb8144a1850a71a3f9adabed5efd370075","_id":"@whsmith/infrastructure@3.0.11","_shasum":"007ac787c5cd2e745197f05effeeff8f2def5af1","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"007ac787c5cd2e745197f05effeeff8f2def5af1","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.11.tgz","fileCount":122,"unpackedSize":578472,"integrity":"sha512-o6/8IicLo4xyohHAGFUutdb2n0h6tldNRsdsp04qhhKhjXr5heC2QSZh7r9YDbIQQ1o40tIyaxKWAQvEpwLpvg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHdph+21mhpoAUWbuXFgpRqy4r8ASvl/qOY+ja4F4UkMAiAPkXZFx4NR3gOsW7XI1WuTgBeh4q7pMP290pVOoy43cQ=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.11_1531833939439_0.6025194572821526"},"_hasShrinkwrap":false},"3.0.12":{"name":"@whsmith/infrastructure","version":"3.0.12","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"8483b83ebb23d0b5a620783e9cdc2dc784906f7d","_id":"@whsmith/infrastructure@3.0.12","_shasum":"567587b2063b015b3a60b1e3a866247e55f6c040","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"567587b2063b015b3a60b1e3a866247e55f6c040","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.12.tgz","fileCount":122,"unpackedSize":578092,"integrity":"sha512-hCcyqAIqjZysy1kzghMkpV+IVTVlHVOLR7tb47ZvOqJ0J+Uv99uoT/DRNjoCXQn5+WBKT4LbeL8smIMDULyZeA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChq7TRCGOjgCEyxUB41shfdX/JfxHxBNdQ1XyRKt8PWAIgFjNqUY14kyKrSDB5HbwOMeGKijuD9SKpEKKnP/zDsuE="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.12_1531839695347_0.5388110021605936"},"_hasShrinkwrap":false},"3.0.13":{"name":"@whsmith/infrastructure","version":"3.0.13","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"e38c394277eb18f743b8d47e43cd4bd0be4df0fd","_id":"@whsmith/infrastructure@3.0.13","_shasum":"67dee0e7ca59590c4ec2f2f253f8e99a77d3f87f","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"67dee0e7ca59590c4ec2f2f253f8e99a77d3f87f","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.13.tgz","fileCount":122,"unpackedSize":578233,"integrity":"sha512-jT1/CF4obNJGtX6dVZU3ydWfoEFG5WSTZlGWBmdLj7K1NiXQq+OsK2WsdgqUmid2hh2WaztgI8yOKQGJqVMufQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7oHlokGZU+2pXxZdwX2YTWoLIz9BfouFtl2LbN6tpDwIhAOZDEsjddHd+YA0AWRCECw7J24AI8siezRiG4QsW4OtK"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.13_1531905290331_0.5608989978565297"},"_hasShrinkwrap":false},"3.0.14":{"name":"@whsmith/infrastructure","version":"3.0.14","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"59f37dedb6eeb57183a9f5c811bd8bd47144a3d5","_id":"@whsmith/infrastructure@3.0.14","_shasum":"e772aac91560fd130309dd0ee2a89344da6a2cd9","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"e772aac91560fd130309dd0ee2a89344da6a2cd9","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.14.tgz","fileCount":122,"unpackedSize":577958,"integrity":"sha512-2WfkFpbp4gYlsr9w30XbfpHVsP2oN+8saF+i7kFafW49RWV7M5XZLnjMTJaZVv1D5f+dN66w2jomvvPHyJIC7g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH9+/kIbk3JSrMptWUe4fufVZHxlfgDf/OFR7qKoLOTwAiEA8s3mDA4Fysn4o5Gpwiqr3u2Xod7aQewPERcKsudj+Tw="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.14_1531988271771_0.06492648125843448"},"_hasShrinkwrap":false},"3.0.15":{"name":"@whsmith/infrastructure","version":"3.0.15","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"7653ecbc33c9fe0844bb677232f2bf0889f76079","_id":"@whsmith/infrastructure@3.0.15","_shasum":"3a0fdf9fecc7102e41c43d0bfe1d4d54f2ab7361","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"3a0fdf9fecc7102e41c43d0bfe1d4d54f2ab7361","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.15.tgz","fileCount":122,"unpackedSize":578119,"integrity":"sha512-xth7kOJjxnPZsf7rOJyDfFhlqGsIlL3b85LWNvI4tNExQsXjWRGBSJr6IQSlA82mK+AOweepnkFNgTCYxzhT2Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICqLc6svGRo+CEXTBfucHq/vqCQxwHqds2mzsETrr8j+AiBIkfZud2uR1BtI+osgzvK97RMA48Yagk7lLLXLr9TDjQ=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.15_1531995397158_0.9127665084806387"},"_hasShrinkwrap":false},"3.0.16":{"name":"@whsmith/infrastructure","version":"3.0.16","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"9c355b98217412a9276697c191428cbcbfda6a3d","_id":"@whsmith/infrastructure@3.0.16","_shasum":"fdf71014ab7fcbca21a30e51e00130c3f53faa9d","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"fdf71014ab7fcbca21a30e51e00130c3f53faa9d","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.16.tgz","fileCount":122,"unpackedSize":578252,"integrity":"sha512-shDLhwViib0oi4dYJfhQS0B+9Gd0YoT7Rjx2uJq0CifcXSaxWDhKUxLuzhxsSwsLN3NTWacBTsePFvzeRiJSXQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCUr3nMRocFB6L2WGtUXS+44OowQnyI3t1koQghE7kiAgIhAIEbU9cyioRNJWGABYhHBNRngXdqgxYguvySwVXXwExI"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.16_1531996347044_0.5956191283262293"},"_hasShrinkwrap":false},"3.0.17":{"name":"@whsmith/infrastructure","version":"3.0.17","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"f289faace2f34e0a5147929fc11a5f9313f1d0ae","_id":"@whsmith/infrastructure@3.0.17","_shasum":"43ac2fc7fea2e41390788c2d1c7b874a514b237d","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"43ac2fc7fea2e41390788c2d1c7b874a514b237d","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.17.tgz","fileCount":122,"unpackedSize":579214,"integrity":"sha512-Ixn4uEWwHf16XTVfW1eEpjvAGCz6ccKbCVbGlkTk/dJGf/RBIgtMaBF4kOHr/2ZmxiPyFb++2obbCfFusMu4Lg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICUZkXZkiUmNDYM+Xe6JiWrCDM/y2eHZJwJXIri8Cq+6AiAV1rynW1KyO4SDOJbCgeM6nibpnDQh9Ou9bPDTwIibUA=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.17_1531998812662_0.03262654789785646"},"_hasShrinkwrap":false},"3.0.18":{"name":"@whsmith/infrastructure","version":"3.0.18","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.0.0"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"25f594dadbf5ec94f6f764f8825092680f908473","_id":"@whsmith/infrastructure@3.0.18","_shasum":"0eaa67e4a7a42043f8d39800ce4c19316f491f47","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"0eaa67e4a7a42043f8d39800ce4c19316f491f47","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.0.18.tgz","fileCount":122,"unpackedSize":579529,"integrity":"sha512-+7BO6FWa+o36dMmxAZ0Jsq+Gz1n9mmb7vb4tYFX+8olUGTM4VaMEiuQOxY4tmFAH1DHwybf/UCmCm298SePUrA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOqLjSi57qV2HFWlT/3EU3QO2JV8WLBm8iHsLFyI3SaQIhAKtfzpwr2dhmrbQx4aoEWQBlnYwg9oOVOcj3jcQd/rJE"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.0.18_1531999932943_0.2744568128474538"},"_hasShrinkwrap":false},"3.1.0":{"name":"@whsmith/infrastructure","version":"3.1.0","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"8b409436ee42b4ceb87fb26b1cd36aa352b5e304","_id":"@whsmith/infrastructure@3.1.0","_shasum":"4b3dcc68b09a85bf0b4f50d25312c1d263cc56c5","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"4b3dcc68b09a85bf0b4f50d25312c1d263cc56c5","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.1.0.tgz","fileCount":126,"unpackedSize":584307,"integrity":"sha512-2jISHgqPDCP4aDwZ3v6QY4+xqbWUtztPvj0TkxuvOANIPPQtOLdNgXbt3EQqXRdkUWmA+V9GGoEko/TzGJAUMw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOQ7JWrtOivoMolsHq1MIw0zKFAgXN8rEnUBYj5bYGuwIgehI+nW12YL6xbhp4LmzSkBHAaVxr3ca4B/gjbECaAmc="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.1.0_1532087908466_0.10066299404023393"},"_hasShrinkwrap":false},"3.1.1":{"name":"@whsmith/infrastructure","version":"3.1.1","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"clean":"shx rm -rf bundles","prebuild":"npm run clean","build":"webpack --colors --env.debug","prebuild:prod":"npm run clean","build:prod":"webpack --colors --env.prod","prebuild:full":"npm run clean","build:full":"webpack --colors --env.prod --env.debug","release-build":"run-s lint build:full","lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"autoprefixer":"^7.1.2","babel-core":"^6.25.0","babel-loader":"^7.1.0","babel-plugin-angularjs-annotate":"^0.8.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.5.2","babel-preset-es2015":"^6.24.1","babel-preset-react":"^6.24.1","babel-runtime":"^6.23.0","css-loader":"^0.28.4","eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0","extract-text-webpack-plugin":"^2.1.2","file-loader":"^0.11.2","html-loader":"^0.4.5","less":"^2.7.2","less-loader":"^4.0.5","loader-utils":"^1.1.0","node-sass":"^4.5.3","npm-run-all":"^4.0.2","postcss-loader":"^2.0.6","raw-loader":"^0.5.1","resolve-url-loader":"^2.1.0","rimraf":"^2.6.1","sass-loader":"^6.0.6","shx":"^0.2.2","style-loader":"^0.18.2","url-loader":"^0.5.9","webpack":"^3.0.0","webpack-dev-server":"^2.5.1","webpack-merge":"^4.1.0"},"optionalDependencies":{},"gitHead":"2a034740ad9963bf7125e9c47ab3afc8df6f3cc7","_id":"@whsmith/infrastructure@3.1.1","_shasum":"16101ea67bfaf23cbadc94a7dde401f7b1b91a25","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"16101ea67bfaf23cbadc94a7dde401f7b1b91a25","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.1.1.tgz","fileCount":126,"unpackedSize":584223,"integrity":"sha512-MquofpsTW38gXPJwWDID2nSMsAPZkPZAInswS7fPmwE63uYojZbauBaIK4+JHmdWbeurxSHc7/XBpbRXOjeP8A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCs4H125LP/ir60ajLlI8YEM9klwAsVxtGrN3w9DUOB9QIhALbmCIVO/N44FIEP7Go6HMo/qmL6Lll23u+nRIpfysvG"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.1.1_1532090122915_0.5274937355182754"},"_hasShrinkwrap":false},"3.1.2":{"name":"@whsmith/infrastructure","version":"3.1.2","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"46fb0fec6df5fa29381430c702b9074e9d806198","_id":"@whsmith/infrastructure@3.1.2","_shasum":"64de9accc834f0bd467251a50a0a187a5afc9b7a","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"64de9accc834f0bd467251a50a0a187a5afc9b7a","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.1.2.tgz","fileCount":126,"unpackedSize":583040,"integrity":"sha512-HrajH/560uQCEdewwp3mdO3s5ZtkLb9D43JH9AxZ6/JkJ/MMg+/0R/Had7WnyWZgdEEGAUiHLB5k6RJmDWyoDA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBTZ/kGR9UyuoBjHG8At2CXoonFdXYWuWmdvBOOrIN6BAiEA76ehXsSINXDMmg35N5Fy2BWwIFan2kG/QZP/ut+7ndo="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.1.2_1532333954689_0.042992474540408665"},"_hasShrinkwrap":false},"3.1.3":{"name":"@whsmith/infrastructure","version":"3.1.3","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"45196b5bc92d46d517a82aef5beff250a9974df2","_id":"@whsmith/infrastructure@3.1.3","_shasum":"931c7da1cf9a63bea7be53388daa102d6e990756","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"931c7da1cf9a63bea7be53388daa102d6e990756","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.1.3.tgz","fileCount":126,"unpackedSize":583197,"integrity":"sha512-C+U+bEHco2eM/+MWftEG0gUrHL2tO9jRlcT26DbUDG3fq/ilkQkpW2ipUEBuftr8nzDh5VIRxvMZDBkS8d074g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwa9TM/uc8wb9JXKVPPh7zjeneRderYuvh1H+EYWS5zwIhAPDdUkee6z/w32NI6kuRThx4uVi45qDTICTpoi49MFSU"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.1.3_1532520890386_0.9368639364853586"},"_hasShrinkwrap":false},"3.1.4":{"name":"@whsmith/infrastructure","version":"3.1.4","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"1f9b8adc3000d13d7eddaae2575d6034ee4d347c","_id":"@whsmith/infrastructure@3.1.4","_shasum":"795f17859dfe5309425efcd0ab4b3034959a9f74","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"795f17859dfe5309425efcd0ab4b3034959a9f74","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.1.4.tgz","fileCount":126,"unpackedSize":583199,"integrity":"sha512-G3r5aJBqJeRXwdcpy/rCscvZd1YYZOcHp3dAFOojNzXpAJoK4EcZrIKPtNRs4uryAyrNUbeJe3uHFetxl+wlvA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBEbkGaN0+vjsA0dgQHfFpw/EMxVbiuO7py+xFOhZjkwAiAonMYdoPwN69gYSsyMTHh17QiH4p8PLG63Y5bmqnIMog=="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.1.4_1532523534281_0.8148154978522051"},"_hasShrinkwrap":false},"3.2.0":{"name":"@whsmith/infrastructure","version":"3.2.0","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"af8d5946571da153e49c7c32aad5a42e23cbaedc","_id":"@whsmith/infrastructure@3.2.0","_shasum":"7eba8d47db387122a69021e2bbe189bdcad5ad7d","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"7eba8d47db387122a69021e2bbe189bdcad5ad7d","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.2.0.tgz","fileCount":127,"unpackedSize":583935,"integrity":"sha512-1VSVh6RYTl6pyy9UMup21x/mHpska+8K3l6RA/2I01+ZzLz71bwvA1uqjAYuM+3QdJqCKdAxhpQlhG2at69k/g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJKispplaNTxQdirhnuFuEmEb/UKctOGvi7pbeow7Y1gIgcVgumFekRB1JQB9PVbA1oOpcIcPFiAQvxGsUFQxi0zc="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.2.0_1532688744426_0.6241447140082432"},"_hasShrinkwrap":false},"3.4.0":{"name":"@whsmith/infrastructure","version":"3.4.0","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"a9a33a8a20d684dc555cc9906282806eb0fec3b8","_id":"@whsmith/infrastructure@3.4.0","_shasum":"b314acbb1dc3b0158fdf470ebb98ea4a44d7bd4f","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"b314acbb1dc3b0158fdf470ebb98ea4a44d7bd4f","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.4.0.tgz","fileCount":128,"unpackedSize":601558,"integrity":"sha512-LpBtJjIihp8wxiuJTUqIy178VOpgkTmS6M68iBkjLhh/yW+/4ccKH3tSZYGEIMCWVy16kx6/3g4oa2euFcXGSw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcAxLs9Z5A2bkW2LuQn5npsB/xHuTXmTSS4F9Js5C72wIhAKrXnuKiQ3zfxnfK9D0mBqltsyW7LCqJb1rGJ2KbKDVE"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.4.0_1533135220935_0.7566610738682749"},"_hasShrinkwrap":false},"3.4.1":{"name":"@whsmith/infrastructure","version":"3.4.1","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"f40276bc8689867824430186cfa7e73fa40a2161","_id":"@whsmith/infrastructure@3.4.1","_shasum":"98041d6065c8cf95d12491576703efd501bd07dc","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"98041d6065c8cf95d12491576703efd501bd07dc","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.4.1.tgz","fileCount":128,"unpackedSize":601728,"integrity":"sha512-W5Ey5+WJ75MvZ4ZLCI+cdXyfGlrZt1rVTSEzmgRHsTgG/zdLh48ZOm4ZghMquhD/w+oX/R/80Ffaz4AjjkqmbQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFj44NYwE0gRbI93NhfB5gnpHMceBbrGV+b1j+8GxTFKAiEAu+CAExDvGh5JKHku/y4EXw97RaCbMj9rMEQLh5ZpZhE="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.4.1_1533201695653_0.6463723562843211"},"_hasShrinkwrap":false},"3.4.2":{"name":"@whsmith/infrastructure","version":"3.4.2","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"^1.6.4","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"f52265bb589d819be24627e2c5c4946a8c1caaeb","_id":"@whsmith/infrastructure@3.4.2","_shasum":"2710570c61badd0776543486a5fe4812f1effc0f","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"2710570c61badd0776543486a5fe4812f1effc0f","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.4.2.tgz","fileCount":128,"unpackedSize":601965,"integrity":"sha512-IizDNjbdB7GA9mkFw2zM3sa49vfD/j3hboWH63bNwvGJ3sXn9W+AAwlGnCYMH3V+qCaSwuMdkaHOSun8l+kL4g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICXZqVYScLkTwD2ixIOlQOtuNI9y0us2FQyf5eT75lEGAiEA7r5ipXk2PmdhIVuZQ/cN1rqEwHecrL8FuMaxY/wNnN4="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.4.2_1533220877098_0.2171595325655753"},"_hasShrinkwrap":false},"3.5.0":{"name":"@whsmith/infrastructure","version":"3.5.0","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","files":["index.js","lint.js","src/","*.md"],"scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"07f10b58d5189a755c85b4b5ba2742faf65b0e9b","_id":"@whsmith/infrastructure@3.5.0","_shasum":"ff971a1506d84b882ac9a844d1dcd45e11621af8","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"ff971a1506d84b882ac9a844d1dcd45e11621af8","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.0.tgz","integrity":"sha512-4F74EEcC9P37giyiz2UYvOjgEpgxiAZwGfp4qktq5wwtn4wW+G3M8OY7AOXiKCMz3eKQBNg7QikWrVL/O9mghQ==","fileCount":132,"unpackedSize":686153,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbcr4iCRA9TVsSAnZWagAAwRcP/2JwbEATHN+Q848O5A99\nIPh+1IdE53rD+9vomtQjCTj1kBfn+uDYUT22E1YuNZkMY2+lqpseylTyiSME\nBYZRtMrLkwbl7ynJRJuxO9IkjgiO8ZQUK91kaMcrreFoUD80PV9oHtUvnlw/\nLx9+lzhzo2AQhsw9mK7AZuHF/pFjr+oZ1KPZNcRQzXAuiCBuiZ6mj9JlXGUQ\n+qaZt4MwHheAdkyDvS+JYzTB/klfCzCaUfg4TzLnimxp9KWvY8CDfX5O72tO\n6DDO4HkHsJkr8sahWuNiXrLTqw42VvfJ6MTo625/CD7yvDTLEhCHpf6M7fb2\nP5EtmHo6MfGtvsTsqvwpH/KhthJ1bFFjPZFG3cidm0XjaWblryNq9SOUCNWT\nDMyhznUZjMCzaq5c85KwXv3FaGXPzEpW49yHg9cNm3OBS/hRA1LOazIUbdS7\nq/3WiLdP+/yWEeKqU7uYtGwF9OO0gvDb8y6jbJ7GCcligcz2kv4r8nnCcUw0\nf6gkQoRu5NBfZSBNFkUouNyeSs04xJXQFQB8tp+mgWRy8AM1Mxqg+2CQra5J\nqN3+Udq9Pp3Nta3yMUdqEfmTTX1LAAbk31FmcIk/3ZeK9hd2TrZpVkVkVzIw\nIMNnnN2VQEGLcVL6U+v/QRxIR7N5Duz+NfVHyPOIO8N/ZFevMn3FzY27iVom\nRhdD\r\n=gsaJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEMCIDWbj+j8VC53ld+j972bUxiUCh3eRDjHK8e4MPF62ZLHAh9O1kiCj7OEdsXViLZpi5SHCRsPOngRWzGktVAWlnCe"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.0_1534246433419_0.17147913386098934"},"_hasShrinkwrap":false},"3.5.1":{"name":"@whsmith/infrastructure","version":"3.5.1","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"94e643f2023bd0b9e4e000ed33013a31fd5f5a82","_id":"@whsmith/infrastructure@3.5.1","_shasum":"b2be660e103c82b8df386b9f1a9ac65be8d69fed","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"b2be660e103c82b8df386b9f1a9ac65be8d69fed","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.1.tgz","integrity":"sha512-lFyri2MWavYAGIhwTfoUrvQkAldP3Ap4SOgQXfNx0CdE3pqExCl9xFT1keHat8HY2qcIljDgJrhGsN2i5CAVqQ==","fileCount":133,"unpackedSize":693660,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7obrCRA9TVsSAnZWagAAAUAP/1iauEJQse41F6eqRRyr\n5o5M97G4C0R0Q8077MrHI2/NHnjOfzpOCMJBP2VWk4c/0C8zhuQYu1ogh/LK\nN/la+ZTicF09qBh7aTmPscHip0hrCXWHdKBjjwtvDAaG5yqi6WYYf5cBw6jP\nOaogcJSsSOpdLFSaxml2vxvTaj+2jZRcvjpYFuaqvNNBm9GWffv5ef+cgJ29\nzSTLfEnYaG7D3SCBvibYZYZiA7IPF4NwGxwzwpSZoSyKo3S1T3rLOFYTVqLO\nUwgpVchsbzlfm9zeETRNPyN9BWdLUXLb1k8jtseMRC9KeF/hlJbpotTiTlOk\nh/sYb7xRE4ZOSuCqr3ft7T2rYlGGnKuvVYLqSYGp+BXFM6LYX4Aqu7pfHIuD\n5PbZGAUnDkAT6qykh6yiq7Gi8zEpfBvU8OWNvVTH/CcTw0MsBdbglaQB7NoD\n30nqj9UiMV6sKbh/TCFw8jQEAKyRi2afY3PfWlWA3/fLpSmr9V/CdPXJvlLE\nIYCJqe3TlRmDRIP9hIWLCGnmro0/WjNmseYxXL/wiA9KDi6G3X/Os4WXyRmx\nePgoafm29ZcgXO34Dn1OBy7404W0tNErFgW9MA69jpJ+KxKqPhXS350nR6Bg\n8eumlN2EFAJtiNe0d9ltWrGfDH7ndRd6utLC6SBG3Mvgv2WEIW07OKN3Wqrt\nkfBN\r\n=l2uc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCsHW0dKsbGbcrVUwinTQXARrNhRfIOQPz5cLwXT81WiQIhAL6r/KvZVA4Ntzigwdp1Ni8QmQ3AaVOZcuZ8xQzVJT2G"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.1_1542358762192_0.1519442844021106"},"_hasShrinkwrap":false},"3.5.2":{"name":"@whsmith/infrastructure","version":"3.5.2","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"f97cb6bdb9c4b74e1c3191dfa498055ff5b2bf25","_id":"@whsmith/infrastructure@3.5.2","_shasum":"3041c4891dc1b1f9d7324c8d17dce68bcae0bc79","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"3041c4891dc1b1f9d7324c8d17dce68bcae0bc79","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.2.tgz","integrity":"sha512-BlFj9DNs7UqP2sqrqi183dvreSDYIpTidaVnnYESRlzlFAIiVICTk2dIecZF5TPNu8EW8+KvpwGQR2b9i4XbjA==","fileCount":133,"unpackedSize":694316,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmI6ICRA9TVsSAnZWagAAHMYP/Rnq0tOxmsx60BBFFfP0\n2/2o1JiiHltcjMxMjiURK/QsoEoKitwWryLJPgvEw+YVw5EVVl0bTQbOikQ/\ndMYAmV6+MxdLAJdpSePSusPwq+TlDdj6GhuURnJF4MyrHSq8FVJyGgL1x0vU\np6NEZQMnGBsHsB9m60/SKxMUSCtwOMQRjRDRlmVy6WkzaGrqgLNxf/HOgZ7G\nJjos93F75HzJVhI7sqJw35tBEvmUEEvk2TFHoq0LdcY+x2YPZAomMo4UphgN\nhGSE8AgWlQIlqAD6hvcafH2dCKGtJb9dpp8H5vPSCWmDWivHzyE3DBugD0WT\n2+YQUizuPrd69pD/cvGYgWR6DSmw4AxOho8ueK3jHDuGIjCQeKNIsFLFgcc2\nR/5hGaH1kxA8YzEN0rRiq7QJh9VkYrOyAJ1r5HcZCbtaCPr0x5gws3BDMmBw\nBV7EmWobZvRu1wTSyqwJk8TPEZp03VKpFklA7sbMXUGnG+LW2mXCtEq/DvEn\nv8zKIjUoYbGN0eiwhS+weVuLZi4Pf47xbvmt8OV4W1j9kSP0voRr1//MZ+ke\nZMUotMcOrfgH/0dd4MGS9CfaiL1uZxz/vUr6RCAA+sI005MYA5a/7+TQef5V\nlaRqW36WnWW789WDyRoTaXys+HkIMFVoIX11ABMnUoScr+0QNvfIVBdcuRFR\n/uDF\r\n=88fG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDuZt0YPSx2SBP0rfVRv5wqZzr+h1AKnUp7sGxAkhg+7wIgDrY7rdSVVLsaqHAMsdK5cUx9K7Aapi4KfcCttWDMYB0="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.2_1553501831888_0.3108852534385351"},"_hasShrinkwrap":false},"3.5.3":{"name":"@whsmith/infrastructure","version":"3.5.3","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"67ac2bb731352809754ded7712c3e242ef4061cd","_id":"@whsmith/infrastructure@3.5.3","_shasum":"c7a35476f065a83331eeaf4703bdf6297947fbd4","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"c7a35476f065a83331eeaf4703bdf6297947fbd4","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.3.tgz","integrity":"sha512-3WGqD7K9iiXI7IgiWPOhf0zJtXebpuFsIZAiILwpdm2KIkmYUBIyfqLQadrLd/fD5E5rUNKdLocQcI5PuH5zKg==","fileCount":133,"unpackedSize":694375,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd+Gz5CRA9TVsSAnZWagAAaI0QAI0Df0QkLCl3sWH3C7yy\nG0Yu2tVME3cQ3dEvpQEfL3JdEq1EKQWAyzV5YGjeUhluHP9cgRbS1vOriAnZ\n088HnJtrCf6NSsdYBWQWFPHjs2H7nDmoFjjQV+SuH6A3Bs7q0z0mOIcjWPbk\ndWRHH/T6ILkZJw4eoapBe8eWgSIAfa6C8wY8muZsJSzWiegNtI0FbKDfs+m9\nawmQ5WFiiNZhzYJD1202JYsbwclSAgw8EATK/T71zxV+kA9lrUURxGZcGGOV\nJZS/L1S8fWF0rZiXAOxJ7xavNBkJL9w/f/TOFwTCmYVpUDqkQ9YwWccRat8g\niN0Skump0V6FSg2uwWbpSvi/W14JU+hdpA/AiPS8bbipTzthasREqSJ5muCl\nOH6BVPA49cuBcamdkfYnzi9cQgviWg5irML4GpTua0kfVoAuLEQWyFJklLMV\nV3gB0sBgxhw41E3yQNMFcUmqV4d+a1l/qnqAWznRGmYMPVTV+0aPbVyXbwMn\nZ2ognLHngh6RCD3UyZ9WRGqqvntEQhcDKjqg4EuNnN3/9n/0LjLyEEDWNuXA\ngIR4YuJkJvx/9TN8WYKZwJtacu/USh0f660dTHzgAd5yXgkhcJD5+i1/x9TE\nNg5MiSlGSSob+13XkShLhVqGnJ0zTLnDT0OKg8Daobiy4L8DdC1C6S2JLuco\n5Hxg\r\n=MzjG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBSzJSvSP/A53N+NbIgofGDbrP503GRNoAyot5s/P2USAiEA45+dcTM281RyNZnRuL/i6HOibq2rhUoYipjaeNilOwY="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.3_1576561913087_0.29937823709150435"},"_hasShrinkwrap":false},"3.5.4":{"name":"@whsmith/infrastructure","version":"3.5.4","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"e3f3fcc92b9601e065fd00d8304b37bfde6dbc5c","_id":"@whsmith/infrastructure@3.5.4","_shasum":"7e1bfaf9f5f32c120c094c559b44ec484ffcc24d","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"7e1bfaf9f5f32c120c094c559b44ec484ffcc24d","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.4.tgz","integrity":"sha512-E4JlFM1FVOeLUml7Kxs8FqMZz6GkntkBBZSj0t548e/jeCy0Iel7s6sDW16AIfjk0J6IFLi+MhKTHECCvMrFcg==","fileCount":133,"unpackedSize":694426,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVh9OCRA9TVsSAnZWagAA5ckP/2R4Yjuo+2PrF0INgk6i\nofH8DC2hOT2JEJATcBDybiAiFEj/ktfpKFdYppPmTK6pIdTI8+BrYaHpk3OC\nhVMs5vZhP//WUwbOlLkgVmRIejh/5cOMW5o06vCKesmwAWXyV7lngS8d5ZRx\nfL3w7ivIPWXNb+JrB2km+j/UIgn4Vvg5kEshoCoeukRCbjVZVzEGnOexAfdB\nbCYMq2eZ5dPXcHD6RqEplZES8MRwwLGrBsskCgrahiUsEEkgCfnDq+E5PKAj\nrD4OtRkD7BI5AZqO5mhZb8cj/Eo4kg44znucj8vugK/csGVwFEqTUUcUr6dO\nCacAsrH46wCNcHXP2JfQMA9LSR9QKO4Zn4ufI3dyLMnUJwVHbjPDQRKG7qjQ\npHSiE6PshM5PeusW4lEMGg9wO/koVztY5G+ZPYuLkXZ24h3U+lVSHh/v5bNm\nO2uwxRsXvHrIg50vuOKyEGovabMeiplHax9v7sDZJINjaSHmawlGDk0e4deB\nmf3aOf+geZ+xYoijPArQCs1uFGs6UG8yvGf6KA/HQZQ5NOb8PorlMk/AlLdj\nI6Sn0XZQBKgGcUPOmsKu7PzLPN2ZHH3yVn6GTLaUmRyxCiMAff3hh6BYYbpM\nf3kQN7JR7RCWAk+erLMjtIZQ6JniTu4Pej81rBoHlOfecI0mqiPHZaFMZavK\nUJxC\r\n=+TOp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD33nIiT/a+Irum3T2em7BdswvIJnNV2Hxz3CHfnY++NQIhAPLPZ2qNtO8WJ/A7GvWwAIlQvKUZCykJds+/DTu4nIfD"}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.4_1582702414081_0.7011877573140006"},"_hasShrinkwrap":false},"3.5.5":{"name":"@whsmith/infrastructure","version":"3.5.5","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"58e3c51caa52417c98340d7b697122879b49b418","_id":"@whsmith/infrastructure@3.5.5","_shasum":"6cf0a1ebccd5951d784bac0ffb519e7f37c96893","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"6cf0a1ebccd5951d784bac0ffb519e7f37c96893","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.5.tgz","integrity":"sha512-FRJy9YQ9ZyoLF91zwfYHWyOLLh6q+FGzfPZ5dz46lLBNtLFlrmpG0spfsqfnqmmqyisVZLZ6wUnujIt70Tl9Gg==","fileCount":135,"unpackedSize":695652,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeV0XsCRA9TVsSAnZWagAAusQP/0TRhMd8cbCP2mUWG9wl\nBe41OSzlB6IIy6RaE1atfsEF7wirUllRa98olW0qVC/Lg7oIBAXWhl7pkBWR\n0asclTBxogwlTpVNDjLWVL/CGKyZEp1+hvOEL0xzTp9kbZWQylsRUX81G4cM\n4zpvPqSldRE9n14iMqNKUOPgtRS/BG9XVRgBEKsppmqE6djlprnW5BWR+6u3\n6XyQUkrO3IiMKojZIKkU11zgXWs0cY1knNa9Cf1eQMjaEhkyytUmL3jdOKnU\naJxO3vs1lUrApX/iW1yJFrEvFIaoKvWI0DKYLG2HF5ZdK9QOJEJa3BBxE+5D\nMoS/Q2xGjjk5v5xnIf2yQU+srqSbp4MdYJW9exGlnfRuJ6e29vmSXCXpW9yP\n8kGAlElewX7HZpy3nhvRv3GeIT+3lyzgEJ+3lvs8zpg2FqroKolCYfKAMzHG\nXFWcLhAtfE3fA/MkPLAl46l/xnrWNu+3H0iNYFlnVeiTL1l89NGk6ooBgtxz\nMGB9qwBIIc7HKFX79LXM7z1vLYAojSEt6gOtltA1leJI6WDXAfA67X5D7/Id\nYg29AVJvdi+qJl5xfkFfOohY31CTMVJQVH0pTByvGMmufj4hU3sTTFsq6T53\nX6jeDwP/TNygesGsNDV0BJrPhQ2OAcDtOSj6bAOeGD+bzButSXOMX7DKfPjs\nWzjG\r\n=LiBP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHSzIJWI3wCIh1OP6Hl9jxlIL0je8WRZbqwmO44Wnpu/AiEAurTWznEeUTL2uprSTpX6JiM0Q7IaeSxacHVTqDpsDKQ="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.5_1582777836113_0.30141208818820453"},"_hasShrinkwrap":false},"3.5.7":{"name":"@whsmith/infrastructure","version":"3.5.7","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"a47aa8737977f00a472f5bb4c892243451d39170","_id":"@whsmith/infrastructure@3.5.7","_shasum":"cb53e2aa484ff4c1eb01a6b5651ae33ed6a53ba3","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"cb53e2aa484ff4c1eb01a6b5651ae33ed6a53ba3","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.7.tgz","integrity":"sha512-arDO8S1urNXq5BC78y06bWg/oGj/38UCB4qPM8b8dwxuUEp4+HJUbjo5XV5bRl9HVsCQRDZgfrfwxei8lDMEjA==","fileCount":133,"unpackedSize":694782,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXdcGCRA9TVsSAnZWagAAuGMP/3nf3SQPTEMWU3V/719q\nVqtecVsKeyZUziYQi4h3gF0ONBHMF4BKz+WA5bvCL3seqskfAoqRsQv+E+C1\nDPQ9yLWczbLoNc9HnIe5E5KRnnpauDRJ33tSNFDz2iHR2DHgDabN0xxUNmWW\nUeoPmMbDic5YP4j1FBv22Wi7bbg28iWytl9b74EAIXWRkhXPg1ntJkohksOj\nGuPH5AxD6ssypLG9X+gMp9/DyYWcNHdz7O3he2cAR9ev0akL74nar8MnXlKg\nYSzd3h2ZtBAfdnECQ/vYjr1abCh18QENv30gwkrs8oJropIV44roNd8Boxrk\nS453Vm/JuzGojeCKXG7cYFN8f4lxGVV2Ew9v5udp4JnpfeZWLgFvJTrh3xuC\nSNDAqfEA4AzRlbJwTrm8ig0jRkB67YnJ5aFJu//C3rpyLI0D2DQEzCBUU0wD\n+vzkchdn4+DIg2ASg9VCp+nry5NpT+BfGiZ/oIv1iYg5ZhTc44MFs7jA6rGj\nn6zDxZrnAXiHyRPS0jqiCAkcrnkZRwi0V7AqBIR58jBE3PMX5zWpq6Ert/AL\nhxbsVheALNplbtrTBQJSxJAz8YGdqQzEEK8KfVD3Ul/Mzc1R/XD9VG557Qd6\n/1vMqXHuAHixBSb3/ijzhMJBKldq31QUT5GzIl8K+8ub7SlFviLP66w4b5xr\nYGuq\r\n=cXN2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/2YokKFlDV4R93dLihIWElQ+plkSeix0478ksKQB7DQIgUj1mARhSTCB0WVeaxEcS8VLa+JFBPbf94X/1a5A/3/k="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.7_1583208196837_0.988973679981729"},"_hasShrinkwrap":false},"3.5.8":{"name":"@whsmith/infrastructure","version":"3.5.8","author":{"name":"WHSmith Development Team"},"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","license":"MIT","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"main":"index.js","scripts":{"lint":"eslint ./src/","lint-fix":"eslint --fix ./src/"},"dependencies":{"angular":"1.6.6","jquery":"^1.9.1","lodash":"^4.17.4","moment":"^2.19.3","eslint-restricted-globals":"^0.1.1","object.assign":"^4.1.0","object.entries":"^1.0.4","pdfmake":"^0.1.37","sticky-table-headers":"^0.1.19","sweetalert2":"^7.25.6"},"devDependencies":{"eslint":"^4.19.1","eslint-find-rules":"^3.2.3","eslint-plugin-import":"^2.12.0"},"optionalDependencies":{},"gitHead":"1e60bade553c0e4757b1638e8497c50514f4f315","_id":"@whsmith/infrastructure@3.5.8","_shasum":"538f5b8ffb9e091adbb8386e0508350b7206a7d5","_from":".","_npmVersion":"3.10.6","_nodeVersion":"6.11.1","_npmUser":{"name":"whsmith.jenkins","email":"jenkins@whsmith.co.uk"},"dist":{"shasum":"538f5b8ffb9e091adbb8386e0508350b7206a7d5","tarball":"https://registry.npmjs.org/@whsmith/infrastructure/-/infrastructure-3.5.8.tgz","integrity":"sha512-4Zy+AqZB36sbn/RBqyX7JvIYmjXQdhutUvga3fnibPiNloaA9KgLTwgQSmjahgWtfqwdD5WHCznT67NQWT/ihA==","fileCount":133,"unpackedSize":694813,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXdm8CRA9TVsSAnZWagAAILMP/AifXZk442D/3NZ3gLgK\nEN5C885xkoNRdP4pnppXGHdLXlcX1Fo1mBpdBr1D+USY6BexCI3/LPW72rsz\ni0wSuTi/a7AE/W1xTxIsQCcowtaA6bb7cWzxyA7vdqaLKZ3TqhPPm8A+94bG\nNwdbEOrCbb+YpD7Gxq4EcebGOGOGy3dmjsgp+grRmZIWV4QkwiLNe0V3k1Ip\na4WyM3fX5hdEofFu+Wa/AWuh/3166Pd7xJwmjM/G5yE/eGKTfNIP+FYVoAQN\nrmClwVLMixA4eP2H8RRTDHLbsl7R8P3sA2rl/TXorwmGigN9hcJ5EpKmoGRM\nffmJ3v/rXiZSH55sUAYqn6bs77kzRY2ACxRSs++emm4PdCCuCRUajSPYOaVD\nMKFH/KfWjj4JhsRZl5BgSNIi1GG2L5ycd3yWbw0vxFScRuESSvhwz636vA8a\n4RhHi5wygHzMS6w4zEo7xtSMeIK1SPH+a363oMKZ5lgUlxoRpc2+Nthx81YK\n2LoIWF9Y4Uc5y5K3JCmFmEtvGk6p1ZeSn924k3NFiD76h8KJkqxhLr2c8rlq\nWgRPcy2wD71bjfc5k+ibuX14lGX6XEUTyhN2kzd5E+dhVksHL6NlV5JeUhOl\ndn2SLU1XxyeVC6KebrAe3e9TfYg5BSpwz0K/qQsBH0UMqAFpB4Ozwcac5/CT\nHAIJ\r\n=+HyL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWcwXw01frfDa/ygKoH0h9Rg5q9AiMpV5NaAXUSf9nnQIge7INkO791bYP2cQoC6wrvXanhtzz5TImDRIX2ky9yiU="}]},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/infrastructure_3.5.8_1583208892049_0.7482985033426774"},"_hasShrinkwrap":false}},"time":{"created":"2018-07-13T11:43:57.702Z","3.0.0":"2018-07-13T11:43:57.817Z","modified":"2022-06-13T01:40:53.101Z","3.0.1":"2018-07-16T13:21:17.467Z","3.0.2":"2018-07-16T13:38:15.190Z","3.0.3":"2018-07-16T13:42:28.462Z","3.0.4":"2018-07-16T14:44:57.363Z","3.0.5":"2018-07-16T14:48:22.458Z","3.0.6":"2018-07-16T14:59:28.005Z","3.0.7":"2018-07-17T10:51:10.292Z","3.0.8":"2018-07-17T10:56:18.023Z","3.0.9":"2018-07-17T11:02:58.336Z","3.0.10":"2018-07-17T11:19:48.166Z","3.0.11":"2018-07-17T13:25:39.511Z","3.0.12":"2018-07-17T15:01:35.473Z","3.0.13":"2018-07-18T09:14:50.433Z","3.0.14":"2018-07-19T08:17:51.856Z","3.0.15":"2018-07-19T10:16:37.291Z","3.0.16":"2018-07-19T10:32:27.100Z","3.0.17":"2018-07-19T11:13:32.746Z","3.0.18":"2018-07-19T11:32:13.006Z","3.1.0":"2018-07-20T11:58:28.607Z","3.1.1":"2018-07-20T12:35:22.974Z","3.1.2":"2018-07-23T08:19:22.826Z","3.1.3":"2018-07-25T12:14:50.551Z","3.1.4":"2018-07-25T12:58:54.412Z","3.2.0":"2018-07-27T10:52:24.521Z","3.4.0":"2018-08-01T14:53:41.153Z","3.4.1":"2018-08-02T09:21:35.758Z","3.4.2":"2018-08-02T14:41:17.235Z","3.5.0":"2018-08-14T11:33:53.537Z","3.5.1":"2018-11-16T08:59:22.419Z","3.5.2":"2019-03-25T08:17:12.087Z","3.5.3":"2019-12-17T05:51:53.241Z","3.5.4":"2020-02-26T07:33:34.310Z","3.5.5":"2020-02-27T04:30:36.297Z","3.5.7":"2020-03-03T04:03:17.058Z","3.5.8":"2020-03-03T04:14:52.238Z"},"maintainers":[{"email":"aaron.barnaby@whsmith.co.uk","name":"aaron.barnaby"},{"email":"jenkins@whsmith.co.uk","name":"whsmith.jenkins"}],"description":"WHS Application Infrastructure Plugin for DNN and AngularJS","repository":{"type":"git","url":"git+ssl://git@10.116.182.161:Applications/Infrastructure.git"},"author":{"name":"WHSmith Development Team"},"license":"MIT","readme":"# Infrastructure\r\nInfrastructure is our core library for all SPA applications build within WHSmiths.  It provides a wide range of helper methods and tools as well as styles, directives and filters.\r\n\r\nAlso infrastructure has a range of extensions which you can read more about below.\r\n\r\n## Quick links\r\n- [Installation](#installation)\r\n- [Setup](#setup)\r\n- [Directives](#directives)\r\n  - [Forms](#forms)\r\n- [Filters](#filters)\r\n- [Services](#services)\r\n- [Extensions](#extensions)\r\n  - [Custom ESLint Config](#eslint-config)\r\n  - [WHS Table](#whs-table)\r\n  - [WHS Datepicker](#whs-datepicker)\r\n  - [WHS Print](#whs-print)\r\n- [Support](#support)\r\n    - [FAQ](#faq)\r\n    - [Found a bug?](#found-a-bug)\r\n\r\n## Installation\r\n\r\nInstallation is easy as we have followed standards for all our plugin development.\r\n\r\n```sh\r\n$ npm install @whsmith/infrastructure --save\r\n```\r\n\r\nThis will install WHS Infrastructure to your node_modules.\r\n\r\nHowever it's likely infrastructure is already in your `package.json` file:\r\n\r\n```json\r\n\"@whsmith/infrastructure\": \"^3.0.0\",\r\n```\r\n\r\nVersion numbers use a sym versioning system so `major`.`minor`.`build`.\r\n\r\nBreaking changes will cause the `major` version update.\r\n\r\nNew features will cause a `minor` version update (unless the new feature causes a breaking change).\r\n\r\nBug fixes will cause a `build` version update.\r\n\r\nIt's important to take all this into account when selecting or changing your version It's also a good idea to check the `CHANGELOG.md` file for information on what has changed with breaking change information.\r\n\r\n# Setup\r\n\r\nNow you can import into your application (within your `app.js`):\r\n\r\n```js\r\nimport { infrastructure } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure]);\r\n```\r\n\r\nYou will also need to make sure that your `WHS.App.start` has the following line to load the config\r\n```js\r\nWHS.App.constant('loaderConfig', WHS.Loader);\r\n```\r\n\r\n# CORE\r\n\r\n## Directives\r\n### Error Box\r\nUsed to display error messages\r\n\r\n```html\r\n<error-box error=\"{errorObj}\" onDismiss=\"function()\"></error-box>\r\n```\r\n\r\n```js\r\nconst errorObj = {\r\n  ErrorMessage: 'Main message for error',\r\n  Errors: ['Multi pointed error', 'Listed below main message'],\r\n  HelpText: 'Error help text',\r\n  HelpLink: 'http://google.co.uk',\r\n  FullException: 'Full Stack Trace Exception',\r\n};\r\n```\r\n\r\n### ErrorSrc\r\nLoads a src if the original src fails to load.\r\n\r\n```html\r\n<img src=\"\" error-src=\"\" />\r\n```\r\n\r\n### Focus\r\nFocus element on load\r\n\r\n```html\r\n<input focus />\r\n<input focus-on />\r\n<input focus-me />\r\n```\r\n\r\n### Last Modified\r\n```html\r\n<last-modifed model=\"{obj}\"></last-modified>\r\n```\r\n\r\n```js\r\nconst obj = {\r\n  LastModifiedBy: 'User', // Display Name\r\n  LastModifiedOn: moment(), // moment date\r\n};\r\n```\r\n\r\n### Loading Wrapper\r\nUsed to wrap content loaded from a promise\r\n\r\n  - **Attributes**\r\n    - `busy` - Instance of the busy library.\r\n    - `content` - Model referance containing text or html to render while loading.\r\n    - `text` - Raw text to display while loading (`content` will overright this).\r\n    - `once` - If provided loading wrapper will load once and then ignore changes to the busy instance.\r\n\r\n```html\r\n<loading-wrapper busy=\"{busyInstance}\" content=\"htmlContentModel\" text=\"Loading Text\" once>\r\n  <!-- CONTENT -->\r\n</loading-wrapper>\r\n```\r\n\r\n### ngEnter\r\nAttaches an enter keybinding action to element that will trigger the referenced function or will remove enter actions if no function is provided.\r\n\r\n### numberInput\r\nRestrict input key press of anything but numbers.\r\n\r\nAllows: backspace, enter, escape, arrows, Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X.\r\n\r\n### ngPatternRestrict\r\nRestrict `ng-model` value to follow the `pattern` provided\r\n```html\r\n<input type=\"text\" ng-model=\"name\" pattern=\"([A-Z])\\w+\" ng-pattern-restrict>\r\n```\r\n\r\n### Spinner\r\nDisplays a simple spinner based on the expression or referenced value.\r\n```html\r\n<spin active=\"true/false\"></spin>\r\n```\r\n\r\n## Forms\r\nThe following directives are available for building and validating forms.\r\n\r\n### Control Group\r\nRenders a simple form group with label and required flag.\r\n```html\r\n<control-group label=\"Your Input Label\" required>\r\n  <input type=\"text\"\r\n  name=\"someInput\"\r\n  class=\"form-control\" />\r\n</control-group>\r\n```\r\n\r\n### Validation\r\nImplements advanced validation on input model value. (see [validation rules](#validation-rules) for more information)\r\n```html\r\n<input type=\"text\"\r\n  name=\"someinput\"\r\n  class=\"form-control\"\r\n  ng-model=\"myModel\"\r\n  validation=\"min_len:2|required\" />\r\n```\r\n\r\n### Form Submit\r\nSimple replace to a standard `ng-click` on a button to trigger a function IF the wrapping form is valid.\r\n```html\r\n<ng-form name=\"testForm\" novalidate>\r\n  <button type=\"button\" class=\"btn btn-success\" form-submit=\"Submit(testForm)\" disable-on-invalid>Submit</button>\r\n</ng-form>\r\n```\r\n\r\nThe function will only be triggered if the wrapping form is `$valid`.\r\n\r\nIf the `disable-on-invalid` attr is used the the button will be disabled when the form is invalid.\r\n\r\n## Filters\r\n\r\n### checkOngoing\r\nDisplays `Ongoing`, `From {date}`, `Until {date}` or `Expired`.\r\n```html\r\n<span>{{date | checkOngoing:start:end</span>\r\n```\r\n\r\n### clearZero\r\nThis filter will clear any Zero (0) value.\r\n\r\n### company\r\nT = Travel, H = High Street, B = Both\r\n```html\r\n<span>{{data | company}}</span>\r\n```\r\n\r\n### dateUntil\r\nReturns date or `No End Date` if the date if same or after the extreme date `2999-12-31`.\r\n\r\n### groupBy\r\nBest used in the controller will group data by property with `key` and `data` mapping.\r\n\r\n```js\r\nconst grouped = $filter('groupBy')(data, 'myGroupProperty');\r\n// grouped.key = myGroupProperty\r\n// grouped.data = Array of grouped objects.\r\n```\r\n\r\n### htmlEscape\r\nEscape any html in value.\r\n\r\n### moment\r\nParse and return data in set format\r\n```html\r\n{{ '2017-04-01': moment:'DD/MM/YYYY':'YYYY/MM/DD' }}\r\n```\r\n`moment:format:parseFormat`\r\n\r\n### percent\r\nReturn the percent value from a decimal (defaults 1 decimal place & precision 1)\r\n\r\n`percent:decimals:precison`\r\n```html\r\n{{ 0.254242: percent:1 }}\r\n```\r\n\r\n### textReplace\r\nFind and replace text in value\r\n\r\n`textReplace:search:replace`\r\n```html\r\n{{ 'Some Text': textReplace:'Text':'Cake' }}\r\n```\r\n\r\n### titleCase\r\nTitle case input based on abbreviationsList\r\n\r\n`titleCase:abbreviationsList`\r\n\r\n### trim\r\nTrim value removing any whitespace\r\n\r\n### uniqueUrl\r\nAdds a unique queryString to the end of provided url value to prevent caching\r\n\r\n## Services\r\n\r\n### whsHelper\r\nThe helper is a must include for most application controllers as it inherits a lot of core services and useful functions.\r\n\r\n```js\r\n// Properties\r\nroot // Inherits $rootScope\r\nbusy // Inherits the busy service\r\nlogger // Inherits the whsLog service\r\nauth // Inherits the whsAuth service\r\ndialog // Inherits the whsDialog service\r\nutil // Inherits the whsUtil service\r\nconfig // Inherits the whsConfig service which inherits loaderConfig\r\nservice // Inherits the CommonService\r\n\r\n// Functions\r\nsendError(message, ...objs); // Will log error objs to console in DEBUG MODE otherwise will send email to developers with objs.\r\ngetSettings() // Returns all the application settings (DNN Settings)\r\ngetSetting(key) // Returns the selected application setting by key\r\ngetUser() // Returns current logged user details or null\r\nshowInfo(message) // Display information sweetalert with message\r\nshowQuestion(title, message) // Display question sweetalert with title and message\r\nshowSuccess(message) // Display success sweetalert with message\r\nshowSuccessToast(message, timer) // Display a successful toast with message and close after timer (ms) (Default: 3000)\r\nshowWarning(message) // Display warning (opps) sweetalert with message\r\nshowError(message, ...objs) // Display error sweetalert with message (first parameter), any additional parameters will be logged to console, an option to report will also be presented\r\nshowErrorWithoutReport(message, ...objs) // Display error sweetalert with message (first parameter), any additional parameters will be logged to console\r\nshowErrorToast(message, timer, ...objs) // Display a error toast with message and close after timer (ms) (Default: 3000)\r\nshowConfirm(options, promise) // Display a confirmation sweetalert giving the user Yes/No Options and executing a promise on yes.\r\ndebug(...) // Log all params to console\r\naddDebugVariable(name, value) // Add a variable to the debug page '/debug'\r\nlogError(...) // Log all params to console as error\r\nsaveAudit(ref, message, oldData, newData) // Adds a new audit event for the current application (each property can be null)\r\n```\r\n\r\n### whsConfig\r\n```js\r\n// Properties\r\n. // Inherits the properties of loaderConfig\r\nappSettings[] // appSettings array of [key] = value (DNN Settings)\r\nDNNAccess // true/false based on if application running in DNN\r\n```\r\n\r\n### whsLog\r\nLog helper handles most console logging\r\n\r\n```js\r\nlog(...); // Prints all args to console as log/debug\r\ninfo(...); // Prints all args to console as info\r\nwarn(...); // Prints all args to console as warning\r\nerror(...); // Prints all args to console as error\r\ntable(...); // Exec console.table(data, [, columns])\r\ntrace(); // Print console trace to this point\r\nclear(); // Clear console\r\ntimeStart(key); // Start timer with key\r\ntimeStop(key); // End timer with key\r\n```\r\n\r\n### pageTitle\r\n\r\n```js\r\npageTitle(title, header, includeAppName) \r\n  // title = page name to display in window/tab title\r\n  // header = page header to display at top\r\n  // includeAppName = true/false - should application name be displayed after title in window/tab title.\r\n```\r\n\r\n### whsDialog\r\n\r\n```js\r\nconfirmModel(options) // Display a confirm model popup\r\n  // OPTIONS\r\n  //   - title: model title\r\n  //   - body: model body\r\n  //   - confirmButtonText: confirm button text, defaults : 'Confirm'\r\n  //   - cancelButtonText: cancel button text, defaults: 'Cancel' \r\n  //   - execute: promise function to run on confirm.\r\nalertMessageBox(options) // Display a alert model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nsuccessMessageBox(options) // Display a success model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nerrorMessageBox(options) // Display a error model popup\r\n  // OPTIONS\r\n  //    - title: model title\r\n  //    - message: model message (Allowed HTML)\r\nmessageBox(caption, message, buttons) // Display a model popup message box\r\n  // caption: title of the model\r\n  // message: body of the model\r\n  // buttons: array of buttons\r\n  //   - cssClass: button classes\r\n  //   - result: button return result\r\n  //   - label: button text\r\nshowConfirm(options, promise) // Display a confirming SWAL (Sweetalert)\r\n  // OPTIONS\r\n  //   - title: title of the swal (defaults: 'Are You Sure')\r\n  //   - html: body of the swal (defaults: 'Please confirm you wish to complete this action')\r\n  //   - confirmButtonText (defaults: 'Yes')\r\n  //   - cancelButtonText (defaults: 'No')\r\n  // promise: promise function to run on confirm\r\ngetSwal()                 // Get an instance of `swal`\r\nshowSwal(options)         // Open a swal with the povided options\r\ncloseSwal()               // Close any open swal dialogs\r\ngetMixin(options)         // Create a swal mixin\r\nrunSwalQueue(items)       // Run array of swal options\r\nshowSwalError(message)    // Display validation error on swal\r\ntriggerSwal(options)      // Trigger a swal with defaults\r\nshowWarning(options)      // Display a 'Opps!' warning swal\r\nshowError(options)        // Display a 'Error!' error swal\r\nshowSuccess(options)      // Display a 'Success!' success swal\r\nshowInfo(options)         // Display a `Info` info swal\r\nshowQuestion(options)     // Display a question swal.\r\n```\r\n\r\n### whsUtil\r\n\r\n```js\r\nescapeHTML(string) // Escaped any html found in string\r\ntoBoolean(string)  // returns true/false based on string value\r\ngoBack() // Return to previous state or `app.default`\r\ncalculateWeekNumber(dateString) // Return the week number of date\r\ncalculateMonthNumber(dateString) // Return the month number of date\r\ncalculateYearNumber(dateString) // Return the year number of date\r\ncheckCalender(dateString) // Returns moment date plus the week, month and year numbers of the provided date.\r\n```\r\n\r\n### whsGateway\r\n\r\n```js\r\n// To initialize the gateway you must first create a new instance\r\nconst Gateway = whsGateway.create(clientName, version); // Client name is required, version will default to 'v1'.\r\n\r\n// Using the newly created instance you have access to the following methods.\r\nsetupUrl(url, client, version) // Return gateway url based on the url, client and version.  (client and version will use the instance values if not provided here).\r\n\r\nget(url, config)          // Trigger a HTTP GET Request\r\npost(url, data, config)   // Trigger a HTTP POST Request\r\nput(url, data, config)    // Trigger a HTTP PUT Request\r\ndelete(url, config)       // Trigger a HTTP DELETE Request\r\n```\r\n\r\n### whsAuth\r\n\r\n```js\r\ngetUser()  // Get current user object or null.\r\ngetUserIpAddress() // Return current user IP or ''.\r\ngetPermissions() // Return current application permissions\r\ndirectToLogin(target, targetParams) // Redirect to login page with the set return target & params.\r\ncheckAuth(required) // Check if current user is authenticated and redirect to login if not authenticated.\r\n```\r\n\r\n### busy\r\n\r\n```js\r\n// First initialize a busy instance\r\nconst isLoading = busy.create;\r\n\r\n// With your new instance you can add a promise(s)\r\nisLoading.promise(myPromiseFunction)\r\nisLoading.promises([promiseFunction1, promiseFunction2]);\r\n// When a promise is running the `isLoading.busy` state will update.\r\n\r\n// You can also force a loading error with:\r\nisLoading.showError(message) // Display message as error\r\nisLoading.showErrors(message, errors) // Display error message and errors array\r\nisLoading.clearError() // Will clear any errors\r\n```\r\n\r\n### pollLoop\r\n\r\n```js\r\n// First initialize a pollLoop instance\r\nconst loop = pollLoop.create($scope, () => functionThatReturnsAPromise(), 4000);\r\n//  Instance of angular scope\r\n//  Function that returns a promise to trigger on polling\r\n//  Time between polls (optional defaults to 4000/4secs)\r\n\r\n// PoolLoop Methods\r\nstart() // Start polling after the timer\r\nstartImmediately() // Start now\r\nstop() // Stop current instance\r\n```\r\n\r\n\r\n### CommonService\r\n\r\n```js\r\nGetAudit(ref)  // Get audit event list for current application\r\nGetAuditForApplication(application, ref) // Get audit event list for application\r\nAddAudit(data) // Add new audit event data\r\nAddAuditEvent(ref, message, oldData, newData) // Add new audit with current user, application and state url.\r\ngetTitleCaseDictionary() // Return `Common/TitleCaseDictionary` API method\r\nsendEmail(data) // Send email using `Common/SendEmail` API method\r\nsendErrorEmail(body, fromUser) // Sends error email to developers\r\n  // body: HTML Enabled email body\r\n  // fromUser: user object.\r\n```\r\n\r\n### Validation Rules\r\nValidation rules can be used within the validation directive.\r\n\r\nFor example the validation rule of `required` has no params and is passed simpily:\r\n```html\r\n<input type=\"text\"\r\n  validation=\"required\" />\r\n```\r\n\r\nYou can pass multiple validation rules using a `|` (pipe) and params are added to rules using `:` semi-colon.\r\n```html\r\n<!--- Required field with min length of 2 -->\r\n<input type=\"text\"\r\n  validation=\"required|min_len:2\" />\r\n```\r\n\r\nLastly a custom error message can be provided with '`alt=`'.\r\n```html\r\n<input type=\"text\"\r\n  validation=\"required:alt=You forgot this field.|min_len:2:alt=Need atleast 2 characters.\">\r\n```\r\n\r\n#### Available Rules\r\n```js\r\n// All rules are written as snake_case but camelCase is also possible.\r\n\r\naccepted            // The field under validation must be yes, on, 1, or true.\r\nalpha               // Only alpha characters (including latin) are allowed (a-z, A-Z)\r\nalpha_spaces        // Same as `alpha` but includes spaces\r\nalpha_num           // Same as `alpha` but includes (0-9)\r\nalpha_num_spaces    // Same as `alpha_num` but includes spaces\r\nalpha_dash          // Same as `alpha_num` but includes dashed (a-z,A-Z,0-9,_-)\r\nalpha_dash_spaces   // Same as `alpha_dash` but includes spaces\r\nbetween_num:min,max // Number between min and max\r\nbetween_len:min,max // Length between min and max\r\nbetween_date_iso:d1,d2 // Alias of `date_iso_between`\r\nbetween_date_euro_long:d1,d2 // Alias of `date_euro_long_between`\r\nbetween_date_euro_short:d1,d2 // Alias of `date_euro_short_between`\r\nboolean             // Ensures value is true or false (or 0 or 1)\r\ncompare             // Alias of `match`\r\ncredit_card         // Valid credit card number\r\ncustom:f            // Calls a custom function (f) for validation  (see custom rule details below)\r\ndate_iso            // Ensure date follows ISO format (yyyy-mm-dd)\r\ndate_iso_between:d1,d2 // Ensure date follows ISO format and is between (d1) & (d2)\r\ndate_iso_max:d      // Date must follow ISO format and lower or equal than date (d)\r\ndate_iso_min:d      // Date must follow ISO format and is higher or equal than date (d)\r\ndate_euro_long      // Date must follow the european long format (dd-mm-yyyy) or (dd/mm/yyyy)\r\ndate_euro_long_between:d1,d2 // Date must follow european long format and is between (d1) & (d2)\r\ndate_euro_long_max:d  // Date must follow european long format and is lower or equal than date\r\ndate_euro_long_min:d // Date must follow european long format and is lower or equal than date\r\ndate_euro_short     // Date must follow the Euro short format (dd-mm-yy) or (dd/mm/yy)\r\ndate_euro_short_between:d1:d2 // Date must follow euro short format and is between (d1) & (d2)\r\ndate_euro_short_max:d // Date must follow euro short format and is lower or equal than date\r\ndate_euro_short_min:d // Date must follow euro short format and is higher or equal than date\r\ndifferent           // Alias of `different_input`\r\ndifferent_input:f   // Must be fidderent from another input (f) where (f) must be the exact ngModel attribute to compare to.  The error message will use the input name or the `friendly-name` if it was provided.\r\ndigits:n            // Ensures the field only has integer numbers and length precisely matches (n)\r\ndigits_between:min,max // Ensures the field only has numbers and is between (min) & (max)\r\nemail               // Checks for a valid email\r\nemail_address       // Alias of `email`\r\nenum                // Alias of `in_list`\r\nexact_len:n         // Ensures that field length precisely matches the length (n)\r\nfloat               // Has to be a floating value\r\nfloat_signed        // Has to be a floating value, could be signed (-/+).\r\nin                  // Alias of `in_list`\r\nin_list:foo,bar,..  // Ensures the value is included inside the given list of values (separated by ,)\r\nint                 // Alias to `integer`\r\ninteger             // Only positive numbers\r\nint_signed          // Alias to `integer_signed`\r\ninteger_signed      // Only numbers, can be signed (-/+)\r\nip                  // Alias of `ipv4`\r\nipv4                // Check for valid IPv4 IP.\r\nipv6                // Check for valid IPv6 IP.\r\nmatch:f             // Match another input field (f) where (f) must be the ngModel attribute of the field to compare.  The error message will use the `friendly-name` if it was provided.\r\nmatch_input         // Alias of `match`\r\nmax_date_iso        // Alias of `date_iso_max`\r\nmax_date_euro_long  // Alias of `date_euro_long_max`\r\nmax_date_euro_short // Alias of `date_euro_short_max`\r\nmax_len:n           // Checks field length no longer than (n)\r\nmax_length:n        // Alias of `max_len`\r\nmax_num:n           // Checks numeric value to be lower or equal than (n)\r\nmin_date_iso        // Alias of `date_iso_min`\r\nmin_date_euro_long  // Alias of `date_euro_long_min`\r\nmin_date_euro_short // Alias of `date_euro_short_min`\r\nmin_len:n           // Checks field length is no shorter than (n)\r\nmin_length:n        // Alias of `min_len`\r\nmin_num:n           // Checks numeric value to be higher or equal than (n)\r\nnot_in              // Alias of `no_in_list`\r\nno_in_list:foo,bar,.. // Ensures the value is not within the given list\r\nnumeric             // Only positive numbers\r\nnumeric_signed      // Only numbers, can be signed (-/+)\r\npattern             // Ensures it follows a regular expression (pattern=/^(A-Z)$/)\r\nremote:f            // Calls function (f) that returns promise for validation (see remote rule below for more details)\r\nrequired            // Ensures the field exists and is not empty\r\nsame                // Alias of `match`\r\nstring_len          // Alias of 'between_len`\r\nstring_length       // Alias of `between_len`\r\ntime                // Ensures time follows the format (hh:mm) or (hh:mm:sss)\r\nurl                 // Check for valid URL or subdomain\r\n```\r\n\r\n##### Custom Rule\r\nWhen using a custom rule your validation function can either return just a true/false bool or the bool and a error message.  If only the bool is returns any error messages should be handles with `alt` message defined in the validator.\r\n\r\n```js\r\nmyCustomValidation(value) {\r\n  let isValid = false;\r\n  if (value === 'abc') {\r\n    isValid = true;\r\n  }\r\n\r\n  // If valid is true or error message is handled with `alt` inside validator\r\n  return isValid;\r\n\r\n  // If valid is false and error message is not handled inside validator\r\n  return { isValid, message: 'custom error' };\r\n}\r\n```\r\n```html\r\n<input type=\"text\"\r\n  ng-model=\"myValue\"\r\n  validation=\"custom:myCustomValidation(myValue)\">\r\n\r\n<input type=\"text\"\r\n  ng-model=\"myValue2\"\r\n  validation=\"custom:myCustomValidation(myValue2):alt=There was an issue validating this field.\">\r\n```\r\n\r\n##### Remote Rule\r\nWhen usign a remote rule your validation function will need to return a promise which returns the same as the `custom` rule.\r\n\r\n```js\r\nmyRemoteValidation(value) {\r\n  const def = $q.defer();\r\n\r\n  // Using a timeout to simulate an API call\r\n  setTimeout(() => {\r\n    const isValid = (value === 'acb') ? true : false;\r\n\r\n    // Option #1\r\n    def.resolve(isValid);\r\n\r\n    // Option #2\r\n    def.resolve({ isValid, message: 'Returned error from promise.' });\r\n  }, 1000);\r\n\r\n  return def.promise;\r\n}\r\n```\r\n```html\r\n<input type=\"text\"\r\n  ng-model=\"myValue\"\r\n  validation=\"remote:myRemoteValidation(myValue)\">\r\n```\r\n\r\n## Routing And Permissions\r\n\r\nWhen adding states you are giving a bunch of additional options you can add to extend your route.\r\n\r\n```js\r\n$stateProvider.state('app.sample', {\r\n  url: '/',\r\n  redirectTo: 'app.default', // Redirect to the desired state\r\n  defaultSubRoute: 'app.sample.page', // Redirect to the desired sub route (will retain parameters)\r\n  layout: {\r\n    title: 'Infrastructure', // Browser title to display for page\r\n    header: 'Infrastructure' // Page header to display for page\r\n  },\r\n  allowAnonymous: true // Flags that the page doesn't require authentication\r\n});\r\n```\r\n\r\nIn regards to permission it will closely relate to roles and inheritance:\r\n```json\r\n[\r\n  {\r\n      \"Name\": \"User\",\r\n      \"Description\": \"Groups that have default access to use and view the application\",\r\n      \"Roles\": [\"All\"],\r\n      \"Inherit\": \"Admin\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n      \"Name\": \"Admin\",\r\n      \"Description\": \"\",\r\n      \"Roles\": [\"Administrator\"],\r\n      \"Inherit\": \"\",\r\n      \"AllowedAccess\": true\r\n  },\r\n  {\r\n    \"Name\": \"StoresAdmin\",\r\n    \"Description\": \"Roles that have access to change there store\",\r\n    \"Roles\": [\"IntranetDeveloper\"],\r\n    \"Inherit\": \"Admin\",\r\n    \"AllowedAccess\": true\r\n  }\r\n]\r\n```\r\nIn the sample above: \r\n - `All` & `Administrator` will get the role `User`\r\n - `Administrator` will get the role `User`\r\n - `IntranetDeveloper` & `Administrator` will get the role `StoresAdmin`\r\n\r\n-----\r\n\r\n## Extensions\r\n\r\nThe Infrastructure plugin has an extendable feature, by default `whs-infrastructure` will use the `core` component this contains all the components we feel are required / commonly used in all our applications.\r\n\r\nYou then have the options of adding extra components to your application.\r\n\r\nBy using this method instead of just adding everything to the core plugin it means we can only include (and build) components that we require which will lower the size of our final bundled application.\r\n\r\nAlso when implementing extensions you can either attach them in your `app.js` so the extension is available app wide, or you can attach them to a specific area so only that area can use a specific extension.\r\n\r\n------------\r\n\r\n## ESLint Config\r\nOur custom ESLint config is simple to implement when using infrastructure and will allow for easily following our coding standards.\r\n\r\n### Installation\r\nOpen your `.eslintrc` file and replace content with the following:\r\n\r\n```json\r\n{\r\n  \"extends\": \"./node_modules/@whsmith/infrastructure/lint.js\"\r\n}\r\n```\r\n\r\nAnd your done.\r\n\r\n-------------\r\n\r\n## WHS Table\r\n\r\n### Usage\r\nImplementing the table extension is very simple by updating your current implementation of your infrastructure to the following:\r\n\r\n```js\r\nimport { infrastructure, whsTable } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsTable]);\r\n```\r\n\r\nOnce loaded you have access to all the directives avaliable for WHS Table.\r\n\r\n#### Basic Usage\r\n\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nrowCollection = [\r\n  {name: 'Test', number: 1},\r\n  {name: 'Test 2', number: 2},\r\n];\r\n```\r\n\r\nIt is recommended however to use `safe-src` to load your data and then loop from a separate generated array:\r\n\r\n```html\r\n<table whs-table=\"gridData\" safe-src=\"rowCollection\">\r\n  <tr ng-repeat=\"row in gridData\"></tr>\r\n</table>\r\n```\r\n\r\n#### Removing Row\r\n```js\r\nfunction removeRow(row) {\r\n  var index = rowCollection.indexOf(row);\r\n  if (index !== -1) {\r\n    rowCollection.splice(index, 1);\r\n  }\r\n}\r\n```\r\n\r\n#### Sorting Data\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\n```js\r\nfunction searchNameFunc(value) {\r\n  // Value will equal the row object\r\n  // this will sort by the length of the name string\r\n  return value.name.length;\r\n}\r\n```\r\n\r\n#### Searching / Filtering Data\r\n```html\r\n<table whs-table=\"rowCollection\" ng-enter=\"\">\r\n  <tr>\r\n    <th whs-table-sort=\"searchNameFunc\">Name</th>\r\n    <th whs-table-sort=\"number\">Number</th>\r\n  </tr>\r\n  <tr>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"search for number\" whs-table-search=\"number\" /></th>\r\n    <th><input class=\"input-sm form-control\" type=\"search\" placeholder=\"global search\" whs-table-search /></th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n```\r\nAs shown above you can add a search for both single column searching and a global table search.\r\n\r\n#### Selecting Rows\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\" whs-table-select-row=\"row\" select-mode=\"single/multiple\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n</table>\r\n````\r\nWhen a row is selected the row object with have a `isSelected` property set to true.\r\n\r\n#### Pagination\r\n```html\r\n<table whs-table=\"rowCollection\">\r\n  <tr>\r\n    <th>Name</th>\r\n    <th>Number</th>\r\n  </tr>\r\n  <tr ng-repeat=\"row in rowCollection\">\r\n    <td>{{ row.number }}</td>\r\n    <td>{{ row.name }}</td>\r\n  </tr>\r\n  <tfoot>\r\n    <tr>\r\n      <td colspan=\"2\">\r\n        <div whs-pagination items-per-page=\"10\" displayed-pages=\"5\" items-options=\"[5,10,20]\" current-page=\"1\" page-change=\"changeTriggerFunc()\"></div>\r\n      </td>\r\n    </tr>\r\n  </tfoot>\r\n</table>\r\n```\r\n\r\n#### Sticky Header\r\nYou can now fix your table headers allowing you to scroll large tables while keeping the header at the top of the screen.\r\n\r\n```html\r\n<div class=\"table-box\" whs-table=\"rowCollection\">\r\n  <div class=\"table-responsive\">\r\n    <table sticky-header=\"true/false\">\r\n      <thead>\r\n        <tr>\r\n          <th>Name</th>\r\n          <th>Number</th>\r\n        </tr>\r\n      </thead>\r\n      <tbody>\r\n        <tr ng-repeat=\"row in rowCollection\">\r\n          <td>{{ row.number }}</td>\r\n          <td>{{ row.name }}</td>\r\n        </tr>\r\n      </tbody>\r\n    </table>\r\n  </div>\r\n</div>\r\n```\r\n\r\n**NOTE** There are some factors to take note of when using the fixed header.\r\n\r\n2. If you plan to use a filter on the table you must move the `whs-table` directive above the table element (like in the example above).  If you don't plan to use filtering then it can be added to the table element as before.\r\n\r\n3. Your notice you have pass `true/false` into the directive this is so you can disable and enable the fixed header.  For example when you open search boxes you should disable to fixed header, then you can re-enable when you hide them again.\r\n\r\n\r\n#### xyScroll (Fixed Columns)\r\nUsing the `xy-scroll` directive you can build a fixed column table.\r\n\r\nIt does this by using a total of 4 tables (1 for left headers, 1 for left rows, 1 for right headers and 1 for right rows).\r\n\r\nIt is important to use fixed widths and heights when using fixed columns because without this your rows and headers will not match up correctly.\r\n\r\n## HTML\r\n```html\r\n<div class=\"table-box\">\r\n  <div id=\"fixedSample\" xy-scroll x=\"sampleFixedTable.x\" y=\"sampleFixedTable.y\" drag-x=\"true\" drag-y=\"false\">\r\n    <!-- LEFT COLUMN HEADER -->\r\n    <div class=\"xy-corner\">\r\n      <table class=\"table fixed\">\r\n        <colgroup>\r\n          <col class=\"col-sm\" />\r\n        </colgroup>\r\n        <tr>\r\n          <th class=\"text-center\">Item Name</th>\r\n        </tr>\r\n      </table>\r\n    </div>\r\n    <!-- FIXED LEFT COLUMN -->\r\n    <div class=\"xy-header-left\">\r\n      <table class=\"table fixed\">\r\n        <colgroup>\r\n          <col class=\"col-sm\" />\r\n        </colgroup>\r\n        <tbody>\r\n          <tr ng-repeat=\"h in $ctrl.sampleData track by $index\">\r\n            <td class=\"text-center\" ng-bind=\"h.name\"></td>\r\n          </tr>\r\n        </tbody>\r\n      </table>\r\n    </div>\r\n    <!-- TABLE HEADER -->\r\n    <div class=\"xy-header-top\">\r\n      <table class=\"table fixed\">\r\n        <colgroup>\r\n          <col ng-repeat=\"col in $ctrl.fields\" class=\"col-sm\" />\r\n        </colgroup>\r\n        <tr>\r\n          <th ng-repeat=\"col in $ctrl.fields\">\r\n            <div class=\"text-center\" ng-bind=\"col\"></div>\r\n          </th>\r\n        </tr>\r\n      </table>\r\n    </div>\r\n    <!-- TABLE CONTENT -->\r\n    <div class=\"xy-content\">\r\n      <table class=\"table fixed\">\r\n        <colgroup>\r\n          <col ng-repeat=\"col in $ctrl.fields\" class=\"col-sm\" />\r\n        </colgroup>\r\n        <tr ng-repeat=\"item in $ctrl.sampleData track by $index\">\r\n          <td ng-bind=\"item['0']\"></td>\r\n          <td ng-bind=\"item['1']\"></td>\r\n          <td ng-bind=\"item['2']\"></td>\r\n          <td ng-bind=\"item['3']\"></td>\r\n          <td ng-bind=\"item['4']\"></td>\r\n          <td><input type=\"text\" class=\"form-control\" ng-model=\"item['5']\"></td>\r\n          <td ng-bind=\"item['6']\"></td>\r\n          <td ng-bind=\"item['7']\"></td>\r\n          <td ng-bind=\"item['8']\"></td>\r\n          <td ng-bind=\"item['9']\"></td>\r\n          <td ng-bind=\"item['10']\"></td>\r\n          <td ng-bind=\"item['11']\"></td>\r\n          <td ng-bind=\"item['12']\"></td>\r\n          <td ng-bind=\"item['13']\"></td>\r\n          <td ng-bind=\"item['14']\"></td>\r\n        </tr>\r\n      </table>\r\n    </div>\r\n  </div>\r\n</div>\r\n```\r\n\r\n## JS\r\n```js\r\n// Variables\r\nvar fields = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'];\r\nvar sampleData = mockData();\r\n\r\nmockData(total) {\r\n  total = total || 20;\r\n  const data = [];\r\n  for (let i = 0; i < total; i++) {\r\n    const item = {\r\n      name: `Item ${i}`\r\n    };\r\n    for (let j = 0; j < fields.length; j++) {\r\n      item[j] = `column ${fields[j] + i}`;\r\n    }\r\n    data.push(item);\r\n  }\r\n  return data;\r\n}\r\n```\r\n\r\n## SCSS\r\n```scss\r\n#fixedSample {\r\n  .fixed {\r\n    table-layout: fixed;\r\n  }\r\n\r\n  .fixed .col-sm {\r\n    width: 100px;\r\n  }\r\n\r\n  .fixed td,\r\n  .fixed th {\r\n    width: 100px;\r\n  }\r\n\r\n  .fixed td { height: 55px; }\r\n\r\n  .fixed th {\r\n    border-bottom: 0 none;\r\n  }\r\n}\r\n```\r\n\r\n\r\n-------------\r\n\r\n## WHS Datepicker\r\n\r\n```js\r\nimport { infrastructure, whsDatepicker } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsDatepicker]);\r\n```\r\n\r\n### Using Datepicker\r\n\r\n```html\r\n<div class=\"input-group\">\r\n  <input type=\"text\"\r\n    name=\"dateFieldName\"\r\n    id=\"dateFieldId\"\r\n    class=\"form-control\"\r\n    ng-model=\"modelProperty\"\r\n    whs-datepicker\r\n    format=\"DD/MM/YYYY\"\r\n    min-date=\"00/00/0000\"\r\n    max-date=\"00/00/0000\"\r\n    disabled=\"true/false\"\r\n    icon-btn=\"#dateField_btn\"\r\n    required />\r\n  <div class=\"input-group-btn\">\r\n    <button type=\"button\" class=\"btn btn-secondary\" id=\"dateField_btn\"><i class=\"fa fa-calendar\"></i></button>\r\n  </div>\r\n</div>\r\n```\r\n\r\n### Filters\r\n\r\n#### Moment Parse\r\n```html\r\n{{ date | momentParse : 'DD/MM/YYYY' }}\r\n```\r\n\r\n#### Moment UTC\r\n```html\r\n{{ date | momentUtc }}\r\n```\r\n\r\n#### Date Differance\r\n```html\r\n{{ fromDate | dateDifference : toDate : unit : usePrecision }}\r\n```\r\n\r\n#### Date Format\r\n```html\r\n{{ date | dateFormat : 'DD/MM/YYYY' }}\r\n```\r\n\r\n----\r\n\r\n## WHS Print\r\n\r\n### Installing WHS Print\r\n\r\nTo load whs-print you can extend your current loading instance of infrastructure.\r\n\r\n```js\r\nimport { infrastructure, whsPrint } from '@whsmith/infrastructure';\r\n\r\nangular.module('WHSApp', [infrastructure, whsPrint]);\r\n```\r\n\r\n### Using WHS Print\r\n\r\n#### printSection\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children visible for printing\r\n\r\n  ```html\r\n  <div>\r\n      <div print-section>\r\n        I'll print\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>I won't</div>\r\n  </div>\r\n  ```\r\n\r\n#### printOnly\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element and its children only visible for printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-only>\r\n        I'll print, but until then nobody wants me\r\n        <p>Me, too!</p>\r\n      </div>\r\n      <div>Me, too! Except that people still want to look at me in the meantime...</div>\r\n  </div>\r\n  ```\r\n#### printHide\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing, but it is replaced by blank space\r\n  \r\n  ```html\r\n  <div print-section>\r\n      <div print-hide>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, but those bozos upstairs are still taking up space</div>\r\n  </div>\r\n  ```\r\n\r\n#### printRemove\r\n  - **Directive type:** Attribute\r\n  - **Description**: Makes element invisible during printing\r\n\r\n  ```html\r\n  <div print-section>\r\n      <div print-remove>\r\n        I won't print\r\n        <p>Me, either</p>\r\n      </div>\r\n      <div>I'll print, and those bozos upstairs will finally stop making such a ruckus</div>\r\n  </div>\r\n  ```\r\n\r\n#### printIf\r\n  - **Directive type:** Attribute\r\n  - **Description**: Toggles print-visibility based on expression\r\n\r\n  ```html\r\n  <!--Pigs do not yet fly, so this div, despite having print-section, will not print-->\r\n  <div print-section print-if=\"pigsFly\"></div>\r\n  <!--ET IS the best, so this div will print, despite not having print-section-->\r\n  <div print-if=\"ETIsTheBest\"></div>\r\n  ```\r\n\r\n#### printBtn\r\n  - **Directive type:** Attribute\r\n  - **Description**: Adds onClick callback to element that will trigger printing\r\n\r\n  ```html\r\n  <button print-btn>Doesn't matter where you put me</button>\r\n  <span print-btn>I will make anything cause a print</span>\r\n  <p print-btn>to happen if you click me</p>\r\n  ```\r\n  \r\n#### printLandscape\r\n  - **Directive type:** Attribute\r\n  - **Description**: Will cause printing to be landscape instead of portrait\r\n\r\n  ```html\r\n  <button print-landscape>Doesn't matter where you put me</button>\r\n  <span print-landscape>I will cause any print</span>\r\n  <p print-landscape>to be landscape</p>\r\n  ```\r\n\r\n#### printAvoidBreak\r\n  - **Directive type:** Attribute\r\n  - **Description**: Prevents page breaks on element\r\n\r\n  ```html\r\n  <button print-avoid-break>This element won't get split by page breaks</button>\r\n  ```\r\n\r\n#### printBreakBefore\r\n  - **Directive type:** Attribute\r\n  - **Description**: Causes a page break before rendering the current element\r\n  \r\n  ```html\r\n  <div print-break-before>Print page will break before this element</div>\r\n  ```\r\n\r\n#### pageBreak\r\n  - **Directive type:** Element\r\n  - **Description**: Element to create a fixed point of a page break, dosn't render anything.\r\n\r\n  ```html\r\n  <div>Page 1</div>\r\n  <page-break></page-break>\r\n  <div>Page 2</div>\r\n  ```\r\n\r\n#### printTable\r\n  - **Directive type:** Attribute\r\n  - **Description**:\r\n    - Optimizes table for printing. This includes keeping 'td' cell content from being cut-off by page breaks.\r\n    - Must be passed an array scope object representing the data presented by the table.\r\n    - Column headers will persist between pages only if the ```<thead>``` and ```<tbody>``` tags are used correctly.\r\n\r\n  This example shows adjustments to an already-visible table in order to tailor it for printing\r\n\r\n  ```html\r\n  <table print-table=\"people\">\r\n    <thead>\r\n      <tr>\r\n        <td print-remove>Unwanted field</td>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td print-remove>{{person.unwantedInfo}}</td>\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n  This example shows a table made to only be visible during printing\r\n\r\n  ```html\r\n  <table print-table=\"people\" print-only>\r\n    <thead>\r\n      <tr>\r\n        <td>Name</td>\r\n        <td>Address</td>\r\n        <td>Phone</td>\r\n      </tr>\r\n    </thead>\r\n    <tbody>\r\n      <tr ng-repeat=\"person in people\">\r\n        <td>{{person.name}}</td>\r\n        <td>{{person.address}}</td>\r\n        <td>{{person.phone}}</td>\r\n      </tr>\r\n    </tbody>\r\n  </table>      \r\n  ```\r\n\r\n### PDFPrinter Service\r\nThe PDFPrinter service is a great tool for building and creating pdfs either to open, print or download.\r\n\r\nYou can either provide the document definitions yourself or load the default and use inbuild functions to build your pdf.\r\n\r\n#### Implementation\r\n\r\n````js\r\nclass SomeClass {\r\n  constructor(PDFPrinter) {\r\n    this.myPDF = PDFPrinter.create(); // Default\r\n\r\n    this.customPDF = PDFPrinter.create(documentDef); // Custom\r\n  }\r\n}\r\n````\r\n`documentDef` is a object using pdfmake, more information about properties you can use are on their website: http://pdfmake.org/playground.html\r\n\r\n\r\n#### Service Methods\r\n\r\nFrom your created PDF instance you will have access to the following methods which will effect the instance you are running from.\r\n\r\n##### print\r\n```js\r\nthis.myPDF.print(); // This will print the current PDF.\r\n```\r\n\r\n##### open\r\n```js\r\nthis.myPDF.open(); // This will open the current PDF.\r\n```\r\n\r\n##### download\r\n```js\r\nthis.myPDF.download(); // This will trigger a download for the current PDF.\r\n```\r\n\r\n##### AddElement\r\n  - **Parameters:** \r\n    - `element` - String or Object to add to the documentDef content.\r\n```js\r\nthis.myPdf.AddElement({\r\n  text: 'sub header text element with page break',\r\n  pageBreak: 'before',\r\n  style: 'subheader'\r\n});\r\n```\r\n\r\n##### AddPageHeader\r\n  - **Parameters:** \r\n    - `text` - Header text\r\n```js\r\nthis.myPdf.AddPageHeader('My PDF');\r\n```\r\n\r\n##### AddFooterPageCount\r\nAdd a page count at the footer of the PDF pages.\r\n```js\r\nthis.myPdf.AddFooterPageCount();\r\n```\r\n\r\n##### AddStyle\r\nAdds a style object to the pdf document.\r\n```js\r\nthis.myPdf.AddStyle('myStyle', {\r\n  bold: true;\r\n  alignment: 'center'\r\n});\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n\r\n```js\r\nthis.myPDF.AddHeader('A Header'); // Add a header to pdf content.\r\n```\r\n\r\n##### AddSubHeader\r\n  - **Parameters:** \r\n    - `text` - Text to display as a sub header\r\n\r\n```js\r\nthis.myPDF.AddSubHeader('A Sub Header'); // Add a sub header to pdf content.\r\n```\r\n\r\n##### AddText\r\n  - **Parameters:** \r\n    - `text` - Text to display\r\n\r\n```js\r\nthis.myPDF.AddText('I want to write some text on my pdf.'); // Add text to pdf content.\r\n```\r\n\r\n##### AddList\r\n  - **Parameters:** \r\n    - `text` - Text to display as a header\r\n    - `bold` - Should the list items be bold\r\n```js\r\nthis.myPDF.AddList(['item 1', 'item 2', 'item 3'], false);\r\n```\r\n\r\n##### AddSimpleTable\r\n   - **Parameters:** \r\n      - `tableBody` - Body array for the table object.\r\n\r\n```js\r\nvar data = [\r\n  ['Header1', 'Header2', 'Header3'], // First colum is the header\r\n  ['row1col1', 'row1col2', 'row2col3']\r\n];\r\nthis.myPDF.AddSimpleTable(data); // Creates a simple table based on the data provided\r\n```\r\n\r\n##### AddRawTable\r\n  - **Parameters:** \r\n    - `tableObj` - Table object.\r\n\r\n```js\r\nvar table = {\r\n  widths: ['*', 200, 50],\r\n  body: [\r\n    [\r\n      { text: 'Auto Width Column', style: 'tableHeader', alignment: 'left' },\r\n      { text: 'Smaller Column', style: 'tableHeader' },\r\n      { text: 'Tiny Column', style: 'tableHeader' }\r\n    ],\r\n    ['row1col1', 'row1col2', 'row2col3']\r\n  ]\r\n};\r\nthis.myPDF.AddRawTable(table); // Creates a table from the raw table object\r\n```\r\n\r\n##### CreateTableObj\r\nBy creating a table object you will gain a bunch of extensions methods to help build your table obj.  All methods can be chained and then ending with `Build()` to return the finished table obj use `getTable()`.\r\n\r\n  - **Parameters:**\r\n    - `tableObj` - Core table object, default will be provided if undefined\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj({\r\n  headerRows: 1,\r\n  style: 'table'\r\n});\r\n\r\n// Add methods here to build your table (see below)\r\ntableBuilder\r\n  .SetWidths([100, 200])\r\n  .AddHeader(['Header 1', 'Header 2'])\r\n  .AddRow(['Row1 Col1', 'Row1 Col2'])\r\n  .Build();\r\n\r\n// Add table to PDF\r\nthis.myPDF.AddRawTable(tableBuilder.getTable());\r\n```\r\n\r\nMethods available within the tableBuilder are as follows:\r\n\r\n##### SetWidths\r\n  - **Parameters:**\r\n    - `widths` - array of widths for each column, pass `'*'` for auto width.\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.SetWidths([100, 200, '*']);\r\n```\r\n\r\n##### AddHeader\r\n  - **Parameters:**\r\n    - `header` - header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeader({ text: 'Header 1', style: 'tableHeader' });\r\n```\r\n\r\n##### AddHeaders\r\n  - **Parameters:**\r\n    - `headers` - array of header objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddHeaders([\r\n  'Header 1',\r\n  { text: 'Header 2', alignment: 'center' }\r\n]);\r\n```\r\n\r\n##### AddRow\r\n  - **Parameters:**\r\n    - `row` - array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRow([\r\n  { text: 'Col1 Text', style: 'someStyle' },\r\n  { text: 'Col2 Text', bold: true },\r\n]);\r\n```\r\n\r\n##### AddRowSmart\r\nAdding a smart row will allow you to pass the raw object and then define how each column should be handled.\r\n  - **Parameters:**\r\n    - `data` - data objects (model)\r\n    - `columnOptions` - array of column options\r\n      - Object used the core pdfmake properties and also the following extension properties:\r\n        - `property` - property name from data object to use for column\r\n        - `run` - function to run property value through and display function response for column.\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRowSmart(obj, [\r\n  { property: 'id', bold: true }, // Column 1, load obj.id\r\n  { property: 'name' }, // Column 2, load obj.name\r\n  { text: 'NEW', style: 'statusGreen' } // Column 3 forced text value\r\n]);\r\n```\r\n\r\n##### AddRows\r\n  - **Parameters:**\r\n    - `rows` - array of array of row objects / string\r\n\r\n```js\r\nconst tableBuilder = this.myPDF.CreateTableObj();\r\ntableBuilder.AddRows([\r\n  ['Row1 Col1', 'Row1 Col2'],\r\n  [{ text: 'Row2 Col1', bold: true }, 'Row2 Col2' ]\r\n])\r\n```\r\n\r\n##### AddRowsSmart\r\nLike the `AddRowSmart` however it will allow you to pass an entire array of data with and single set of column options and it will add all the rows.\r\n  - **Parameters:**\r\n    - `data` - Array of objects\r\n    - `columnOptions` - array of column options\r\n\r\n##### AddImage\r\n  - **Parameters:** \r\n    - `imageUrl` - Url / Location of the image you want to display\r\n    - `width` - [*Optional*] Width to scale the image to.\r\n\r\n```js\r\nthis.myPDF.AddImage('myimage.jpg', 200);\r\n```\r\n","readmeFilename":"README.md"}