{"_id":"data-layer-helper","_rev":"3-7bfe619310e945c51c7e9a8140c2c41c","name":"data-layer-helper","description":"This library provides the ability to process messages passed onto a dataLayer queue.","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"data-layer-helper","version":"0.1.0","devDependencies":{"grunt":"~0.4.1","grunt-contrib-jshint":"~0.6.0","grunt-contrib-nodeunit":"~0.2.0","grunt-contrib-uglify":"~0.2.2","grunt-contrib-qunit":"~0.2.2","grunt-contrib-watch":"~0.4.4","grunt-contrib-concat":"~0.3.0","grunt-closure-tools":"~0.8.3","grunt-closure-linter":"~0.1.0"},"contributors":[{"name":"* Brian Kuhn","email":"bnkuhn@gmail.com"}],"gitHead":"7d047879c1d45b33c437259b3eb6ec976f7770d9","description":"This library provides the ability to process messages passed onto a dataLayer queue.","_id":"data-layer-helper@0.1.0","scripts":{},"_shasum":"d7a4a796eb3d6ed26ce27165bf697aaee9b4ddea","_from":".","_npmVersion":"2.12.1","_nodeVersion":"0.12.7","_npmUser":{"name":"gotan","email":"olgaletter@yahoo.com"},"dist":{"shasum":"d7a4a796eb3d6ed26ce27165bf697aaee9b4ddea","tarball":"https://registry.npmjs.org/data-layer-helper/-/data-layer-helper-0.1.0.tgz","integrity":"sha512-1e5VML7HB2KCIL3o9z7Duq3Y86x8cMRFOMupoeRH0vQOesKKjiKWUpB3GzvX6SkuuuHpRvSoQBlSiFtrHegcDA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGBmDjsGlVbngJF3a2rIGtyV0gLCbif19TVibLBLTaSTAiEA++y8/s9eK95iYu85RXHMCMOCDRtXU+b8Uqg8WCe7kQ8="}]},"maintainers":[{"name":"gotan","email":"olgaletter@yahoo.com"}]}},"readme":"# Data Layer Helper Library\nThis library provides the ability to process messages passed onto a dataLayer queue.\n\n- [Background](#what-is-a-datalayer-queue)\n- [Why Do We Need a Library?](#why-do-we-need-a-library)\n- [The Abstract Data Model](#the-abstract-data-model)\n    - [Overwriting Existing Values](#overwriting-existing-values)\n    - [Recursively Merging Values](#recursively-merging-values)\n    - [Meta Commands](#meta-commands)\n    - [Native Methods](#native-methods)\n    - [Custom Methods](#custom-methods)\n- [Listening for Messages](#listening-for-messages)\n    - [Processing the Past](#processing-the-past)\n- [Summary](#summary)\n- [Build and Test](#build-and-test)\n- [License](#license)\n  \n\n## What is a dataLayer queue?\nA dataLayer queue is simply a JavaScript array that lives on a webpage. \n\n```html\n<script>\n  dataLayer = [];\n</script>\n```\n\nPage authors can append messages onto the queue in order to emit information about the page and \nits state. \n\n```html\n<script>\n  dataLayer.push({\n    title: \"Migratory patterns of ducks\",\n    category: \"Science\",\n    author: \"Bradley Wogulis\"\n  });\n</script>\n```\n\nThese messages are simply JavaScript objects containing a hierarchy of key/value pairs. They can \nbe metadata about the page content, information about the visitor, or data about events happening \non the page. This system allows tools like analytics libraries and tag management systems to access \nthis data in a standard way, so page authors can avoid using a bunch of proprietary, repetitive APIs.\n\n* It provides a common, well defined system for exposing page data.\n* It doesn't slow down page rendering.\n* It doesn't pollute the global JavaScript namespace.\n* It doesn't require page authors to learn a different, one-off API for every new tool.\n* It doesn't require page authors to expose the same data multiple times.\n* It allows page authors to add, remove or change vendors easily.\n\n## Why do we need a library?\nThe dataLayer system make things very easy for page authors. The syntax is simple, and there's no\nextra code to load. But this system _does_ make life a little more difficult for vendors and tools\nthat want to consume the data. That's where this library comes in. \n\nThis project provides the ability to listen for dataLayer messages and to read the key/value pairs \nthat have been set by all the previous messages. It can be used by the tools/vendors mentioned above, \nor by page authors that need to read back the data they've emitted.\n\nTo use this library, you'll need to get it onto the page. You can do this by hosting a copy and \nsourcing it from the page, or by compiling it into your own JavaScript library. Once it's on the \npage, you can create a new helper object like this:\n\n```js\nvar helper = new DataLayerHelper(dataLayer);\n```\n\nThis helper object will listen for new messages on the given dataLayer.  Each new message will be \nmerged into the helper's **\"abstract data model\"**.  This internal model object holds the most recent\nvalue for all keys which have been set on messages processed by the helper. \n\nYou can retrieve values from the data model by using the helper's get() method:\n\n```js\nhelper.get('category');   // Returns \"Science\".\n```\n\nAs mentioned above, messages passed onto the dataLayer can be hierarchical. For example, a page \nauthor might push the following message, which has data multiple levels deep:\n\n```js\ndataLayer.push({\n  one: {\n    two: {\n      three: 4\n    }\n  }\n});\n```\n\nUsing the helper, you can retrieve the nested value using dot-notation:\n\n```js\nhelper.get('one.two.three');     // Returns 4.\nhelper.get('one.two');           // Returns {three: 4}.\n```\n## The Abstract Data Model\nAs mentioned above, the abstract data model is an internal representation, which hold\nthe most recent value for all keys that have been set by a dataLayer message. This \nmeans that as each message is pushed onto the dataLayer, the abstract data model must \nbe updated. The helper library does this using a well-defined process. \n\nAs each message is processed, its key/value pairs will be added to the abstract data \nmodel. If the key doesn't currently exist in the model, this operation is simple. \nThe pair is simply added to the model object. But in the case of key conflicts, we have\nto specify how values will be overwritten and/or merged.\n\nThere are two possible actions to take when merging a key/value pair onto the abstract \nmodel; overwriting the existing value or recursively merging the new value onto the \nexisting value. The action taken will depend on the type of the two values. For this, \nwe define three types of values:\n\n* JavaScript Arrays\n* \"Plain\" Objects\n* Everything else\n\nHopefully, JavaScript Arrays are self-explanatory. \"Plain\" Objects are JavaScript \nobjects that were created via Object literal notation (e.g. {one: 2}) or via \"new Object\".\nNulls, Dates, RegExps, Windows, DOM Elements, etc. are not \"Plain\". Those fall into the \ncategory of \"everything else\", along with strings, numbers, booleans, undefined, etc.  \n\nOnce the type of the new and existing values has been categorized this way, we can use the \nfollowing table to describe what action will happen for that key/value pair:\n\nExisting Value | New Value    | Merging Action\n---------------|--------------|--------------------------\nArray          | Array        | Recursively merge\nArray          | Plain Object | Overwrite existing value\nArray          | Other        | Overwrite existing value\nPlain Object   | Array        | Overwrite existing value\nPlain Object   | Plain Object | Recursively merge\nPlain Object   | Other        | Overwrite existing value\nOther          | Array        | Overwrite existing value\nOther          | Plain Object | Overwrite existing value\nOther          | Other        | Overwrite existing value\n\n### Overwriting Existing Values\nWhen the merging action is \"Overwrite exsting value\", the result of the operation is very\nsimple. The existing value will be completely discarded and the new value will take its \nplace in the abstract data model. The following table provides some examples:\n\n\nExisting Value   | New Value        | Result of Overwrite\n-----------------|------------------|---------------------\n[1, 2, 3]        | 'hello'          | 'hello'\n{ducks: 'quack'} | [1, 2, 3]        | [1, 2, 3]\n{ducks: 'quack'} | 'hello'          | 'hello'\n'hello'          | [1, 2, 3]        | [1, 2, 3]\n'hello'          | {ducks: 'quack'} | {ducks: 'quack'}\n'hello'          | 42               | 42\n\n### Recursively Merging Values\nWhen the merging action is \"Recursively Merge\", the result of the operation will be the \nresult of iterating through each property in the new value, and for each property, deciding \nhow to copy that sub-key/value into the abstract data model by looking at the type of that \nsub-key in the existing value. If the key does not exist on the existing value, the new \nvalue is simply assigned to the abstract model. The following examples demonstrate this:\n\nExisting Value     | New Value                     | Result of Overwrite\n-------------------|-------------------------------|----------------------------\n{one: 1, three: 3} | {two: 2}                      | {one: 1, three: 3, two: 2}\n{one: 1, three: 3} | {three: 4}                    | {one: 1, three: 4}\n{one: {two: 3}}    | {one: {four: 5}}              | {one: {two: 3, four: 5}}\n{one: {two: 3}}    | {two: 4}                      | {one: {two: 3}, two: 4}\n[]                 | ['hello']                     | ['hello']\n[1]                | [undefined, 2]                | [1, 2]\n[1, {two: 3}]      | [undefined, {two: 4, six: 8}] | [1, {two: 4, six: 8}] \n\n### Meta Commands\nUsing the above methods alone, some operations on the abstract model are somewhat cumbersome.\nFor example, appending items onto the end of an existing array requires you to know the \nlength of the existing array and then requires you to clumsily build an array that can be \nmerged onto the existing value.  To make these cases easier, we provide a set of alternative \nsyntaxes for updating values that are already in the abstract data model. \n\nThe first of these syntaxes allows you to call any method supported on the existing type. \nFor example, if the existing value in the abstract model is an Array, you'd have a wide \nvariety of APIs that can be called (e.g. push, pop, concat, shift, unshift, etc.).  To invoke\nthis syntax, you would push a \"command array\" onto the dataLayer instead of a normal message \nobject.  \n\n```js\ndataLayer.push(['abc.push', 4, 5, 6]);\n```\n\nA command array is a normal JavaScript array, where the first element is a string. The string \ncontains the key of the value to update, followed by a dot (.), followed by the name of the \nmethod to invoke on the value.  In the above example, the key to update is 'abc', and the \nmethod to invoke is the 'push' method.  The string may be followed by zero or more arguments, \nwhich will be passed to the invoked method.  \n\nIf the given method name does not exist on the existing value, or if the invocation throws an \nexception, the assignment will be ignored, and a warning message will be logged to the browser's \ndeveloper console (if available).\n\n### Native Methods\nBrowsers come with dozens, if not hundreds, of useful APIs. Any method supported by the \nexisting value can be called using the command array syntax. Here are some additional examples:\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>abc</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>[1, 2, 3]</td>\n  </tr>\n  <tr>\n    <td><b>Command Array:</b></td>\n    <td>dataLayer.push(['abc.push', 4, 5, 6])</td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>[1, 2, 3, 4, 5, 6]</td>\n  </tr>\n</table>\n\nIn the following example, no arguments are provided:\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>abc</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>[1, 2, 3]</td>\n  </tr>\n  <tr>\n    <td><b>Command Array:</b></td>\n    <td>dataLayer.push(['abc.pop'])</td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>[1, 2]</td>\n  </tr>\n</table>\n\n\nIn the following example, the value to update (bbb) is nested inside a top level object (aaa):\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>aaa.bbb</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>[1, 2, 3]</td>\n  </tr>\n  <tr>\n    <td><b>Command Array:</b></td>\n    <td>dataLayer.push(['aaa.bbb.push', 4])</td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>[1, 2, 3, 4]</td>\n  </tr>\n</table>\n\nAnd the following example demonstrates an operation on a Date object.  Remember that all types \nare supported, not just Arrays:\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>time</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>Fri Dec 20 2013 15:23:22 GMT-0800 (PST)</td>\n  </tr>\n  <tr>\n    <td><b>Command Array:</b></td>\n    <td>dataLayer.push(['time.setYear', 2014])</td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>Fri Dec 20 2014 15:23:22 GMT-0800 (PST)</td>\n  </tr>\n</table>\n\nNotice that because command arrays are processed asynchronously, nothing can be done with the \nreturn values from these method invocations. This brings us to our second syntax for updating \nvalues in the abstract data model.\n\n### Custom Methods\nSo far, we've seen that objects (messages) can be pushed onto the dataLayer, as well as arrays \n(command arrays). Pushing a function onto the dataLayer will also allow you to update the abstract \ndata model, but with custom code. This technique has the added benefit of being able to handle \nreturn values of any native method calls made from within the function.\n\nWhen a function is processed, it will be executed in the context of the abstract data model. The \nvalue of \"this\" will be an interface that represents the current abstract data model. This \ninterfact will provide two APIs: get(key) and set(key, value). The following examples demonstrate \nhow these APIs can be used to update values in the abstract data model.\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>time</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>Fri Dec 20 2013 15:23:22 GMT-0800 (PST)</td>\n  </tr>\n  <tr>\n    <td><b>Custom function:</b></td>\n    <td><pre>dataLayer.push(function() {\n  this.get('time').setMonth(0); \n})</pre></td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>Fri Jan 20 2013 15:23:22 GMT-0800 (PST)</td>\n  </tr>\n</table>\n\nThe following example demonstrates updating a nested value:\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>aaa.bbb.ccc</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>[1, 2, 3]</td>\n  </tr>\n  <tr>\n    <td><b>Custom function:</b></td>\n    <td><pre>dataLayer.push(function() {\n  var ccc = this.get('aaa.bbb.ccc');\n  ccc.push(ccc.pop() * 2);\n})</pre></td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>[1, 2, 6]</td>\n  </tr>\n</table>\n\n\nThe following example demonstrates overwriting a value:\n\n<table>\n  <tr>\n    <td><b>Existing Key:</b></td>\n    <td>abc</td>\n  </tr>\n  <tr>\n    <td><b>Existing Value:</b></td>\n    <td>[1, 2, 3]</td>\n  </tr>\n  <tr>\n    <td><b>Custom function:</b></td>\n    <td><pre>dataLayer.push(function() {\n  this.set('abc', {xyz: this.get('abc')});\n})</pre></td>\n  </tr>\n  <tr>\n    <td><b>Result:</b></td>\n    <td>{xyz: [1, 2, 3]}</td>\n  </tr>\n</table>\n\n## Listening for Messages\nWhen creating a DataLayerHelper object, you can also specify a callback function to be called \nwhenever a message is pushed onto the given dataLayer. This allows your code to be notified\nimmediately whenever the dataLayer has been updated, which is a key advantage of the message\nqueue approach.\n\n```js\nfunction listener(message, model) {\n  // Message has been pushed. \n  // The helper has merged it onto the model.\n  // Now use the message and the updated model to do something.\n}\nvar helper = new DataLayerHelper(dataLayer, listener);\n```\n\n### Processing the Past\nTools that are loaded onto the page asynchronously or lazily will appreciate that you can also\nopt to process message that were pushed onto the dataLayer in the past. This can be done by \npassing true as the third parameter in the DataLayerHelper constructor.\n\n```js\nfunction listener(message, model) {\n  // Message has been pushed. \n  // The helper has merged it onto the model.\n  // Now use the message and the updated model to do something.\n}\nvar helper = new DataLayerHelper(dataLayer, listener, true);\n```\n\nUsing this option means that your listener callback will be called once for every message that\nhas ever been pushed onto the given dataLayer. And on each call to the callback, the model\nwill represent the abstract model at the time of the message.\n\n## Summary\nWe've seen above that the dataLayer provides a simple API for page authors. They simply define\nan array called dataLayer, then push messages onto it. \n\nThere are three types of messages:\n* Standard Messages (Objects)\n* Native Method Calls (Command Arrays)\n* Custom Method Calls (Functions)\n\nThis helper library provides tools and vendors a way to consume these messages. It automatically\nlistens for new messages and merges them onto its abstract data model. You can query the model\nusing the get() API, or you can get message notifications with a callback function.\n\nAt this point, we highly recommend that you read the code and browse the tests for examples of\nhow the library works and how it can be used.\n\n## Build and Test\n\nA few prerequisites:\n\n1. [Install Node.js and npm](http://nodejs.org/download/)\n2. [Install Git](https://help.github.com/articles/set-up-git)\n\nClone a copy of the project repo by running:\n\n```bash\ngit clone --recursive git://github.com/google/data-layer-helper.git\n```\n\nInstall the [grunt-cli](http://gruntjs.com/getting-started#installing-the-cli) package if you haven't before. This should be done as global install:\n\n```bash\nnpm install -g grunt-cli\n```\n\nEnter the data-layer-helper directory and install the Node dependencies, this time *without* specifying a global install:\n\n```bash\ncd data-layer-helper\nnpm install\n```\n\nEnter the third_party/closure-linter directory and install the Closure linter:\n\n```bash\ncd third_party/closure-linter\npython setup.py install\n```\n\nMake sure you have `grunt` installed. From the root directory of the project, run:\n\n```bash\ngrunt -version\n```\n\nThat should be everything.  You can try running the build, which will run the linter, compile/minify the JavaScript and run the tests.\n\n```bash\ngrunt\n```\n\nThe built version (data-layer-helper.js) will be in the `dist/` subdirectory.\n\n\n## License\n\n   Copyright 2012 Google Inc. All Rights Reserved.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n","maintainers":[{"name":"gotan","email":"olgaletter@yahoo.com"}],"time":{"modified":"2022-06-14T19:02:39.738Z","created":"2015-08-11T18:22:32.254Z","0.1.0":"2015-08-11T18:22:32.254Z"},"contributors":[{"name":"* Brian Kuhn","email":"bnkuhn@gmail.com"}],"readmeFilename":"README.md"}