{"action":"created","comment":{"url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/comments/75930282","id":75930282,"diff_hunk":"@@ -0,0 +1,444 @@\n+---\n+layout: post\n+title:  \"Making a JS plug-in: a full-stack approach\"\n+date:   2016-08-22 00:00:00\n+author: Miguel Revetria\n+categories: JavaScript, Plugin, Widget, CORS, Ruby, Rails, CSS\n+author_id: remer\n+markdown: redcarpet\n+\n+---\n+\n+In this occasion we will introduce a way to create a JavaScript client side plug-in that can be easily included in any third party websites.\n+This plug-in, will be useful to allow your users to execute custom actions like filling a registration form or maybe displaying information related with website.\n+\n+This plug-in might be included into many different web sites, each one implemented with its own technologies stack.\n+Nevertheless, all of them will run on a web browser.\n+\n+Since is all done with JavaScript, we won't care about the stack of the website but we will use and take advantages of native web technologies: HTML, CSS and JavaScript.\n+\n+Additionally, we are going to show some set up that must be done in server side in the case you are using Rails.\n+\n+## Designing the plug-in\n+\n+We are going to simplify the plug-in created in order to focus in the design and implementation of the plug-in.\n+You will be able to reuse the source code and ideas presented here even if you want to do something more complex that this example.\n+\n+Suppose that our plug-in clients are hair cutting web sites and we want to allow the users of those sites to schedule a hair cutting.\n+We are going to provide a schedule form within our plug-in.\n+\n+We have to take in mind that this client-side plug-in must be compatible with all kind of web applications and browsers in order to be used by all of our possible clients.\n+For this reason we will use only pure JavaScript code along with CSS and HTML. \n+This means, that we won't use any external library in the client since we want to be the less intrusive as possible.\n+\n+We want to make this plug-in easy to be integrated by client web sites' developers, that's why we will make public the constructor and a few methods and we will keep under the hood all the auxiliary code (internal variables and any detail of the implementation).\n+We will keep private all the auxiliary code used by the plug-in in order to correctly work, hiding it from developers.\n+\n+> Global variables and that kind of patterns will be avoided in our code.\n+Below we have a starting template for our plug-in:\n+\n+``` javascript\n+(function() {\n+\n+  var defaults = {\n+    clientId: undefined\n+  }\n+\n+  /******* Public Methods *******/\n+\n+  this.Widget = function() {\n+  }\n+\n+  Widget.prototype.init = function(options, doc) {\n+    this.doc = doc || window.document\n+    this.options = merge(defaults, options)\n+    \n+    if (!this.options.clientId) {\n+      // Validate options as needed\n+      console.error(\"Undefined `clientId`. You must set it before start using Widget\");\n+      return\n+    }\n+\n+    // Will create the widget\n+  }\n+\n+  /******* Private Methods *******/\n+\n+  function merge(source, overrides) {\n+    var property;\n+    for (property in overrides) {\n+      if (overrides.hasOwnProperty(property)) {\n+        if (overrides[property] instanceof Object) {\n+          source[property] = source[property] || new Object();\n+          merge(source[property], overrides[property])\n+        } else {\n+          source[property] = overrides[property];\n+        }\n+      }\n+    }\n+    return source;\n+  }\n+\n+}());\n+```\n+\n+This way all the client code only need to access to `Widget` function, in order to create an instance of our plug-in and to its `init` function.\n+\n+We have added defaults options that can be overridden by clients code.\n+Next code shows you how to add the widget to the client web site, add it within the `body` tag.\n+\n+```html\n+<script type=\"text/javascript\">\n+  var widgetInit = function() {\n+    widget.init({\n+      clientId: '7b4a03708a19ed6669ca00f4975a42b3'\n+    });\n+  };\n+\n+  (function(window, document, tag, source, objectName, init) {\n+    var element = document.createElement(tag);\n+    element.async = 1;\n+    element.defer = 'defer';\n+    element.src = source;\n+    element.onload = function() {\n+      window['MyAppWidget'] = objectName;\n+      window[objectName] = new Widget();\n+      init();\n+    };\n+    var parent = document.getElementsByTagName(tag)[0];\n+    parent.parentNode.insertBefore(element, parent);\n+  })(window, document, 'script', '//widget-server-domain/widget/widget.js?client_id=7b4a03708a19ed6669ca00f4975a42b3', 'widget', widgetInit);\n+</script>\n+```\n+\n+> Previous code asynchronously loads the widget's JavaScript.\n+> A global variable `widget` will be created with our plug-in prototype and the function `widgetInit` will be called immediately after the widget's JavaScript was loaded.\n+> We recommend to obfuscate all the code, except the `widgetInit` function (like most of the existing SDKs), before adding it in your client's webpage.\n+\n+Now that we've created the code base for applying the plug-in and we introduced a generic way to load it into any web page, we are going to go deep in our goal by adding some styles and functionalities to our widget.\n+\n+> Note: we are going to need a CSS file where to place the widget's styles. \n+\n+```javascript\n+(function() {\n+\n+  var defaults = {\n+    clientId: undefined,\n+  }\n+\n+  /******* Public Methods *******/\n+\n+  this.Widget = function() {\n+  }\n+\n+  Widget.prototype.init = function(options, doc) {\n+    this.doc = doc || window.document\n+    this.options = merge(defaults, options)\n+    \n+    if (!this.options.clientId) {\n+      // Validate options as needed\n+      console.error(\"Undefined `clientId`. You must set it before start using Widget\");\n+      return\n+    }\n+\n+    loadCss(this.doc, this.options);\n+    createWidgetMarker(this.doc, this.options);\n+    createWidget(this.doc, this.options);\n+  }\n+\n+  /******* Private Methods *******/\n+\n+  function merge(source, overrides) {\n+    var property;\n+    for (property in overrides) {\n+      if (overrides.hasOwnProperty(property)) {\n+        if (overrides[property] instanceof Object) {\n+          source[property] = source[property] || new Object();\n+          merge(source[property], overrides[property])\n+        } else {\n+          source[property] = overrides[property];\n+        }\n+      }\n+    }\n+    return source;\n+  }\n+\n+  function me() {\n+    // In the code added to the client web page, we have used `MyAppWidget` to store the name of the variable which holds the instance of our plug-in. \n+    // Next line will return the object stored in the `window` which the stored in the key `MyAppWidget` of `window`.\n+    return window[window['MyAppWidget']];\n+  }\n+\n+  function loadCss(doc, options) {\n+    // Load widget CSS into the page's head\n+    var css = me().doc.createElement('style');\n+    css.href = \"<URL to the plug-in's css file>\";\n+    me().doc.getElementsByTagName('head')[0].appendChild(css);\n+  }\n+\n+  function createWidgetMarker(doc, options) {\n+    var widgetMarker = doc.createElement('div');\n+    widgetMarker.className = 'myapp-widget-marker myapp-widget-hoverable myapp-widget-unselectable';\n+    widgetMarker.innerHTML = '<div class=\"myapp-widget-title\">Schedule your cute</div>';\n+    widgetMarker.onclick = toggleWidgetCollapsed;\n+    \n+    customizeMarker(widgetMarker, options);\n+\n+    doc.getElementsByTagName('body')[0].appendChild(widgetMarker);\n+  }\n+\n+  function createWidget(doc, options) {\n+    var widget = doc.createElement('div');\n+    widget.className = 'myapp-widget-widget myapp-widget-centered myapp-widget-hidden';\n+    widget.isCollpased = true;\n+    widget.onclick = hideWidget;\n+    widget.onscroll = function(ev) {  ev.stopPropagation(); }\n+    widget.ontouchmove = function(ev) {\n+      event.preventDefault();\n+      e.preventDefault();\n+    }\n+\n+    var body = doc.createElement('div');\n+    body.className = 'myapp-widget-body';\n+    body.onclick = function(evt) { evt.stopPropagation(); }\n+    customizeWidgetBody(body, options);\n+\n+    widget.appendChild(body);\n+\n+    var bodyContent = doc.createElement('div');\n+    bodyContent.className = 'myapp-widget-body-container';\n+    body.appendChild(bodyContent);\n+\n+    bodyContent.appendChild(createScheduleForm(doc, options));\n+\n+    doc.getElementsByTagName('body')[0].appendChild(widget);\n+\n+    me().widget = widget;\n+  }\n+\n+  function createScheduleForm(doc, options) {\n+    var page = doc.createElement('div');\n+    page.className = 'myapp-widget-container myapp-widget-centered';\n+    page.innerHTML = \n+      '<div class=\"myapp-widget-content\">' +\n+        '<div class=\"myapp-widget-title\">Schedule your hair cute</div>' +\n+        '<div class=\"myapp-widget-form\">' +\n+          '<div class=\"myapp-widget-row\">' +\n+            '<input id=\"myapp-widget-first-name\" type=\"text\" placeholder=\"First Name\" />' +\n+            '<input id=\"myapp-widget-last-name\" type=\"text\" placeholder=\"Last Name\" />' +\n+          '</div>' +\n+          '<div class=\"myapp-widget-row\">' +\n+            '<input id=\"myapp-widget-date\" type=\"date\" />' +\n+          '</div>' +\n+          '<div id=\"myapp-widget-subscribe-action\" class=\"myapp-widget-action myapp-widget-unselectable\">Schedule</div>' +\n+        '</div>' +\n+      '</div>';\n+\n+    var inputs = page.getElementsByTagName('input');\n+    for (var ind = 0; inputs[ind] != undefined; ind++) {\n+      customizeInput(inputs[ind]);\n+    }\n+\n+    var action = page.getElementsByClassName('myapp-widget-action')[0];\n+    action.onclick = scheduleButtonClicked;\n+    customizeAction(action);\n+\n+    return page;\n+  }\n+\n+  function toggleWidgetCollapsed() {\n+    var widget = me().widget;\n+    widget.isCollpased ? showWidget() : hideWidget();\n+  }\n+\n+  function hideWidget() {\n+    var widget = me().widget;\n+    widget.className = 'myapp-widget-widget myapp-widget-centered myapp-widget-hidden';\n+    widget.isCollpased = true;\n+    clearWidgetForm();\n+  }\n+\n+  function showWidget() {\n+    var widget = me().widget;\n+    widget.className = 'myapp-widget-widget myapp-widget-centered myapp-widget-visible';\n+    widget.isCollpased = false;\n+  }\n+\n+  function scheduleButtonClicked() {\n+    var xhttp = new XMLHttpRequest();\n+    xhttp.open(\"POST\", \"<URL to the appropriate resource>\", true);\n+    xhttp.setRequestHeader(\"Content-type\", \"application/json\");\n+    xhttp.onreadystatechange = function() {\n+      if (xhttp.readyState != 4) {\n+        return\n+      }\n+\n+      var responseText = xhttp.responseText;\n+      try {\n+        var response = JSON.parse(responseText);\n+        if (xhttp.status >= 200 && xhttp.status < 300) {\n+          hideWidget();\n+        } else {\n+          var msg = response.error.message\n+          alert(\"Oops! Something went wrong. \" + response.error.message);\n+        }\n+      } catch (error) {\n+        alert(\"Oops! Something went wrong, please try again later.\");\n+        console.error(error)\n+      }\n+    };\n+\n+    var params = {\n+      'client_id': me().options.clientId,\n+      'schedule': {\n+        'date': me().doc.getElementById('myapp-widget-date').value,\n+        'first_name': me().doc.getElementById('myapp-widget-first-name').value,\n+        'last_name': me().doc.getElementById('myapp-widget-last-name').value\n+      }\n+    };\n+    xhttp.send(JSON.stringify(params));    \n+  }\n+  \n+  /******* DIY: straightforward functions *******/\n+\n+  // change marker style properties according to the options passed\n+  function customizeMarker(marker, options) { }\n+\n+  // change widget body style properties according to the options passed\n+  function customizeWidgetBody(body, options) { }\n+\n+  // change input style properties according to the options passed\n+  function customizeInput(input, options) { }\n+\n+  // change action style properties according to the options passed\n+  function customizeAction(action, options) { }\n+\n+  // clear form input values\n+  function clearWidgetForm() { }\n+}());\n+```\n+\n+> To keep this blogpost simple we omitted some implementations and the entire CSS file. Don't worry, these files are in the templates code base [here](https://github.com/xmartlabs/javascript-plugin).\n+\n+## Server Time\n+\n+Now that we've done our brand new JavaScript plug-in, it's time to handle some concerns in the server side:\n+\n+### 1. Dynamic JavaScript\n+\n+In some cases you may need to do some minor modifications in the plug-in, based on the client that is currently requiring the widget.\n+Let's see this with an example:\n+\n+Imagine that we allow our clients to modify the positioning and styles dynamically of the widget (that way it will be integrated smoothly).\n+\n+With this information accessible, we need a way to retrieve it depending on the client web site that is requesting our plug-in.\n+So, you need to add an endpoint to serve the plug-in and remove the static JavaScript file.\n+Such endpoint will accept a `clientId` parameter included in the request in order to get our plug-in JavaScript customized with the client preferences.\n+\n+If `clientId` is not present we can return a default JavaScript without any customization.\n+\n+You can save the plug-in code and the CSS files as a template (ERB, HAML, etc., if you are using Ruby) and use an engine to render them. \n+The following code will help you to achieve this.\n+\n+```javascript\n+// Instead of setting the `href` property of the style element, just add the parse CSS file \n+// content as its `innerHTML`\n+var css = me().doc.createElement('style');\n+// As an example, we are passing an additionally option arguments in order to make some\n+// modifications to the CSS template file.\n+css.innerHTML = \"<%=j render('widget/widget.css', options: options) %>\";\n+me().doc.getElementsByTagName('head')[0].appendChild(css);\n+```\n+\n+> We have embed the CSS result within the script as a HTML `style` element.\n+\n+### 2. Identifying Client Web Sites\n+\n+In the sample code of the widget we have created a form allowing users to schedule a hair cut.\n+We'd like to be sure that customers registering from the widget are using it from the hair cutting web site and prevent that someone else executes the schedule service from non-allowed domains (remember that we have enabled CORS for the service related to the plug-in).\n+\n+The best approach to resolve this, is having a whitelist of the domains that will be allowed to hit your server.","path":"_posts/2016-08-22-making-a-js-plug-in-a-full-stack-approach.md","position":360,"original_position":360,"commit_id":"2ea98e583d3bf02f7f3fb61c52525f5e0dfecb1c","original_commit_id":"2ea98e583d3bf02f7f3fb61c52525f5e0dfecb1c","user":{"login":"bryant1410","id":3905501,"avatar_url":"https://avatars.githubusercontent.com/u/3905501?v=3","gravatar_id":"","url":"https://api.github.com/users/bryant1410","html_url":"https://github.com/bryant1410","followers_url":"https://api.github.com/users/bryant1410/followers","following_url":"https://api.github.com/users/bryant1410/following{/other_user}","gists_url":"https://api.github.com/users/bryant1410/gists{/gist_id}","starred_url":"https://api.github.com/users/bryant1410/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bryant1410/subscriptions","organizations_url":"https://api.github.com/users/bryant1410/orgs","repos_url":"https://api.github.com/users/bryant1410/repos","events_url":"https://api.github.com/users/bryant1410/events{/privacy}","received_events_url":"https://api.github.com/users/bryant1410/received_events","type":"User","site_admin":false},"body":"I think it should be \"it is\"","created_at":"2016-08-23T19:02:35Z","updated_at":"2016-08-23T19:02:35Z","html_url":"https://github.com/xmartlabs/xmartlabs.github.io/pull/14#discussion_r75930282","pull_request_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14","_links":{"self":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/comments/75930282"},"html":{"href":"https://github.com/xmartlabs/xmartlabs.github.io/pull/14#discussion_r75930282"},"pull_request":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14"}}},"pull_request":{"url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14","id":67058793,"html_url":"https://github.com/xmartlabs/xmartlabs.github.io/pull/14","diff_url":"https://github.com/xmartlabs/xmartlabs.github.io/pull/14.diff","patch_url":"https://github.com/xmartlabs/xmartlabs.github.io/pull/14.patch","issue_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/14","number":14,"state":"open","locked":false,"title":"Post about creating a client-side Javascript plugin to show a widget","user":{"login":"m-revetria","id":4791678,"avatar_url":"https://avatars.githubusercontent.com/u/4791678?v=3","gravatar_id":"","url":"https://api.github.com/users/m-revetria","html_url":"https://github.com/m-revetria","followers_url":"https://api.github.com/users/m-revetria/followers","following_url":"https://api.github.com/users/m-revetria/following{/other_user}","gists_url":"https://api.github.com/users/m-revetria/gists{/gist_id}","starred_url":"https://api.github.com/users/m-revetria/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/m-revetria/subscriptions","organizations_url":"https://api.github.com/users/m-revetria/orgs","repos_url":"https://api.github.com/users/m-revetria/repos","events_url":"https://api.github.com/users/m-revetria/events{/privacy}","received_events_url":"https://api.github.com/users/m-revetria/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-04-19T17:36:01Z","updated_at":"2016-08-23T19:02:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c217cb6b2d765489e26c819e71e2e0cc81c55eb9","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14/commits","review_comments_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14/comments","review_comment_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/14/comments","statuses_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/statuses/2ea98e583d3bf02f7f3fb61c52525f5e0dfecb1c","head":{"label":"xmartlabs:javascript-client-widget","ref":"javascript-client-widget","sha":"2ea98e583d3bf02f7f3fb61c52525f5e0dfecb1c","user":{"login":"xmartlabs","id":2227731,"avatar_url":"https://avatars.githubusercontent.com/u/2227731?v=3","gravatar_id":"","url":"https://api.github.com/users/xmartlabs","html_url":"https://github.com/xmartlabs","followers_url":"https://api.github.com/users/xmartlabs/followers","following_url":"https://api.github.com/users/xmartlabs/following{/other_user}","gists_url":"https://api.github.com/users/xmartlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/xmartlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xmartlabs/subscriptions","organizations_url":"https://api.github.com/users/xmartlabs/orgs","repos_url":"https://api.github.com/users/xmartlabs/repos","events_url":"https://api.github.com/users/xmartlabs/events{/privacy}","received_events_url":"https://api.github.com/users/xmartlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":31186390,"name":"xmartlabs.github.io","full_name":"xmartlabs/xmartlabs.github.io","owner":{"login":"xmartlabs","id":2227731,"avatar_url":"https://avatars.githubusercontent.com/u/2227731?v=3","gravatar_id":"","url":"https://api.github.com/users/xmartlabs","html_url":"https://github.com/xmartlabs","followers_url":"https://api.github.com/users/xmartlabs/followers","following_url":"https://api.github.com/users/xmartlabs/following{/other_user}","gists_url":"https://api.github.com/users/xmartlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/xmartlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xmartlabs/subscriptions","organizations_url":"https://api.github.com/users/xmartlabs/orgs","repos_url":"https://api.github.com/users/xmartlabs/repos","events_url":"https://api.github.com/users/xmartlabs/events{/privacy}","received_events_url":"https://api.github.com/users/xmartlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/xmartlabs/xmartlabs.github.io","description":"Xmartlabs Blog","fork":false,"url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io","forks_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/forks","keys_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/teams","hooks_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/hooks","issue_events_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/events","assignees_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/tags","blobs_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/languages","stargazers_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/stargazers","contributors_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/contributors","subscribers_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/subscribers","subscription_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/subscription","commits_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/merges","archive_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/downloads","issues_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/labels{/name}","releases_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/deployments","created_at":"2015-02-22T23:40:28Z","updated_at":"2016-02-26T19:10:39Z","pushed_at":"2016-08-23T15:44:47Z","git_url":"git://github.com/xmartlabs/xmartlabs.github.io.git","ssh_url":"git@github.com:xmartlabs/xmartlabs.github.io.git","clone_url":"https://github.com/xmartlabs/xmartlabs.github.io.git","svn_url":"https://github.com/xmartlabs/xmartlabs.github.io","homepage":null,"size":13232,"stargazers_count":5,"watchers_count":5,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":2,"mirror_url":null,"open_issues_count":2,"forks":2,"open_issues":2,"watchers":5,"default_branch":"master"}},"base":{"label":"xmartlabs:master","ref":"master","sha":"adcad4bdb26b95d844d878dddf4591c8efe2790f","user":{"login":"xmartlabs","id":2227731,"avatar_url":"https://avatars.githubusercontent.com/u/2227731?v=3","gravatar_id":"","url":"https://api.github.com/users/xmartlabs","html_url":"https://github.com/xmartlabs","followers_url":"https://api.github.com/users/xmartlabs/followers","following_url":"https://api.github.com/users/xmartlabs/following{/other_user}","gists_url":"https://api.github.com/users/xmartlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/xmartlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xmartlabs/subscriptions","organizations_url":"https://api.github.com/users/xmartlabs/orgs","repos_url":"https://api.github.com/users/xmartlabs/repos","events_url":"https://api.github.com/users/xmartlabs/events{/privacy}","received_events_url":"https://api.github.com/users/xmartlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":31186390,"name":"xmartlabs.github.io","full_name":"xmartlabs/xmartlabs.github.io","owner":{"login":"xmartlabs","id":2227731,"avatar_url":"https://avatars.githubusercontent.com/u/2227731?v=3","gravatar_id":"","url":"https://api.github.com/users/xmartlabs","html_url":"https://github.com/xmartlabs","followers_url":"https://api.github.com/users/xmartlabs/followers","following_url":"https://api.github.com/users/xmartlabs/following{/other_user}","gists_url":"https://api.github.com/users/xmartlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/xmartlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xmartlabs/subscriptions","organizations_url":"https://api.github.com/users/xmartlabs/orgs","repos_url":"https://api.github.com/users/xmartlabs/repos","events_url":"https://api.github.com/users/xmartlabs/events{/privacy}","received_events_url":"https://api.github.com/users/xmartlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/xmartlabs/xmartlabs.github.io","description":"Xmartlabs Blog","fork":false,"url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io","forks_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/forks","keys_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/teams","hooks_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/hooks","issue_events_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/events","assignees_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/tags","blobs_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/languages","stargazers_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/stargazers","contributors_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/contributors","subscribers_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/subscribers","subscription_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/subscription","commits_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/merges","archive_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/downloads","issues_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/labels{/name}","releases_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/deployments","created_at":"2015-02-22T23:40:28Z","updated_at":"2016-02-26T19:10:39Z","pushed_at":"2016-08-23T15:44:47Z","git_url":"git://github.com/xmartlabs/xmartlabs.github.io.git","ssh_url":"git@github.com:xmartlabs/xmartlabs.github.io.git","clone_url":"https://github.com/xmartlabs/xmartlabs.github.io.git","svn_url":"https://github.com/xmartlabs/xmartlabs.github.io","homepage":null,"size":13232,"stargazers_count":5,"watchers_count":5,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":2,"mirror_url":null,"open_issues_count":2,"forks":2,"open_issues":2,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14"},"html":{"href":"https://github.com/xmartlabs/xmartlabs.github.io/pull/14"},"issue":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/14"},"comments":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/issues/14/comments"},"review_comments":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14/comments"},"review_comment":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/pulls/14/commits"},"statuses":{"href":"https://api.github.com/repos/xmartlabs/xmartlabs.github.io/statuses/2ea98e583d3bf02f7f3fb61c52525f5e0dfecb1c"}}}}
{"actor":{"display_login":"bryant1410"}}