{"action":"created","comment":{"url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/comments/48603134","id":48603134,"diff_hunk":"@@ -0,0 +1,209 @@\n+import EventEmitter from '../../utils/event-emitter';\n+import { isIE } from '../../utils/browser';\n+import * as destLocation from '../../utils/destination-location';\n+\n+const STORAGES_SANDBOX_TEMP = 'hammerhead|storeges-sandbox-temp';\n+const API_KEY_PREFIX        = 'hammerhead|api-key-prefix|';\n+const KEY                   = 0;\n+const VALUE                 = 1;\n+\n+export default class StorageWrapper extends EventEmitter {\n+    constructor (window, nativeStorage, nativeStorageKey) {\n+        super();\n+\n+        this.nativeStorage     = nativeStorage;\n+        this.nativeStorageKey  = nativeStorageKey;\n+        this.lastState         = null;\n+        this.window            = window;\n+        this.initialProperties = [];\n+        this.wrapperMethods    = [];\n+\n+        this.STORAGE_CHANGED_EVENT = 'hammerhead|event|storage-changed';\n+        this.EMPTY_OLD_VALUE_ARG   = isIE ? '' : null;\n+\n+        Object.defineProperty(this, 'length', {\n+            get: () => this._getAddedProperties().length,\n+            set: () => void 0\n+        });\n+\n+        // NOTE: Save wrapper properties and methods to be able to distinguish them from\n+        // properties that will be created from the outside.\n+        this.initialProperties = this._getInitialProperties();\n+        this.wrapperMethods    = StorageWrapper._getWrapperMethods();\n+\n+        this._init();\n+    }\n+\n+    static _getWrapperMethods () {\n+        var methods = [];\n+\n+        for (var key in StorageWrapper.prototype)\n+            methods.push(key);\n+\n+        return methods;\n+    }\n+\n+    _init () {\n+        this._loadFromNativeStorage();\n+        this.lastState = this._getCurrentState();\n+\n+        window.setInterval(() => this._checkStorageChanged(), 10);\n+    }\n+\n+    _getInitialProperties () {\n+        var properties = ['length'];\n+\n+        for (var property in this) {\n+            if (this.hasOwnProperty(property))\n+                properties.push(property);\n+        }\n+\n+        return properties;\n+    }\n+\n+    _checkStorageChanged () {\n+        var currentState = this._getCurrentState();\n+\n+        for (var i = 0; i < this.lastState[KEY].length; i++) {\n+            var lastStateKey   = this.lastState[KEY][i];\n+            var lastStateValue = this.lastState[VALUE][i];\n+\n+            var keyIndex = currentState[KEY].indexOf(lastStateKey);\n+\n+            if (keyIndex !== -1) {\n+                if (currentState[VALUE][keyIndex] !== lastStateValue)\n+                    this._raiseStorageChanged(currentState[KEY][keyIndex], lastStateValue, currentState[VALUE][keyIndex]);\n+\n+                currentState[KEY].splice(keyIndex, 1);\n+                currentState[VALUE].splice(keyIndex, 1);\n+            }\n+            else\n+                this._raiseStorageChanged(lastStateKey, lastStateValue, null);\n+        }\n+\n+        for (var j = 0; j < currentState[KEY].length; j++)\n+            this._raiseStorageChanged(currentState[KEY][j], this.EMPTY_OLD_VALUE_ARG, currentState[VALUE][j]);\n+\n+        this.lastState = this._getCurrentState();\n+    }\n+\n+    _getCurrentState () {\n+        var addedProperties = this._getAddedProperties();\n+        var state           = [[], []];\n+\n+        for (var i = 0; i < addedProperties.length; i++) {\n+            state[KEY].push(addedProperties[i]);\n+            state[VALUE].push(this[addedProperties[i]]);\n+        }\n+\n+        return state;\n+    }\n+\n+    _getAddedProperties () {\n+        var properties = [];\n+\n+        for (var property in this) {\n+            if (this.hasOwnProperty(property) && this.initialProperties.indexOf(property) === -1)\n+                properties.push(property);\n+        }\n+\n+        return properties;\n+    }\n+\n+    _castToString (value) {\n+        // NOTE: The browser automatically translates the key and the value to a string. To repeat this behavior,\n+        // we use native storage:\n+        // localStorage.setItem(null, null) equivalently to localStorage.setItem('null', 'null')\n+        this.nativeStorage[STORAGES_SANDBOX_TEMP] = value;","path":"src/client/sandbox/storages/wrapper.js","position":117,"original_position":117,"commit_id":"a2f79b51e6b16645d2a5d2eeecf68493fe8740c4","original_commit_id":"a2f79b51e6b16645d2a5d2eeecf68493fe8740c4","user":{"login":"miherlosev","id":4133518,"avatar_url":"https://avatars.githubusercontent.com/u/4133518?v=3","gravatar_id":"","url":"https://api.github.com/users/miherlosev","html_url":"https://github.com/miherlosev","followers_url":"https://api.github.com/users/miherlosev/followers","following_url":"https://api.github.com/users/miherlosev/following{/other_user}","gists_url":"https://api.github.com/users/miherlosev/gists{/gist_id}","starred_url":"https://api.github.com/users/miherlosev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miherlosev/subscriptions","organizations_url":"https://api.github.com/users/miherlosev/orgs","repos_url":"https://api.github.com/users/miherlosev/repos","events_url":"https://api.github.com/users/miherlosev/events{/privacy}","received_events_url":"https://api.github.com/users/miherlosev/received_events","type":"User","site_admin":false},"body":"Maybe rename to `STORAGE_VALUE_RESOLVER`?.\r\nIs is semantic analog of `DOCUMENT_URL_RESOLVER`","created_at":"2015-12-30T13:20:19Z","updated_at":"2015-12-30T13:20:19Z","html_url":"https://github.com/DevExpress/testcafe-hammerhead/pull/300#discussion_r48603134","pull_request_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300","_links":{"self":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/comments/48603134"},"html":{"href":"https://github.com/DevExpress/testcafe-hammerhead/pull/300#discussion_r48603134"},"pull_request":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300"}}},"pull_request":{"url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300","id":52003199,"html_url":"https://github.com/DevExpress/testcafe-hammerhead/pull/300","diff_url":"https://github.com/DevExpress/testcafe-hammerhead/pull/300.diff","patch_url":"https://github.com/DevExpress/testcafe-hammerhead/pull/300.patch","issue_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues/300","number":300,"state":"open","locked":false,"title":"Add localStorage/sessionStorage sandbox (close #252)","user":{"login":"churkin","id":5182202,"avatar_url":"https://avatars.githubusercontent.com/u/5182202?v=3","gravatar_id":"","url":"https://api.github.com/users/churkin","html_url":"https://github.com/churkin","followers_url":"https://api.github.com/users/churkin/followers","following_url":"https://api.github.com/users/churkin/following{/other_user}","gists_url":"https://api.github.com/users/churkin/gists{/gist_id}","starred_url":"https://api.github.com/users/churkin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/churkin/subscriptions","organizations_url":"https://api.github.com/users/churkin/orgs","repos_url":"https://api.github.com/users/churkin/repos","events_url":"https://api.github.com/users/churkin/events{/privacy}","received_events_url":"https://api.github.com/users/churkin/received_events","type":"User","site_admin":false},"body":"Guys, it is not finish pull request, i just want to make sure that this approach is suitable for all browsers.","created_at":"2015-11-27T15:03:16Z","updated_at":"2015-12-30T13:20:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a8a34f1189df919d74284d7598d14d8ce1f5f507","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300/commits","review_comments_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300/comments","review_comment_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/comments{/number}","comments_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues/300/comments","statuses_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/statuses/a2f79b51e6b16645d2a5d2eeecf68493fe8740c4","head":{"label":"churkin:storages","ref":"storages","sha":"a2f79b51e6b16645d2a5d2eeecf68493fe8740c4","user":{"login":"churkin","id":5182202,"avatar_url":"https://avatars.githubusercontent.com/u/5182202?v=3","gravatar_id":"","url":"https://api.github.com/users/churkin","html_url":"https://github.com/churkin","followers_url":"https://api.github.com/users/churkin/followers","following_url":"https://api.github.com/users/churkin/following{/other_user}","gists_url":"https://api.github.com/users/churkin/gists{/gist_id}","starred_url":"https://api.github.com/users/churkin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/churkin/subscriptions","organizations_url":"https://api.github.com/users/churkin/orgs","repos_url":"https://api.github.com/users/churkin/repos","events_url":"https://api.github.com/users/churkin/events{/privacy}","received_events_url":"https://api.github.com/users/churkin/received_events","type":"User","site_admin":false},"repo":{"id":39562496,"name":"testcafe-hammerhead","full_name":"churkin/testcafe-hammerhead","owner":{"login":"churkin","id":5182202,"avatar_url":"https://avatars.githubusercontent.com/u/5182202?v=3","gravatar_id":"","url":"https://api.github.com/users/churkin","html_url":"https://github.com/churkin","followers_url":"https://api.github.com/users/churkin/followers","following_url":"https://api.github.com/users/churkin/following{/other_user}","gists_url":"https://api.github.com/users/churkin/gists{/gist_id}","starred_url":"https://api.github.com/users/churkin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/churkin/subscriptions","organizations_url":"https://api.github.com/users/churkin/orgs","repos_url":"https://api.github.com/users/churkin/repos","events_url":"https://api.github.com/users/churkin/events{/privacy}","received_events_url":"https://api.github.com/users/churkin/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/churkin/testcafe-hammerhead","description":"","fork":true,"url":"https://api.github.com/repos/churkin/testcafe-hammerhead","forks_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/forks","keys_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/keys{/key_id}","collaborators_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/teams","hooks_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/hooks","issue_events_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/issues/events{/number}","events_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/events","assignees_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/assignees{/user}","branches_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/branches{/branch}","tags_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/tags","blobs_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/git/refs{/sha}","trees_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/git/trees{/sha}","statuses_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/statuses/{sha}","languages_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/languages","stargazers_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/stargazers","contributors_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/contributors","subscribers_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/subscribers","subscription_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/subscription","commits_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/commits{/sha}","git_commits_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/git/commits{/sha}","comments_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/comments{/number}","issue_comment_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/issues/comments{/number}","contents_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/contents/{+path}","compare_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/compare/{base}...{head}","merges_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/merges","archive_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/downloads","issues_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/issues{/number}","pulls_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/pulls{/number}","milestones_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/milestones{/number}","notifications_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/labels{/name}","releases_url":"https://api.github.com/repos/churkin/testcafe-hammerhead/releases{/id}","created_at":"2015-07-23T11:03:55Z","updated_at":"2015-07-24T11:26:01Z","pushed_at":"2015-12-30T11:54:53Z","git_url":"git://github.com/churkin/testcafe-hammerhead.git","ssh_url":"git@github.com:churkin/testcafe-hammerhead.git","clone_url":"https://github.com/churkin/testcafe-hammerhead.git","svn_url":"https://github.com/churkin/testcafe-hammerhead","homepage":null,"size":1969,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"DevExpress:master","ref":"master","sha":"70e52dcffbca4939bcfb181d4950ffeba46e35fb","user":{"login":"DevExpress","id":4243232,"avatar_url":"https://avatars.githubusercontent.com/u/4243232?v=3","gravatar_id":"","url":"https://api.github.com/users/DevExpress","html_url":"https://github.com/DevExpress","followers_url":"https://api.github.com/users/DevExpress/followers","following_url":"https://api.github.com/users/DevExpress/following{/other_user}","gists_url":"https://api.github.com/users/DevExpress/gists{/gist_id}","starred_url":"https://api.github.com/users/DevExpress/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DevExpress/subscriptions","organizations_url":"https://api.github.com/users/DevExpress/orgs","repos_url":"https://api.github.com/users/DevExpress/repos","events_url":"https://api.github.com/users/DevExpress/events{/privacy}","received_events_url":"https://api.github.com/users/DevExpress/received_events","type":"Organization","site_admin":false},"repo":{"id":36883823,"name":"testcafe-hammerhead","full_name":"DevExpress/testcafe-hammerhead","owner":{"login":"DevExpress","id":4243232,"avatar_url":"https://avatars.githubusercontent.com/u/4243232?v=3","gravatar_id":"","url":"https://api.github.com/users/DevExpress","html_url":"https://github.com/DevExpress","followers_url":"https://api.github.com/users/DevExpress/followers","following_url":"https://api.github.com/users/DevExpress/following{/other_user}","gists_url":"https://api.github.com/users/DevExpress/gists{/gist_id}","starred_url":"https://api.github.com/users/DevExpress/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DevExpress/subscriptions","organizations_url":"https://api.github.com/users/DevExpress/orgs","repos_url":"https://api.github.com/users/DevExpress/repos","events_url":"https://api.github.com/users/DevExpress/events{/privacy}","received_events_url":"https://api.github.com/users/DevExpress/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/DevExpress/testcafe-hammerhead","description":"A powerful web-proxy used as a core for the TestCafe testing framework.","fork":false,"url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead","forks_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/forks","keys_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/keys{/key_id}","collaborators_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/teams","hooks_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/hooks","issue_events_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues/events{/number}","events_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/events","assignees_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/assignees{/user}","branches_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/branches{/branch}","tags_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/tags","blobs_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/git/refs{/sha}","trees_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/git/trees{/sha}","statuses_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/statuses/{sha}","languages_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/languages","stargazers_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/stargazers","contributors_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/contributors","subscribers_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/subscribers","subscription_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/subscription","commits_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/commits{/sha}","git_commits_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/git/commits{/sha}","comments_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/comments{/number}","issue_comment_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues/comments{/number}","contents_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/contents/{+path}","compare_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/compare/{base}...{head}","merges_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/merges","archive_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/downloads","issues_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues{/number}","pulls_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls{/number}","milestones_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/milestones{/number}","notifications_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/labels{/name}","releases_url":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/releases{/id}","created_at":"2015-06-04T16:56:53Z","updated_at":"2015-11-19T10:59:06Z","pushed_at":"2015-12-30T12:58:00Z","git_url":"git://github.com/DevExpress/testcafe-hammerhead.git","ssh_url":"git@github.com:DevExpress/testcafe-hammerhead.git","clone_url":"https://github.com/DevExpress/testcafe-hammerhead.git","svn_url":"https://github.com/DevExpress/testcafe-hammerhead","homepage":"","size":1676,"stargazers_count":5,"watchers_count":5,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":30,"forks":9,"open_issues":30,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300"},"html":{"href":"https://github.com/DevExpress/testcafe-hammerhead/pull/300"},"issue":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues/300"},"comments":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/issues/300/comments"},"review_comments":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300/comments"},"review_comment":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/pulls/300/commits"},"statuses":{"href":"https://api.github.com/repos/DevExpress/testcafe-hammerhead/statuses/a2f79b51e6b16645d2a5d2eeecf68493fe8740c4"}}}}