├── LICENSE.md ├── README.md ├── ig-min.js └── ig.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Instagram (Burbn, Inc.) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Instagram JavaScript SDK 2 | ============================ 3 | A JavaScript library for the Instagram REST and Search APIs 4 | (currently supports authentication, authorization and persisting sessions) 5 | 6 | Take a look at the [Instadrop](https://github.com/Instagram/Instadrop) project for an implementation example. 7 | 8 | 9 | Follow @instagramapi on Twitter 10 | ---------------------------- 11 | You should [follow @instagramapi on Twitter](http://twitter.com/#!/instagramapi) for announcements, 12 | updates, and news about the Instagram API. 13 | 14 | 15 | Join the mailing list! 16 | ---------------------- 17 | 18 | 19 | 20 | SDK Usage Examples 21 | ------------------ 22 | IG.init({ 23 | client_id: YOUR_CLIENT_ID, 24 | check_status: true, // check and load active session 25 | cookie: true // persist a session via cookie 26 | }); 27 | 28 | // client side access_token flow (implicit) 29 | IG.login(function (response) { 30 | if (response.session) { 31 | // user is logged in 32 | } 33 | }, {scope: ['comments', 'likes']}); 34 | 35 | // client side code flow 36 | IG.login(function (response) { 37 | if (response.code) { 38 | // user authorized app, send code to server 39 | // for access_token exchange 40 | } 41 | }, {response_type: 'code', scope: ['comments', 'likes']}); 42 | 43 | Contributing 44 | ------------ 45 | In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project. 46 | 47 | Here are some ways *you* can contribute: 48 | 49 | * by using alpha, beta, and prerelease versions 50 | * by reporting bugs 51 | * by suggesting new features 52 | * by writing or editing documentation 53 | * by writing specifications 54 | * by writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace) 55 | * by refactoring code 56 | * by closing [issues](http://github.com/Instagram/instagram-javascript-sdk/issues) 57 | * by reviewing patches 58 | 59 | 60 | Submitting an Issue 61 | ------------------- 62 | We use the [GitHub issue tracker](http://github.com/Instagram/instagram-javascript-sdk/issues) to track bugs and 63 | features. Before submitting a bug report or feature request, check to make sure it hasn't already 64 | been submitted. You can indicate support for an existing issuse by voting it up. When submitting a 65 | bug report, please include a [Gist](http://gist.github.com/) that includes a stack trace and any 66 | details that may be necessary to reproduce the bug, including your library version, browser version, and 67 | operating system. Ideally, a bug report should include a pull request with failing specs. 68 | 69 | 70 | Submitting a Pull Request 71 | ------------------------- 72 | 1. Fork the project. 73 | 2. Create a topic branch. 74 | 3. Implement your feature or bug fix. 75 | 4. Add documentation for your feature or bug fix. 76 | 5. Commit and push your changes. 77 | 6. Submit a pull request. 78 | 79 | 80 | Copyright 81 | --------- 82 | Copyright (c) 2011 Instagram (Burbn, Inc). 83 | See [LICENSE](https://github.com/Instagram/instagram-javascript-sdk/blob/master/LICENSE.md) for details. 84 | -------------------------------------------------------------------------------- /ig-min.js: -------------------------------------------------------------------------------- 1 | if(!window.IG){window.IG={_client_id:null,_session:null,_userStatus:"unknown",_logging:false,_domain:{https_com:"https://instagram.com/",api:"http://api.instagram.com/"},getDomain:function(a){switch(a){case "https_com":return IG._domain.https_com;case "api":return IG._domain.api}},provide:function(a,d){return IG.copy(IG.create(a),d)},copy:function(a,d){for(var b in d)if(d.hasOwnProperty(b))if(typeof a[b]==="undefined")a[b]=d[b];return a},create:function(a,d){var b=window.IG,e=a?a.split("."):[],f= 2 | e.length,g;for(g=0;g';a.root.innerHTML='';b=true;window.setTimeout(function(){a.root.innerHTML= 7 | f},0)}else{var g=document.createElement("iframe");g.id=a.id;g.name=a.name;g.onload=IG.Content._callbacks[d];g.scrolling="no";g.style.border="none";g.style.overflow="hidden";if(a.title)g.title=a.title;if(a.className)g.className=a.className;if(a.height)g.height=a.height;if(a.width)g.width=a.width;a.root.appendChild(g);b=true;g.src=a.url}}});IG.provide("JSON",{stringify:function(a){return window.Prototype&&Object.toJSON?Object.toJSON(a):JSON.stringify(a)},parse:function(a){return JSON.parse(a)},flatten:function(a){var d= 8 | {},b;for(b in a)if(a.hasOwnProperty(b)){var e=a[b];if(typeof e==="string"&&b!==null)d[b]=e;else if(e!==null&&e!==undefined)d[b]=IG.JSON.stringify(e)}return d}});IG.provide("EventProvider",{subscribers:function(){if(!this._subscribersMap)this._subscribersMap={};return this._subscribersMap},subscribe:function(a,d){IG.log("Subscription to: "+a);var b=this.subscribers();if(b[a])b[a].push(d);else b[a]=[d]},unsubscribe:function(a,d){var b=this.subscribers()[a];IG.Array.forEach(b,function(e,f){if(e===d)b[f]= 9 | null})},monitor:function(a,d){if(!d()){var b=this,e=function(){d.apply(d,arguments)&&b.unsubscribe(a,e)};this.subscribe(a,e)}},clear:function(a){delete this.subscribers()[a]},fire:function(){var a=Array.prototype.slice.call(arguments),d=a.shift();IG.log("Fire for: "+d);IG.Array.forEach(this.subscribers()[d],function(b){b&&b.apply(this,a)})}});IG.provide("Event",IG.EventProvider);IG.provide("XD",{_origin:null,_transport:null,_callbacks:{},init:function(){if(IG.XD._origin)IG.log('XD.init called with "XD._origin" already set. Returning.'); 10 | else if(window.addEventListener&&!window.attachEvent&&window.postMessage){IG.log('Using "postmessage" as XD transport.');IG.XD._origin=window.location.protocol+"//"+window.location.host+"/"+IG.guid();IG.XD.PostMessage.init();IG.XD._transport="postmessage"}else{IG.log('Using "fragment" as XD transport.');IG.XD._transport="fragment";IG.XD.Fragment._channelUrl=window.location.toString()}},resolveRelation:function(a){var d=a.split("."),b=window,e,f;f=0;for(num_relations=d.length;f0)return"javascript:false;//";var b=IG.getDomain("https_com")+"oauth/xd_proxy/#",e=IG.guid();if(IG.XD._transport==="fragment"){b=IG.XD.Fragment._channelUrl;var f=b.indexOf("#");if(f> 12 | 0)b=b.substr(0,f);b+=(b.indexOf("?")<0?"?":"&")+IG.XD.Fragment._magic+"#?=&"}IG.XD._callbacks[e]=a;return b+IG.QS.encode({cb:e,origin:IG.XD._origin,relation:d||"opener",transport:IG.XD._transport})},recv:function(a){if(typeof a==="string")a=IG.QS.decode(a);var d=IG.XD._callbacks[a.cb];delete IG.XD._callbacks[a.cb];d&&d(a)},PostMessage:{init:function(){var a=IG.XD.PostMessage.onMessage;window.addEventListener?window.addEventListener("message",a,false):window.attachEvent("onmessage",a)},onMessage:function(a){IG.XD.recv(a.data)}}, 13 | Fragment:{_magic:"ig_xd_fragment",checkAndDispatch:function(){var a=window.location.toString(),d=a.substr(a.indexOf("#")+1);if(a.indexOf(IG.XD.Fragment._magic)>0){IG.init=function(){};document.documentElement.style.display="none";IG.XD.resolveRelation(IG.QS.decode(d).relation).IG.XD.recv(d)}}}});IG.XD.Fragment.checkAndDispatch();IG.provide("",{ui:function(a,d){var b=IG.UIServer.prepareCall(a,d);if(b){var e=IG.UIServer[b.params.display];e?e(b):IG.log('"display" must be "popup"')}else IG.log('"prepareCall" failed to return options')}}); 14 | IG.provide("UIServer",{Methods:{},_active:{},_defaultCb:{},prepareCall:function(a,d){var b=a.method.toLowerCase(),e=IG.UIServer.Methods[b];b=IG.guid();IG.copy(a,{client_id:IG._client_id,access_token:IG._session&&IG._session.access_token||undefined});a.display=IG.UIServer.getDisplayMode(e,a);b={callback:d,id:b,size:e.size||{},url:e.url,params:a};if(e.transform){b=e.transform(b);if(!b){IG.log('Call to "transform" in "prepareCall" failed to return options');return}}e=IG.UIServer.getXdRelation(b.params.display); 15 | b.params.redirect_uri=IG.UIServer._xdResult(b.callback,b.id,e,true);b.params=IG.JSON.flatten(b.params);if(e=IG.QS.encode(b.params))b.url+="?"+e;return b},getDisplayMode:function(a,d){if(d.display==="hidden")return"hidden";return"popup"},getXdRelation:function(a){if(a==="popup")return"opener"},popup:function(a){var d=typeof window.screenX!=="undefined"?window.screenX:window.screenLeft,b=typeof window.screenY!=="undefined"?window.screenY:window.screenTop,e=typeof window.outerHeight!=="undefined"?window.outerHeight: 16 | document.documentElement.clientHeight-22,f=a.size.width,g=a.size.height;d=parseInt((d<0?window.screen.width+d:d)+((typeof window.outerWidth!=="undefined"?window.outerWidth:document.documentElement.clientWidth)-f)/2,10);b=parseInt(b+(e-g)/2.5,10);f="width="+f+",height="+g+",left="+d+",top="+b+",scrollbars=1,location=1,toolbar=0";IG.log("opening popup: "+a.id);IG.UIServer._active[a.id]=window.open(a.url,a.id,f)},hidden:function(a){a.className="IG_UI_Hidden";a.root=IG.Content.appendHidden("");IG.UIServer._insertIframe(a)}, 17 | _insertIframe:function(a){IG.UIServer._active[a.id]=false;IG.Content.insertIframe({url:a.url,root:a.root,className:a.className,width:a.size.width,height:a.size.height,onload:function(d){if(IG.UIServer._active.hasOwnProperty(a.id))IG.UIServer._active[a.id]=d}})},_xdRedirectUriHandler:function(a,d,b,e){if(e)IG.UIServer._defaultCb[d]=a;return IG.XD.handler(function(f){IG.UIServer._xdRecv(f,a)},b)+"&frame="+d},_xdRecv:function(a,d){var b=IG.UIServer._active[a.frame];try{b.close&&b.close()}catch(e){}delete IG.UIServer._active[a.frame]; 18 | delete IG.UIServer._defaultCb[a.frame];d(a)},_xdResult:function(a,d,b,e){return IG.UIServer._xdRedirectUriHandler(function(f){a&&f.result&&a(IG.JSON.parse(f.result))},d,b,e)}});IG.provide("",{getLoginStatus:function(a,d){if(IG._client_id){if(a)if(!d&&IG.Auth._loadState==="loaded"){a({status:IG._userStatus,session:IG._session});return}else IG.Event.subscribe("IG.loginStatus",a);if(!(!d&&IG.Auth._loadState==="loading")){IG.Auth._loadState="loading";IG.ui({method:"auth.status",display:"hidden"},function(b){IG.Auth._loadState= 19 | "loaded";IG.Event.fire("IG.loginStatus",b);IG.Event.clear("IG.loginStatus")})}}else IG.log("IG.getLoginStatus() called before calling IG.init().")},login:function(a,d){IG.ui(IG.copy({display:"popup",method:"authorize"},d||{}),a)},logout:function(){IG.Auth.setSession()}});IG.provide("Auth",{setSession:function(a,d){var b=!IG._session&&a,e=IG._session&&!a,f=b||e||IG._session&&a&&IG._session.access_token!==a.access_token,g=d!==IG._userStatus,h={session:a,status:d};IG._session=a;IG._userStatus=d;f&&IG.Cookie.getEnabled()&& 20 | IG.Cookie.set(a);g&&IG.Event.fire("auth.statusChange",h);b&&IG.Event.fire("auth.login",h);e&&IG.Event.fire("auth.logout",h);f&&IG.Event.fire("auth.sessionChange",h);return h},xdHandler:function(a,d,b,e,f,g){return IG.UIServer._xdRedirectUriHandler(IG.Auth.xdResponseWrapper(a,f,g),d,b,e)},xdResponseWrapper:function(a,d,b){return function(e){try{b=IG.JSON.parse(e.session||null);b.scope=IG.JSON.parse(e.scope||null)}catch(f){}if(b)d="connected";var g=IG.Auth.setSession(b||null,d),h=["code","error","error_reason", 21 | "error_description"];e&&IG.Array.forEach(h,function(i){g[i]=e[i]||null});a&&a(g)}}});IG.provide("UIServer.Methods",{authorize:{size:{width:627,height:326},url:IG.getDomain("https_com")+"oauth/authorize/",transform:function(a){if(IG._client_id){if(IG._session){var d=false;IG.Array.forEach(a.params.scope,function(b){if(IG._session.scope.indexOf(b)===-1)d=true});if(!d){IG.log("IG.login() called when user is already connected.");if(a.callback){a.callback({status:IG._userStatus,session:IG._session});return}}}a.callback= 22 | IG.Auth.xdResponseWrapper(a.callback);if(a.params.scope)a.params.scope=a.params.scope.join(" ");IG.copy(a.params,{response_type:"token"});return a}else IG.log("IG.login() called before claling IG.init().")}},"auth.status":{url:IG.getDomain("https_com")+"oauth/login_status/",transform:function(a){var d=a.callback;delete a.callback;IG.copy(a.params,{no_session:IG.Auth.xdHandler(d,a.id,"parent",false,"notConnected"),no_user:IG.Auth.xdHandler(d,a.id,"parent",false,"unknown"),ok_session:IG.Auth.xdHandler(d, 23 | a.id,"parent",false,"connected")});return a}}});IG.provide("Cookie",{_domain:null,_enabled:false,setEnabled:function(a){IG.Cookie._enabled=a},getEnabled:function(){return IG.Cookie._enabled},load:function(){var a=document.cookie.match("\\bigs_"+IG._client_id+'="([^;]*)\\b'),d;if(a){d=IG.QS.decode(a[1]);if(d.expires)d.expires=parseInt(d.expires,10);IG.Cookie._domain=d.base_domain}return d},setRaw:function(a,d,b){document.cookie="igs_"+IG._client_id+'="'+a+'"'+(a&&d===0?"":"; expires="+(new Date(d* 24 | 1E3)).toGMTString())+"; path=/"+(b?"; domain=."+b:"");IG.Cookie._domain=b},set:function(a){a?IG.Cookie.setRaw(IG.QS.encode(a),a.expires,a.base_domain):IG.Cookie.clear()},clear:function(){IG.Cookie.setRaw("",0,IG.Cookie._domain)}});IG.provide("",{init:function(a){a=IG.copy(a||{},{logging:false,check_status:true});IG._client_id=a.client_id;IG._logging=a.logging||typeof a.logging==="undefined"&&window.location.toString().indexOf("ig_debug=1")>0;IG.XD.init();if(IG._client_id){IG.Cookie.setEnabled(a.cookie); 25 | a.session=a.session||a.cookie&&IG.Cookie.load();IG.Auth.setSession(a.session);a.check_status&&IG.getLoginStatus()}}});window.setTimeout(function(){if(window.igAsyncInit&&!window.igAsyncInit.hasRun){window.igAsyncInit.hasRun=true;igAsyncInit()}},0)}; -------------------------------------------------------------------------------- /ig.js: -------------------------------------------------------------------------------- 1 | if (!window.IG) { 2 | window.IG = { 3 | _client_id: null, 4 | _session: null, 5 | _userStatus: 'unknown', 6 | _logging: false, 7 | _domain: { 8 | // https_com: 'http://instagram.local/', 9 | https_com: 'https://instagram.com/', 10 | api: 'http://api.instagram.com/' 11 | }, 12 | getDomain: function (site) { 13 | switch (site) { 14 | case 'https_com': 15 | return IG._domain.https_com; 16 | case 'api': 17 | return IG._domain.api; 18 | } 19 | }, 20 | provide: function (name, literal) { 21 | return IG.copy(IG.create(name), literal); 22 | }, 23 | copy: function (to_object, from_object) { 24 | var key; 25 | for (key in from_object) { 26 | if (from_object.hasOwnProperty(key)) { 27 | if (typeof to_object[key] === 'undefined') { 28 | to_object[key] = from_object[key]; 29 | } 30 | } 31 | } 32 | return to_object; 33 | }, 34 | create: function (key_or_keys, object) { 35 | var root = window.IG, 36 | keys = key_or_keys ? key_or_keys.split('.') : [], 37 | num_keys = keys.length, 38 | i; 39 | for (i = 0; i < num_keys; i++) { 40 | var key = keys[i]; 41 | var child = root[key]; 42 | if (!child) { 43 | child = (object && i + 1 === num_keys) ? object : {}; 44 | root[key] = child; 45 | } 46 | root = child; 47 | } 48 | return root; 49 | }, 50 | guid: function () { 51 | return 'f' + (Math.random() * (1 << 30)).toString(16).replace('.', ''); 52 | }, 53 | log: function (message) { 54 | if (IG._logging) { 55 | if (window.Debug && window.Debug.writeln) { 56 | window.Debug.writeln(message); 57 | } else if (window.console) { 58 | window.console.log(message); 59 | } 60 | } 61 | }, 62 | $: function (id) { 63 | return document.getElementById(id); 64 | } 65 | }; 66 | IG.provide('Array', { 67 | forEach: function (array, fn) { 68 | var i, key; 69 | 70 | if (!array) { 71 | return; 72 | } 73 | 74 | if (Object.prototype.toString.apply(array) === '[object Array]' || (!(array instanceof Function) && typeof array.length === 'number')) { 75 | if (array.forEach) { 76 | array.forEach(fn); 77 | } else { 78 | for (i = 0, length = array.length; i < length; i++) { 79 | fn(array[i], i, array); 80 | } 81 | } 82 | } else { 83 | for (key in array) { 84 | if (array.hasOwnProperty(key)) { 85 | fn(array[key], key, array); 86 | } 87 | } 88 | } 89 | }, 90 | join: function (array, delimiter) { 91 | var join_string = ''; 92 | 93 | IG.Array.forEach(array, function (value, key) { 94 | join_string += value + delimiter; 95 | }); 96 | 97 | join_string = join_string.substr(0, join_string.lastIndexOf(delimiter)); 98 | 99 | return join_string; 100 | } 101 | }); 102 | IG.provide('QS', { 103 | encode: function (object, delimiter, encode) { 104 | delimiter = (delimiter === undefined) ? '&' : delimiter; 105 | encoder = (encode === false) ? 106 | function (component) { 107 | return component; 108 | } : encodeURIComponent; 109 | var pairs = []; 110 | IG.Array.forEach(object, function (value, key) { 111 | if (value !== null && typeof value !== 'undefined') { 112 | pairs.push(encoder(key) + '=' + encoder(value)); 113 | } 114 | }); 115 | pairs.sort(); 116 | return pairs.join(delimiter); 117 | }, 118 | decode: function(string) { 119 | var pairs = string.split('&'), 120 | object = {}, 121 | i; 122 | 123 | for (i = 0; i < pairs.length; i++) { 124 | pair = pairs[i].split('=', 2); 125 | if (pair && pair[0]) { 126 | object[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); 127 | } 128 | } 129 | 130 | return object; 131 | } 132 | }); 133 | IG.provide('Content', { 134 | _root: null, 135 | _hiddenRoot: null, 136 | _callbacks: {}, 137 | append: function (element_or_html, root) { 138 | if (!root) { 139 | if (!IG.Content._root) { 140 | IG.Content._root = root = IG.$('ig-root'); 141 | if (!root) { 142 | IG.log('The "ig-root" div has not been created.'); 143 | return; 144 | } 145 | } else { 146 | c = IG.Content._root; 147 | } 148 | } 149 | if (typeof element_or_html === 'string') { 150 | var div = document.createElement('div'); 151 | root.appendChild(div).innerHTML = element_or_html; 152 | return div; 153 | } else { 154 | return root.appendChild(element_or_html); 155 | } 156 | }, 157 | appendHidden: function (options) { 158 | if (!IG.Content._hiddenRoot) { 159 | var div = document.createElement('div'); 160 | 161 | div.style.position = 'absolute'; 162 | div.style.top = '-10000px'; 163 | div.style.width = div.style.height = 0; 164 | 165 | IG.Content._hiddenRoot = IG.Content.append(div); 166 | } 167 | 168 | return IG.Content.append(options, IG.Content._hiddenRoot); 169 | }, 170 | insertIframe: function (options) { 171 | options.id = options.id || IG.guid(); 172 | options.name = options.name || IG.guid(); 173 | 174 | var callback_guid = IG.guid(), 175 | callback_ready = false, 176 | callback_fired = false; 177 | 178 | IG.Content._callbacks[callback_guid] = function () { 179 | if (callback_ready && !callback_fired) { 180 | callback_fired = true; 181 | if (options.onload) { 182 | options.onload(options.root.firstChild); 183 | } 184 | } 185 | }; 186 | 187 | if (document.attachEvent) { 188 | var html = (''); 199 | 200 | options.root.innerHTML = ''; 204 | callback_ready = true; 205 | window.setTimeout(function () { 206 | options.root.innerHTML = html; 207 | }, 0); 208 | } else { 209 | var iframe = document.createElement('iframe'); 210 | iframe.id = options.id; 211 | iframe.name = options.name; 212 | iframe.onload = IG.Content._callbacks[callback_guid]; 213 | iframe.scrolling = 'no'; 214 | iframe.style.border = 'none'; 215 | iframe.style.overflow = 'hidden'; 216 | if (options.title) { 217 | iframe.title = options.title; 218 | } 219 | if (options.className) { 220 | iframe.className = options.className; 221 | } 222 | if (options.height) { 223 | iframe.height = options.height; 224 | } 225 | if (options.width) { 226 | iframe.width = options.width; 227 | } 228 | options.root.appendChild(iframe); 229 | callback_ready = true; 230 | iframe.src = options.url; 231 | } 232 | } 233 | }); 234 | IG.provide('JSON', { 235 | stringify: function (object) { 236 | if (window.Prototype && Object.toJSON) { 237 | return Object.toJSON(object); 238 | } else { 239 | return JSON.stringify(object); 240 | } 241 | }, 242 | parse: function (string) { 243 | return JSON.parse(string); 244 | }, 245 | flatten: function (object) { 246 | var flat_object = {}, 247 | key; 248 | 249 | for (key in object) { 250 | if (object.hasOwnProperty(key)) { 251 | var value = object[key]; 252 | 253 | if (typeof value === 'string' && key !== null) { 254 | flat_object[key] = value; 255 | } else if (value !== null && value !== undefined) { 256 | flat_object[key] = IG.JSON.stringify(value); 257 | } 258 | } 259 | } 260 | return flat_object; 261 | } 262 | }); 263 | IG.provide('EventProvider', { 264 | subscribers: function () { 265 | if (!this._subscribersMap) { 266 | this._subscribersMap = {}; 267 | } 268 | return this._subscribersMap; 269 | }, 270 | subscribe: function (event_name, callback) { 271 | IG.log('Subscription to: ' + event_name); 272 | var subscribers = this.subscribers(); 273 | if (!subscribers[event_name]) { 274 | subscribers[event_name] = [callback]; 275 | } else { 276 | subscribers[event_name].push(callback); 277 | } 278 | }, 279 | unsubscribe: function (event_name, callback) { 280 | var subscribers = this.subscribers()[event_name]; 281 | IG.Array.forEach(subscribers, function (event_callback, i) { 282 | if (event_callback === callback) { 283 | subscribers[i] = null; 284 | } 285 | }); 286 | }, 287 | monitor: function (event_name, callback) { 288 | if (!callback()) { 289 | var event_provider = this, 290 | bound_callback = function () { 291 | if (callback.apply(callback, arguments)) { 292 | event_provider.unsubscribe(event_name, bound_callback); 293 | } 294 | }; 295 | this.subscribe(event_name, bound_callback); 296 | } 297 | }, 298 | clear: function(event_name) { 299 | delete this.subscribers()[event_name]; 300 | }, 301 | fire: function () { 302 | var event_args = Array.prototype.slice.call(arguments), 303 | event_name = event_args.shift(); 304 | 305 | IG.log('Fire for: ' + event_name); 306 | IG.Array.forEach(this.subscribers()[event_name], function (subscriber) { 307 | if (subscriber) { 308 | subscriber.apply(this, event_args); 309 | } 310 | }); 311 | } 312 | }); 313 | IG.provide('Event', IG.EventProvider); 314 | IG.provide('XD', { 315 | _origin: null, 316 | _transport: null, 317 | _callbacks: {}, 318 | init: function () { 319 | if (IG.XD._origin) { 320 | IG.log('XD.init called with "XD._origin" already set. Returning.'); 321 | return; 322 | } 323 | 324 | if (window.addEventListener && !window.attachEvent && window.postMessage) { 325 | IG.log('Using "postmessage" as XD transport.'); 326 | IG.XD._origin = (window.location.protocol + '//' + window.location.host + '/' + IG.guid()); 327 | IG.XD.PostMessage.init(); 328 | IG.XD._transport = 'postmessage'; 329 | } else { 330 | IG.log('Using "fragment" as XD transport.'); 331 | IG.XD._transport = 'fragment'; 332 | IG.XD.Fragment._channelUrl = window.location.toString(); 333 | } 334 | }, 335 | resolveRelation: function (relation) { 336 | var relation_chain = relation.split('.'), 337 | root = window, 338 | frame_match, 339 | i; 340 | 341 | for (i = 0, num_relations = relation_chain.length; i < num_relations; i++) { 342 | child = relation_chain[i]; 343 | if (child === 'opener' || child === 'parent' || child === 'top') { 344 | root = root[child]; 345 | } else { 346 | frame_match = /^frames\[['"]?([a-zA-Z0-9-_]+)['"]?\]$/.exec(child); 347 | if (frame_match) { 348 | root = root.frames[frame_match[1]]; 349 | } else { 350 | throw new SyntaxError('Malformed relation to resolve: ' + relation + ', pt: ' + child); 351 | } 352 | } 353 | } 354 | 355 | return root; 356 | }, 357 | handler: function (callback, relation) { 358 | if (window.location.toString().indexOf(IG.XD.Fragment._magic) > 0) { 359 | return 'javascript:false;//'; 360 | } 361 | 362 | var proxy_url = IG.getDomain('https_com') + 'oauth/xd_proxy/#', 363 | callback_guid = IG.guid(); 364 | 365 | if (IG.XD._transport === 'fragment') { 366 | proxy_url = IG.XD.Fragment._channelUrl; 367 | var hash_index = proxy_url.indexOf('#'); 368 | if (hash_index > 0) { 369 | proxy_url = proxy_url.substr(0, hash_index); 370 | } 371 | proxy_url += ((proxy_url.indexOf('?') < 0 ? '?' : '&') + IG.XD.Fragment._magic + '#?=&'); 372 | } 373 | 374 | IG.XD._callbacks[callback_guid] = callback; 375 | return proxy_url + IG.QS.encode({ 376 | cb: callback_guid, 377 | origin: IG.XD._origin, 378 | relation: relation || 'opener', 379 | transport: IG.XD._transport 380 | }); 381 | }, 382 | recv: function (data) { 383 | if (typeof data === 'string') { 384 | data = IG.QS.decode(data); 385 | } 386 | var callback = IG.XD._callbacks[data.cb]; 387 | delete IG.XD._callbacks[data.cb]; 388 | if (callback) { 389 | callback(data); 390 | } 391 | }, 392 | PostMessage: { 393 | init: function () { 394 | var handler = IG.XD.PostMessage.onMessage; 395 | if (window.addEventListener) { 396 | window.addEventListener('message', handler, false); 397 | } else { 398 | window.attachEvent('onmessage', handler); 399 | } 400 | }, 401 | onMessage: function (event) { 402 | IG.XD.recv(event.data); 403 | } 404 | }, 405 | Fragment: { 406 | _magic: 'ig_xd_fragment', 407 | checkAndDispatch: function () { 408 | var url = window.location.toString(), 409 | fragment = url.substr(url.indexOf('#') + 1), 410 | magic_pos = url.indexOf(IG.XD.Fragment._magic); 411 | 412 | if (magic_pos > 0) { 413 | IG.init = function() {}; 414 | document.documentElement.style.display = 'none'; 415 | IG.XD.resolveRelation(IG.QS.decode(fragment).relation).IG.XD.recv(fragment); 416 | } 417 | } 418 | } 419 | }); 420 | IG.XD.Fragment.checkAndDispatch(); 421 | IG.provide('', { 422 | ui: function (options, callback) { 423 | var prepared_options = IG.UIServer.prepareCall(options, callback); 424 | if (!prepared_options) { 425 | IG.log('"prepareCall" failed to return options'); 426 | return; 427 | } 428 | 429 | var display = prepared_options.params.display; 430 | var display_method = IG.UIServer[display]; 431 | if (!display_method) { 432 | IG.log('"display" must be "popup"'); 433 | return; 434 | } 435 | 436 | display_method(prepared_options); 437 | } 438 | }); 439 | IG.provide('UIServer', { 440 | Methods: {}, 441 | _active: {}, 442 | _defaultCb: {}, 443 | prepareCall: function (options, callback) { 444 | var method_name = options.method.toLowerCase(), 445 | method = IG.UIServer.Methods[method_name], 446 | popup_id = IG.guid(); 447 | 448 | IG.copy(options, { 449 | client_id: IG._client_id, 450 | access_token: (IG._session && IG._session.access_token) || undefined 451 | }); 452 | 453 | options.display = IG.UIServer.getDisplayMode(method, options); 454 | 455 | var prepared_options = { 456 | callback: callback, 457 | id: popup_id, 458 | size: method.size || {}, 459 | url: method.url, 460 | params: options 461 | }; 462 | 463 | if (method.transform) { 464 | prepared_options = method.transform(prepared_options); 465 | if (!prepared_options) { 466 | IG.log('Call to "transform" in "prepareCall" failed to return options'); 467 | return; 468 | } 469 | } 470 | 471 | var relation = IG.UIServer.getXdRelation(prepared_options.params.display); 472 | 473 | prepared_options.params.redirect_uri = IG.UIServer._xdResult(prepared_options.callback, prepared_options.id, relation, true); 474 | 475 | prepared_options.params = IG.JSON.flatten(prepared_options.params); 476 | var query_string = IG.QS.encode(prepared_options.params); 477 | if (query_string) { 478 | prepared_options.url += '?' + query_string; 479 | } 480 | 481 | return prepared_options; 482 | }, 483 | getDisplayMode: function (method, options) { 484 | if (options.display === 'hidden') { 485 | return 'hidden'; 486 | } 487 | return 'popup'; 488 | }, 489 | getXdRelation: function (display) { 490 | if (display === 'popup') { 491 | return 'opener'; 492 | } 493 | }, 494 | popup: function (options) { 495 | var screenX = typeof window.screenX !== 'undefined' ? window.screenX : window.screenLeft, 496 | screenY = typeof window.screenY !== 'undefined' ? window.screenY : window.screenTop, 497 | clientWidth = typeof window.outerWidth !== 'undefined' ? window.outerWidth : document.documentElement.clientWidth, 498 | clientHeight = typeof window.outerHeight !== 'undefined' ? window.outerHeight : (document.documentElement.clientHeight - 22), 499 | popupWidth = options.size.width, 500 | popupHeight = options.size.height, 501 | screenWidth = (screenX < 0) ? window.screen.width + screenX : screenX, 502 | popupX = parseInt(screenWidth + ((clientWidth - popupWidth) / 2), 10), 503 | popupY = parseInt(screenY + ((clientHeight - popupHeight) / 2.5), 10), 504 | popupFeatures = ('width=' + popupWidth + ',height=' + popupHeight + ',left=' + popupX + ',top=' + popupY + ',scrollbars=1,location=1,toolbar=0'); 505 | IG.log('opening popup: ' + options.id); 506 | IG.UIServer._active[options.id] = window.open(options.url, options.id, popupFeatures); 507 | }, 508 | hidden: function (options) { 509 | options.className = 'IG_UI_Hidden'; 510 | options.root = IG.Content.appendHidden(''); 511 | IG.UIServer._insertIframe(options); 512 | }, 513 | _insertIframe: function (options) { 514 | IG.UIServer._active[options.id] = false; 515 | 516 | var set_callback = function (callback) { 517 | if (IG.UIServer._active.hasOwnProperty(options.id)) { 518 | IG.UIServer._active[options.id] = callback; 519 | } 520 | }; 521 | 522 | IG.Content.insertIframe({ 523 | url: options.url, 524 | root: options.root, 525 | className: options.className, 526 | width: options.size.width, 527 | height: options.size.height, 528 | onload: set_callback 529 | }); 530 | }, 531 | _xdRedirectUriHandler: function (callback, guid, relation, set_default) { 532 | if (set_default) { 533 | IG.UIServer._defaultCb[guid] = callback; 534 | } 535 | return IG.XD.handler(function (response) { 536 | IG.UIServer._xdRecv(response, callback); 537 | }, relation) + '&frame=' + guid; 538 | }, 539 | _xdRecv: function (response, callback) { 540 | var win = IG.UIServer._active[response.frame]; 541 | try { 542 | if (win.close) { 543 | win.close(); 544 | } 545 | } catch (e) {} 546 | delete IG.UIServer._active[response.frame]; 547 | delete IG.UIServer._defaultCb[response.frame]; 548 | callback(response); 549 | }, 550 | _xdResult: function (callback, guid, relation, set_default) { 551 | return (IG.UIServer._xdRedirectUriHandler(function (response) { 552 | if (callback) { 553 | if (response.result) { 554 | callback(IG.JSON.parse(response.result)); 555 | } 556 | } 557 | }, guid, relation, set_default)); 558 | } 559 | }); 560 | IG.provide('', { 561 | getLoginStatus: function (callback, force_recheck) { 562 | if (!IG._client_id) { 563 | IG.log('IG.getLoginStatus() called before calling IG.init().'); 564 | return; 565 | } 566 | 567 | if (callback) { 568 | if (!force_recheck && IG.Auth._loadState === 'loaded') { 569 | callback({ 570 | status: IG._userStatus, 571 | session: IG._session 572 | }); 573 | return; 574 | } else { 575 | IG.Event.subscribe('IG.loginStatus', callback); 576 | } 577 | } 578 | 579 | if (!force_recheck && IG.Auth._loadState === 'loading') { 580 | return; 581 | } 582 | 583 | IG.Auth._loadState = 'loading'; 584 | 585 | var internal_callback = function (response) { 586 | IG.Auth._loadState = 'loaded'; 587 | IG.Event.fire('IG.loginStatus', response); 588 | IG.Event.clear('IG.loginStatus'); 589 | }; 590 | IG.ui({ 591 | method: 'auth.status', 592 | display: 'hidden' 593 | }, internal_callback); 594 | }, 595 | login: function (callback, options) { 596 | IG.ui(IG.copy({ 597 | display: 'popup', 598 | method: 'authorize' 599 | }, options || {}), callback); 600 | }, 601 | logout: function (callback) { 602 | IG.Auth.setSession(); 603 | } 604 | }); 605 | IG.provide('Auth', { 606 | setSession: function (session, status) { 607 | var did_login = !IG._session && session, 608 | did_logout = IG._session && !session, 609 | session_changed = did_login || did_logout || (IG._session && session && IG._session.access_token !== session.access_token), 610 | status_changed = status !== IG._userStatus; 611 | 612 | var session_wrapper = {session: session, status: status}; 613 | 614 | IG._session = session; 615 | IG._userStatus = status; 616 | 617 | if (session_changed && IG.Cookie.getEnabled()) { 618 | IG.Cookie.set(session); 619 | } 620 | 621 | if (status_changed) { 622 | IG.Event.fire('auth.statusChange', session_wrapper); 623 | } 624 | 625 | if (did_login) { 626 | IG.Event.fire('auth.login', session_wrapper); 627 | } 628 | 629 | if (did_logout) { 630 | IG.Event.fire('auth.logout', session_wrapper); 631 | } 632 | 633 | if (session_changed) { 634 | IG.Event.fire('auth.sessionChange', session_wrapper); 635 | } 636 | 637 | return session_wrapper; 638 | }, 639 | xdHandler: function (callback, guid, relation, set_default, user_status, session_object) { 640 | return IG.UIServer._xdRedirectUriHandler(IG.Auth.xdResponseWrapper(callback, user_status, session_object), guid, relation, set_default); 641 | }, 642 | xdResponseWrapper: function (callback, user_status, session_object) { 643 | return function (response) { 644 | try { 645 | session_object = IG.JSON.parse(response.session || null); 646 | session_object.scope = IG.JSON.parse(response.scope || null); 647 | } catch (e) {} 648 | 649 | if (session_object) { 650 | user_status = 'connected'; 651 | } 652 | 653 | var session = IG.Auth.setSession(session_object || null, user_status), 654 | additional_vars = ['code', 'error', 'error_reason', 'error_description']; 655 | 656 | if (response) { 657 | IG.Array.forEach(additional_vars, function (var_name) { 658 | session[var_name] = response[var_name] || null; 659 | }); 660 | } 661 | 662 | if (callback) { 663 | callback(session); 664 | } 665 | }; 666 | } 667 | }); 668 | IG.provide('UIServer.Methods', { 669 | 'authorize': { 670 | size: { 671 | width: 627, 672 | height: 326 673 | }, 674 | url: IG.getDomain('https_com') + 'oauth/authorize/', 675 | transform: function (options) { 676 | if (!IG._client_id) { 677 | IG.log('IG.login() called before claling IG.init().'); 678 | return; 679 | } 680 | 681 | if (IG._session) { 682 | var needs_authorization = false; 683 | IG.Array.forEach(options.params.scope, function (required_scope) { 684 | if (IG._session.scope.indexOf(required_scope) === -1) { 685 | needs_authorization = true; 686 | } 687 | }); 688 | 689 | if (!needs_authorization) { 690 | IG.log('IG.login() called when user is already connected.'); 691 | if (options.callback) { 692 | options.callback({ 693 | status: IG._userStatus, 694 | session: IG._session 695 | }); 696 | return; 697 | } 698 | } 699 | } 700 | 701 | options.callback = IG.Auth.xdResponseWrapper(options.callback); 702 | 703 | if (options.params.scope) { 704 | options.params.scope = options.params.scope.join(' '); 705 | } 706 | 707 | IG.copy(options.params, { 708 | response_type: "token" 709 | }); 710 | 711 | return options; 712 | } 713 | }, 714 | 'auth.status': { 715 | url: IG.getDomain('https_com') + 'oauth/login_status/', 716 | transform: function (options) { 717 | var callback = options.callback, 718 | id = options.id; 719 | 720 | delete options.callback; 721 | 722 | IG.copy(options.params, { 723 | no_session: IG.Auth.xdHandler(callback, options.id, 'parent', false, 'notConnected'), 724 | no_user: IG.Auth.xdHandler(callback, options.id, 'parent', false, 'unknown'), 725 | ok_session: IG.Auth.xdHandler(callback, options.id, 'parent', false, 'connected') 726 | }); 727 | 728 | return options; 729 | } 730 | } 731 | }); 732 | IG.provide('Cookie', { 733 | _domain: null, 734 | _enabled: false, 735 | setEnabled: function (enabled) { 736 | IG.Cookie._enabled = enabled; 737 | }, 738 | getEnabled: function () { 739 | return IG.Cookie._enabled; 740 | }, 741 | load: function () { 742 | var cookie_match = document.cookie.match('\\bigs_' + IG._client_id + '="([^;]*)\\b'), 743 | value; 744 | 745 | if (cookie_match) { 746 | value= IG.QS.decode(cookie_match[1]); 747 | if (value.expires) { 748 | value.expires = parseInt(value.expires, 10); 749 | } 750 | IG.Cookie._domain = value.base_domain; 751 | } 752 | 753 | return value; 754 | }, 755 | setRaw: function (value, timestamp, domain) { 756 | document.cookie = 'igs_' + IG._client_id + '="' + value + '"' + (value && timestamp === 0 ? '' : '; expires=' + new Date(timestamp * 1000).toGMTString()) + '; path=/' + (domain ? '; domain=.' + domain : ''); 757 | IG.Cookie._domain = domain; 758 | }, 759 | set: function (value) { 760 | if (value) { 761 | IG.Cookie.setRaw(IG.QS.encode(value), value.expires, value.base_domain); 762 | } else { 763 | IG.Cookie.clear(); 764 | } 765 | }, 766 | clear: function () { 767 | IG.Cookie.setRaw('', 0, IG.Cookie._domain); 768 | } 769 | }); 770 | IG.provide('', { 771 | init: function (settings) { 772 | settings = IG.copy(settings || {}, { 773 | logging: false, 774 | check_status: true 775 | }); 776 | 777 | IG._client_id = settings.client_id; 778 | IG._logging = settings.logging || (typeof settings.logging === 'undefined' && window.location.toString().indexOf('ig_debug=1') > 0); 779 | 780 | IG.XD.init(); 781 | 782 | if (IG._client_id) { 783 | IG.Cookie.setEnabled(settings.cookie); 784 | 785 | settings.session = settings.session || (settings.cookie && IG.Cookie.load()); 786 | IG.Auth.setSession(settings.session); 787 | if (settings.check_status) { 788 | IG.getLoginStatus(); 789 | } 790 | } 791 | } 792 | }); 793 | window.setTimeout(function(){ 794 | if (window.igAsyncInit && !window.igAsyncInit.hasRun) { 795 | window.igAsyncInit.hasRun = true; 796 | igAsyncInit(); 797 | } 798 | }, 0); 799 | } --------------------------------------------------------------------------------