Created
May 23, 2014 10:50
-
-
Save aulianov/81447ba6a22734ba442f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Vkontakte JavaScript API wrapper. | |
* | |
* This script meant for simplify development of Vkontakte applications | |
* using Vkontakte API. Using vk_api() object you can call API methods, | |
* get HTTP-GET parameters (aka flashVars) in a simple way. You don't need | |
* to calculate signature parameter, don't need to form request string, | |
* don't need to implement JSONP-communication. You just connect this script | |
* to HTML-page using <script> tag, call one function to initialize object | |
* and one function to perform API-request! | |
* | |
* | |
* Changelog | |
* 1.5.4 | |
* (+) Added field SETT_STATUS to use it in showSettingsBox() function. | |
* | |
* 1.5.3 | |
* (f) Fixed bug with blending data when call API-methods multiple times. | |
* (f) Fixed removeCallback() bug. | |
* | |
* 1.5.2 | |
* == Special thanks to <Дмитрий Разумовский> == | |
* (f) Fixed bug with getting cookies in IE 6 and 7. | |
* | |
* 1.5.1 | |
* (+) Now GET-parameters of application properly transmitted from the first | |
* page of application to all other pages you can open. So you needn't | |
* to store all values manually and you can just use hyperlinks. | |
* (i) Some changes in code to make it more readable and strong (thanks to | |
* <Иванн Елизарьев> and his vk_api.js based library). | |
* | |
* 1.4.4 | |
* (f) Fixed IE bug with installation page. | |
* | |
* 1.4.3 | |
* (i) Now md5.js called from vk.com so it will work even if domain | |
* vkontakte.ru denied by your admin. | |
* | |
* 1.4.2 | |
* == Special thanks to <Андреенко Артём> == | |
* (f) Fixed decodeSecret() bug in some browsers. | |
* | |
* 1.4.1 | |
* == Special thanks to <Timofey Koolin> == | |
* (f) Fixed bug when first API request used and XML data requested. Now | |
* if data came in XML in params.api_result, vk_api leaves it unparsed. | |
* | |
* 1.4 | |
* == Special thanks to <Лёшка JIEXA Арсеньев> == | |
* (+) Methods .makeInstall() and .makeSettings() returns after being | |
* completely rewritten. Now these methods don't block entire page but | |
* only prevent application to be used showing correct vkontakte-style | |
* message. | |
* (i) Now md5 function loaded from vkontakte.ru site, so library became | |
* smaller. | |
* (i) Multiple little bugs fixed and some little optimisations applied. | |
* | |
* 1.3 | |
* (-) Removed method .makeInstall(). | |
* (-) Removed method .makeSettings(). | |
* (i) Now if there is first API call, then it parsed in params.api_result | |
* and you can deal with object ready to use. Of course JSON should be | |
* use in first requert. | |
* (i) Callbacks queue optimised. | |
* | |
* 1.2.1 | |
* (f) Fixed bug which caused errors like 'VK is not defined'. | |
* | |
* 1.2 | |
* (f) Methods .makeInstall() and .makeSettings() can now correctly work | |
* one after another. | |
* (i) Now all event handlers are organised in queue so you can set multiple | |
* handlers to one event. | |
* (i) Method .addCallback() now returns handler ID. | |
* (i) Method .removeCallback() now gets event handler ID as the second | |
* parameter. | |
* | |
* 1.1 | |
* (+) Method .makeInstall() to force user to install application added. | |
* (+) Method .makeSettings() to force user to set appropriate settings | |
* added. | |
* (+) Set of constants .SETT_* added to simplify settings requesting. | |
* | |
* 1.0 | |
* (i) Release | |
* | |
* | |
* @author Alexander Zubakov <[email protected]> | |
* @copyright © 2010 Alexander Zubakov | |
* @version 1.5.1 | |
* @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License | |
*/ | |
/** | |
* Create new object to call Vkontakte API throurh it. | |
* | |
* @param String api_secret Application sercet (can be changed on | |
* application edit page). | |
* @param Function oninit Function to perform after API initialised. | |
* @param Function onfailure Function to perform if API initialisation | |
* fails for msome reason. | |
* @param boolean test_mode If set to true then all API requests will be | |
* executed in test mode - additional parameter | |
* test_mode=1 will be automatically added to every | |
* API request, so you needn't do it manually. | |
*/ | |
function vk_api(api_secret, oninit /* [, onfailure] [, test_mode] */) { | |
/****************** Private Constants and variables *******************/ | |
/** | |
* URL of external JavaScript-file we need to call first of all. | |
* @var String | |
*/ | |
var vk_api_initializer = 'http://vk.com/js/xd_connection.js'; | |
/** | |
* URl of vkontakte.ru MD5 function. | |
* @var String | |
*/ | |
var vk_api_md5 = 'http://vk.com/js/lib/md5.js'; | |
/** | |
* Cookie prefix to make them unique. | |
* @var String | |
*/ | |
var ckpref = '__vkjsapi_'; | |
/** | |
* List of all accessible GET-parameters names. | |
* var Array | |
*/ | |
var params_list = [ | |
'api_url', 'api_id', 'user_id', 'group_id', | |
'viewer_id', 'is_app_user', 'viewer_type', 'auth_key', 'referrer', | |
'language', 'api_result', 'api_settings', 'parent_language', 'lc_name' | |
] | |
/** | |
* Message to make user install application. | |
* @var String | |
*/ | |
var msg_install = 'Для работы приложения его необходимо <a href="#" id="vk_api_msga">установить</a>'; | |
/** | |
* Message to meke user set settings to application. | |
* @var String | |
*/ | |
var msg_settings = 'Для работы приложения необходимо установить требуемые <a href="#" id="vk_api_msga">настройки</a>'; | |
/** | |
* This callback function is the third parameter of vk_api and executes | |
* if VK initialisation fails. | |
* @var Function | |
*/ | |
var onfailure = null; | |
/** | |
* Version of vkontakte API | |
* @var String | |
*/ | |
var api_version = '2.0'; | |
/** | |
* Test mode flag. If true then test_mode enabled | |
* @var Boolean | |
*/ | |
var test_mode = false; | |
/** | |
* Queue of callback functions used by addCallback() to perform correct | |
* processing of multiple events assigned in different places | |
* @var Object | |
*/ | |
var callbacks = {}; | |
/** | |
* Number of JSONP-callback function | |
* @var Number | |
*/ | |
var cb_index = 0; | |
/** | |
* Hack to use 'this' reference in callback functions in private methods | |
* @var Object | |
*/ | |
var this_proxy = this; | |
/***************** /Private Constants and variables *******************/ | |
/************************** Public Constants **************************/ | |
/** | |
* There is set of settings constants to modify application permissions | |
* using makeSettings() or external.showSettingsBox() methods. | |
* @const Number | |
*/ | |
this.SETT_NOTIFY = 1; //allow to send notifications | |
this.SETT_FRIENDS = 2; //add access to friends | |
this.SETT_PHOTOS = 4; //add access to photos | |
this.SETT_AUDIO = 8; //add access to audio | |
this.SETT_OFFER = 32; //add access to offers | |
this.SETT_QUESTIONS = 64; //add access to questions | |
this.SETT_WIKI = 128; //add access to Wiki-pages | |
this.SETT_MENU = 256; //add access to left menu | |
this.SETT_WALL = 512; //add access to user wall | |
this.SETT_STATUS = 1024; //add access to user status | |
/************************* /Public Constants **************************/ | |
/************************* Public Properties **************************/ | |
/** | |
* Parameters, which were sent to application through request string | |
* @var Object | |
*/ | |
this.params = {}; | |
/************************ /Public Properties **************************/ | |
/****************************** Methods *******************************/ | |
/**** Private: ****/ | |
/** | |
* Sort key-value pairs in objects by alphaber in ascending order. | |
* | |
* @param Object obj Object which keys to sort. | |
* @return Object Object with the same set key-value pairs as 'obj' but | |
* sorted in ascending order. | |
*/ | |
function sortByKey(obj) { | |
//make Array from object keys | |
var keys = new Array(); | |
for (var k in obj) { | |
keys.push(k); | |
} | |
//sort Array | |
keys.sort(); | |
//form new object with keys sorted alphabetically | |
var sortedObj = {}; | |
for (var i = 0; i < keys.length; i++) { | |
sortedObj[keys[i]] = obj[keys[i]]; | |
} | |
return sortedObj; | |
} | |
/** | |
* Perform XSS-request (load JavaScript). | |
* | |
* @param String url URL of JavaScript file to load and execute in | |
* context of this page. | |
*/ | |
function requestScript(url) { | |
var script = document.createElement('script'); | |
script.type = 'text/javascript'; | |
script.src = url; | |
document.getElementsByTagName('head')[0].appendChild(script); | |
} | |
/** | |
* Decode api_secret if it is encoded using special algorithm accessible | |
* at http://xinit.ru/vk_app_secret/ | |
* | |
* @param String api_secret Application secret from application edit | |
* page. If it is encoded then function | |
* performs decoding. If secret is not encoded | |
* then function returns api_secret itself. | |
* @return String Decoded application secret. | |
*/ | |
function decodeSecret(api_secret) { | |
//decode api_secret | |
if (api_secret.substring(0, 4) == 'sx--') { | |
var api_secret_decoded = ''; | |
var char_code = ''; | |
for (var i = 4; i < api_secret.length; i++) { | |
//form char code | |
if (api_secret.charAt(i) != 'l') { | |
char_code += api_secret.charAt(i); | |
//there is full char_code | |
} else { | |
api_secret_decoded += String.fromCharCode(parseInt(char_code) + 31); | |
char_code = ''; | |
} | |
} | |
return api_secret_decoded; | |
//api_secret is not encoded, so just return it | |
} else { | |
return api_secret; | |
} | |
} | |
/** | |
* Makes object to react on events. | |
* | |
* @param string Type of event to handle | |
* @param Function Function to call when event occurs. | |
* @param boolean Flag shows if we need to capture event. | |
* @return boolean true, if event is successfully set, false otherwise. | |
*/ | |
function addEvent(obj, eventType, callback /* [, useCapture] */) { | |
//set default value | |
useCapture = false; | |
if (arguments.length > 2) { | |
useCapture = arguments[2]; | |
} | |
//try to use DOM possibility | |
if (typeof obj.addEventListener != 'undefined') { | |
obj.addEventListener(eventType, callback, useCapture); | |
return true; | |
//use IE capability | |
} else if (typeof obj.attachEvent != 'undefined') { | |
return obj.attachEvent('on' + eventType, callback); | |
} | |
} | |
/** | |
* Get cookie variable. | |
* | |
* @param string name Name of cookie variable to get. | |
* @return string Value of the variable we got. | |
*/ | |
function getcookie(name) { | |
if (document.cookie.length > 0) { | |
c_start = document.cookie.indexOf(name + '='); | |
if (c_start != -1) { | |
c_start = c_start + name.length + 1; | |
c_end = document.cookie.indexOf(";", c_start); | |
if (c_end == -1) { | |
c_end = document.cookie.length; | |
} | |
return unescape(document.cookie.substring(c_start, c_end)); | |
} | |
} | |
//no variable with given name found | |
return undefined; | |
} | |
/** | |
* Set cookie with given name and value. | |
* | |
* @param String name Name of cookie variable. | |
* @param String value Value of variable. | |
*/ | |
function setcookie(name, value) { | |
//set cookie | |
document.cookie = name + '=' + escape(value); | |
} | |
/** | |
* Load parameters from document URL or from cookies if there is no | |
* GET-parameters. | |
* | |
* @param String url URL of the page to load API parameters from. | |
*/ | |
function loadParams(url) { | |
VK.loadParams(document.location.href); | |
//there are GET-parameters | |
if (typeof VK.params.api_id != 'undefined') { | |
//get parameters | |
this_proxy.params = VK.params; | |
//add all parameters to cookies | |
for (var p in params_list) { | |
if (typeof this_proxy.params[params_list[p]] != 'undefined') { | |
setcookie(ckpref + params_list[p], this_proxy.params[params_list[p]]); | |
} | |
} | |
//there are no GET-parameters, so try to get them from cookies | |
} else { | |
this_proxy.params = {}; | |
//load all parameters from cookies | |
for (var p in params_list) { | |
//load parameter from cookies | |
this_proxy.params[params_list[p]] = getcookie(ckpref + params_list[p]); | |
//if there is no such parameter then remove it from parameters | |
if (typeof this_proxy.params[params_list[p]] == 'undefined') { | |
delete this_proxy.params[params_list[p]]; | |
} | |
} | |
} | |
} | |
/** | |
* Show message which don't allow user to use application. Used if user | |
* hasn't installed application or set wrong settins. | |
* | |
* @param String msg Message text to show. | |
* @param Number settings Settings value application need user to set. | |
*/ | |
function showMessage(msg /* , settings */) { | |
//define settings if this is 'wrong settings' message | |
var settings = -1; | |
if (arguments.length > 1) { | |
settings = arguments[1]; | |
} | |
//show message | |
if (document.getElementById('vk_api_msgbox') == null) { | |
//create new message box | |
document.getElementsByTagName('body')[0].innerHTML += | |
'<div id="vk_api_msgbox" style="' + | |
'position: absolute;' + | |
'width: 100%; height: 100%;' + | |
'margin: 0; padding: 50px 0 0 0;' + | |
'border-top: #dbe2e8 1px solid;' + | |
'top: 0; left: 0;' + | |
'z-index: 100;' + | |
'background: white; color: gray;' + | |
'font-size:12px;' + | |
'text-align:center;' + | |
'">' + | |
msg + | |
'</div>'; | |
} else { | |
//just change text in existing message box | |
document.getElementById('vk_api_msgbox').innerHTML = msg; | |
document.getElementById('vk_api_msgbox').style.display = 'block'; | |
} | |
//create onclick handler | |
addEvent( | |
document.getElementById('vk_api_msga'), | |
'click', | |
function() { | |
//this is 'wrong settings' message | |
if (settings >= 0) { | |
this_proxy.external.showSettingsBox(settings); | |
//this is 'not installed' message | |
} else { | |
this_proxy.external.showInstallBox(); | |
} | |
}, | |
true | |
); | |
} | |
/** | |
* Hide message previously shown with showMessage(). | |
*/ | |
function hideMessage() { | |
if (document.getElementById('vk_api_msgbox') != null) { | |
document.getElementById('vk_api_msgbox').style.display = 'none'; | |
} | |
} | |
/**** Public: ****/ | |
/** | |
* Ask user to install application (add to their page) in indefinite | |
* loop until user finally installs application. | |
* Warning! Application will ask user to install application until he | |
* did it, so if user don't want to add application to his page, he | |
* can't run application. | |
* | |
* @param Function ifInstalled Callback function which executed if and | |
* only if application is installed. First | |
* time function executes if user installs | |
* application after makeInstall() called. | |
* But unlike | |
* addCallback('onApplicationAdded', ...), | |
* this function executes even if | |
* application already added before | |
* makeInstall() called. This means that if | |
* you use makeInstall(fucnName) then | |
* funcName() will be executed every time | |
* user runs application, even if he | |
* installed application only once long time | |
* ago. | |
*/ | |
this.makeInstall = function(/* ifInstalled */) { | |
//define if there is ifInstalled callback | |
var appInstalled = null; | |
if (arguments.length > 0) { | |
appInstalled = arguments[0]; | |
} | |
//current application settings and settings we need are different so | |
//show installation window | |
if (this_proxy.params.is_app_user != 1) { | |
//hide entire application with special message | |
showMessage(msg_install); | |
this_proxy.external.showInstallBox(); | |
//when user adds application | |
this_proxy.addCallback( | |
'onApplicationAdded', | |
function() { | |
//set new settings as current and verify if settings are | |
//correct again | |
this_proxy.params.is_app_user = 1; | |
hideMessage(); | |
//application added so run appInstalled | |
if (appInstalled != null) appInstalled(); | |
} | |
); | |
//application is already installed, so just run callback | |
} else { | |
if (appInstalled != null) appInstalled(); | |
} | |
} | |
/** | |
* Ask user to set appropriate settings and wait for user to do it. | |
* Warning! Application will ask user to specify settings until he did | |
* it, so if user don't want to set your settings, he can't run | |
* application. | |
* | |
* @param Number settings Settings you want user to set to application. | |
* You can use either integer value according to | |
* official Vkontakte documentation on | |
* http://vkontakte.ru/page7002134 or combination | |
* of .SETT_* constants. | |
*/ | |
this.makeSettings = function(settings) { | |
//current application settings and settings we need are different so | |
//show settings-change window | |
if ((this_proxy.params.api_settings & settings) != settings) { | |
//hide entire application with special message | |
showMessage(msg_settings, settings); | |
this_proxy.external.showSettingsBox(settings); | |
} | |
//when settings changed | |
this_proxy.addCallback( | |
'onSettingsChanged', | |
function(new_settings) { | |
//set new settings as current and verify if settings are | |
//correct again | |
this_proxy.params.api_settings = new_settings; | |
//current application settings and settings we need are the | |
//same so hide message window | |
if ((this_proxy.params.api_settings & settings) == settings) { | |
hideMessage(); | |
} | |
} | |
); | |
} | |
/** | |
* Call Vkontakte API method. | |
* | |
* @param String method Method name to execute. | |
* @param Object parameters Parameters of API method in the following | |
* form: | |
* { | |
* name1: 'value 1', | |
* name2: 'value 2', | |
* | |
* //arrays are also allowed | |
* name3: [ | |
* 'array_value_1', | |
* 'array_value_2', | |
* 'array_value_3' | |
* ] | |
* } | |
* @param Function onData Callback function to perform when answer from | |
* API call comes from server. Function has the | |
* only parameter - object with method call | |
* results. | |
*/ | |
this.call = function(method /* [, parameters] [, onData] */) { | |
//onData callback | |
var onData = null; | |
//onData is the second parameter | |
if ((arguments.length >= 2) && (typeof arguments[1] == 'function')) { | |
onData = arguments[1]; | |
//onData is the third parameter | |
} else if ((arguments.length >= 3) && (typeof arguments[2] == 'function')) { | |
onData = arguments[2]; | |
} | |
//callback name | |
var cb_name = ckpref + 'cbfunk_' + (++cb_index); | |
/** | |
* Performs when browser gets server response to parse response and | |
* call onData function. Located in widdow scope to be accessible | |
* for loaded script. | |
* | |
* @param Object json JSON object which browser got from server, | |
* already evaluated and ready for using. | |
*/ | |
window[cb_name] = function(json) { | |
//call onData callback function | |
if (onData != null) { | |
onData(json); | |
} | |
}; | |
//parameters to send to server | |
var parameters = {}; | |
if ((arguments.length >= 2) && (typeof arguments[1] == 'object')) { | |
parameters = arguments[1]; | |
} | |
//append parameters with common strings | |
parameters.api_id = this_proxy.params.api_id; | |
parameters.v = api_version; | |
parameters.format = 'json'; | |
parameters.callback = cb_name; | |
//set test mode | |
if (test_mode) parameters.test_mode = '1'; | |
//append parameters with method name | |
parameters.method = method; | |
//sort parameters | |
parameters = sortByKey(parameters); | |
//calculate API signature and add it to parameters list | |
var sig = this_proxy.params.viewer_id; | |
for (var k in parameters) { | |
//can be array | |
if (typeof parameters[k] == 'object') { | |
sig += k + '=' + parameters[k].join(','); | |
//ordinar parameter | |
} else { | |
sig += k + '=' + parameters[k]; | |
} | |
} | |
sig += decodeSecret(api_secret); | |
parameters.sig = MD5(sig); | |
//form URL to call | |
var url = this_proxy.params.api_url + '?'; | |
for (var k in parameters) { | |
url += k + '=' + encodeURIComponent(parameters[k]) + '&'; | |
} | |
//load and execute script | |
requestScript(url); | |
} | |
/***************************** /Methods *******************************/ | |
/************************ Initialize object ***************************/ | |
//onfailure() function is set as the third parameter of vk_api() | |
if ((arguments.length >= 3) && (typeof arguments[2] == 'function')) { | |
onfailure = arguments[2]; | |
} | |
//test_mode is set as the third parameter of vk_api() | |
if ((arguments.length >= 3) && (typeof arguments[2] == 'boolean')) { | |
test_mode = arguments[2]; | |
//test_mode is set as the fourth parameter of vk_api() | |
} else if ((arguments.length >= 4) && (typeof arguments[3] == 'boolean')) { | |
test_mode = arguments[3]; | |
} | |
//request MD5 function from vkontakte | |
requestScript(vk_api_md5); | |
//first of all we need to load special library from vk.com | |
requestScript(vk_api_initializer); | |
//wait to 'vk_api_initializer' to load | |
var VKLOAD_intervel = setInterval( | |
vkapiInit, | |
100 | |
); | |
/** | |
* Perform when 'vk_api_initializer' loaded. | |
*/ | |
function vkapiInit() { | |
if ((typeof VK != 'undefined') && (typeof MD5 != 'undefined')) { | |
clearInterval(VKLOAD_intervel); | |
//initialize VK object | |
VK.init( | |
/** | |
* Perform on successful init. | |
*/ | |
function() { | |
//get VK.params and translate it to vk_api.params | |
//VK.loadParams(document.location.href); | |
//this_proxy.params = VK.params; | |
loadParams(document.location.href); | |
//parse json response from the first api call result if | |
//it is not in XML | |
if ( | |
(typeof this_proxy.params.api_result != 'undefined') && | |
(this_proxy.params.api_result.indexOf('<?xml') != 0) | |
) { | |
this_proxy.params.api_result = eval('(' + this_proxy.params.api_result + ')'); | |
} | |
//map VK.External methods to vk_api.external | |
this_proxy.external = VK.External; | |
/** | |
* Call external method. | |
* | |
* @param String method Method name to call. You can | |
* call methods from external.* | |
* (VK.External.*) with proper | |
* parameters. | |
*/ | |
this_proxy.callMethod = function(method /*, ...*/) { | |
VK.callMethod.apply(VK, arguments); | |
} | |
/** | |
* Add callback function. | |
* | |
* @param String name Name of event to which callback | |
* will be added. | |
* @param Function callback Callback function to run | |
* after event 'name' occurs. | |
* @return Number ID of assigned callback function. | |
*/ | |
this_proxy.addCallback = function(name, callback) { | |
//add callback to the queue | |
if (typeof callbacks[name] == 'undefined') { | |
callbacks[name] = new Array(); | |
} | |
callbacks[name].push(callback); | |
//add callback using VK | |
VK.addCallback( | |
name, | |
/** | |
* Function uses callbacks queue to call all | |
* functions that were added using addCallback() | |
* in order it were added. | |
*/ | |
function() { | |
//call every callback in the queue | |
for (var c in callbacks[name]) { | |
//call callback if it is not deleted | |
if (callbacks[name][c] != null) { | |
callbacks[name][c].apply(null, arguments); | |
} | |
} | |
} | |
); | |
//return so called ID of assigned callback function | |
//to use it in removeCallback() function | |
return callbacks[name].length - 1; | |
}; | |
/** | |
* Remove callback function from the queue. | |
* | |
* @param String name Name of event from which callback | |
* will be removed. | |
* @param Number callback_id ID of callback function to | |
* remove from queue. | |
*/ | |
this_proxy.removeCallback = function(name, callback_id) { | |
//do not remove callback using low-level function | |
//VK.removeCallback(name, callback); | |
//remove function from the queue | |
//really just replace it with empty function | |
callbacks[name].splice(callback_id, 1, null); | |
}; | |
//execute user initialisation function | |
oninit(); | |
}, | |
/** | |
* Perform on init failure. | |
*/ | |
function() { | |
if (onfailure != null) { | |
onfailure(); | |
} | |
} | |
); | |
} | |
} | |
/*********************** /Initialize object ***************************/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment