app.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. jQuery( document ).ready(function( $ ) {
  2. var wpConsole = wpConsole || {};
  3. $.ajax( { url: 'config.json' } ).done( function( config ) {
  4. wpConsole.access_token = $.cookie('access_token');
  5. wpConsole.site_id = $.cookie('site_id');
  6. var params = QueryStringToHash( location.hash.substr(1) );
  7. if ( params.access_token ) {
  8. $.cookie('access_token', params.access_token );
  9. $.cookie('site_id', params.site_id );
  10. wpConsole.access_token = params.access_token;
  11. wpConsole.site_id = params.site_id;
  12. }
  13. var c = config;
  14. var auth = osmAuth({
  15. oauth_consumer_key: config.client_key,
  16. oauth_secret: config.client_secret,
  17. url: config.api_url,
  18. urls: {
  19. request: config.site_url + "/oauth1/request",
  20. authorize: config.site_url + "/oauth1/authorize",
  21. access: config.site_url + "/oauth1/access",
  22. },
  23. // singlepage: true
  24. });
  25. window.auth = auth;
  26. var signRequest = function (opts) {
  27. };
  28. $( '#auth a' ).click( function(e) {
  29. e.preventDefault();
  30. auth.authenticate(function() {
  31. $( '#auth a' ).html( 'Authenticated!' );
  32. });
  33. // $.ajax(signRequest({
  34. // "url": config.api_url + "/oauth1/request",
  35. // }));
  36. // window.location = "https://public-api.wordpress.com/oauth1/authorize/?client_id=" + config.client_id + '&redirect_uri=' + encodeURIComponent ( config.redirect_uri ) + '&response_type=token';
  37. } );
  38. if ( auth.authenticated() ) {
  39. $( '#auth a' ).html( 'Authenticated!' );
  40. }
  41. var log = $('#requests'), // where the requests are listed
  42. initialWidth = $.cookie('panelWidth'), // save the width of the left panel
  43. panel = $('#panel'), // a reference to the left panel
  44. inputs = $('.inputs'), // all input divs
  45. resizing = false, // if the user is currently resizing the panel
  46. canResize = false, // if the user's mouse is in a location where resizing can be activated
  47. lastX = null, // track the last mouse x position
  48. resizeDetector = function(e){ // function to detect if the user can activate resizing
  49. var padding = 5, width = panel.width(), left = width, right = width + padding;
  50. if (lastX == e.pageX) return;
  51. lastX = e.pageX;
  52. if (lastX > left && lastX < right) {
  53. canResize = true;
  54. document.body.style.cursor = "ew-resize !important";
  55. } else {
  56. canResize = false;
  57. document.body.style.cursor = null;
  58. }
  59. },
  60. resizePerformer = function(e){ // attached as a mouse listener to perform resizing of the panel
  61. var min = 220, max = window.innerWidth - 400, width = Math.max(Math.min(max, e.pageX), min);
  62. e.preventDefault();
  63. panel.width(width);
  64. log.css({left:width});
  65. },
  66. $span = $( '<span/>' ),
  67. safeText = function( string ) {
  68. return $span.text( string ).html();
  69. };
  70. panel.width(initialWidth); // set the initial width of the panel, can come from cookie
  71. log.css({left:initialWidth+'px'}); // resize the log to fit with the panel
  72. $(window).bind('resize', function(){ // listen to resizes to scale the panel/log correctly
  73. var min = 220, max = window.innerWidth - 400, width = Math.max(Math.min(max, panel.width()), min);
  74. panel.width(width);
  75. log.css({left:width});
  76. $.cookie('panelWidth', panel.width());
  77. }).trigger('resize');
  78. $(document).bind('mousemove', resizeDetector); // track the position of the mouse
  79. $(document).bind('mousedown', function(e){ // determine if user is activating panel resize
  80. if(!canResize) return;
  81. e.preventDefault();
  82. resizing = true;
  83. $(document)
  84. .unbind('mousemove', resizeDetector)
  85. .bind('mousemove', resizePerformer);
  86. });
  87. $(document).bind('mouseup', function(e){ // stop resizing action and save the panel width
  88. if(!resizing) return;
  89. resizing = false;
  90. $.cookie('panelWidth', panel.width());
  91. $(document)
  92. .unbind('mousemove', resizePerformer)
  93. .bind('mousemove', resizeDetector);
  94. });
  95. // expand/collapse response views
  96. $('ul#requests').click(function(e){
  97. var $target = $(e.target),
  98. $h2 = $target.is('h2') ? $target : $target.parents('h2');
  99. if (!$h2.is('h2')) return;
  100. $li = $h2.parents('li');
  101. $li[$li.hasClass('expanded') ? 'removeClass' : 'addClass']('expanded');
  102. });
  103. // add expand/collapse interations to the response nodes
  104. var initializeResponse = function(object){
  105. object.find('ul.object, ul.array').each(function(){
  106. var $this = $(this),
  107. $li = $this.parent(),
  108. $disclosure = $("<a>&#x25B8;</a>").addClass('disclosure').click(function(){
  109. $li[$li.hasClass('closed') ? 'removeClass' : 'addClass']('closed');
  110. });
  111. $li.addClass('closed').append($disclosure);
  112. });
  113. return object;
  114. }
  115. // outputs a single line of JSON
  116. var rawResponse = function(object, formatter){
  117. return JSON.stringify(object, null, formatter);
  118. };
  119. // build the response HTML nodes from the parsed JSON object
  120. var formatResponse = function(object, keyPath){
  121. var node, li;
  122. if (!keyPath) keyPath = [];
  123. if (!object || object.constructor == Array && object.length == 0) return;
  124. if ('object' === typeof(object)) {
  125. node = $("<ul></ul>").addClass('object');
  126. li;
  127. for(field in object){
  128. li = $("<li>");
  129. li.append($("<span></span>").addClass('key').text(field)).append(' ');
  130. if ( object && object[field] && ('array' === typeof(object[field]) || Array == object[field].constructor)) {
  131. li
  132. .append(
  133. $("<span></span>")
  134. .text("Array["+object[field].length+"]")
  135. .addClass('hint')
  136. );
  137. } else if('object' === typeof(object[field])) {
  138. li
  139. .append($("<span></span>")
  140. .text("Object")
  141. .addClass('hint')
  142. );
  143. }
  144. li.append(formatResponse(object[field], keyPath.concat([field])));
  145. node.append(li);
  146. }
  147. } else {
  148. if (object === undefined) return $("<span>undefined</span>");;
  149. var str = object.toString(), matches;
  150. node = $('<span>').addClass('value').addClass(typeof(object)).text(str)
  151. if (matches = str.match("^" + config.api_url + "(/.*)")) { // IF it's an API URL make it clickable
  152. node.click(function(){
  153. $('form').trigger('reset');
  154. $('[name=path]').val(matches[1]).parents('form').trigger('submit');
  155. log.animate({scrollTop:'0'}, 'fast');
  156. }).addClass('api-url');
  157. } else if(str.match(/^https?:\/\/[^.]+\.gravatar\.com\/avatar/)){ // If it's an Avatar let's add an img tag
  158. node = $('<img>').attr( 'src', str ).addClass('avatar').add(node);
  159. }
  160. }
  161. return node;
  162. }
  163. // the path build
  164. var $pathField = $("[name=path]"),
  165. $queryField = $("[name=query]"),
  166. $bodyField = $("[name=body]"),
  167. helpTimer = null,
  168. formatHelp = function(variable, help){
  169. var $node = $helpBubble.children().first();
  170. $node.html("");
  171. $node.append(
  172. $("<h1></h1>")
  173. .append($("<code></code>").text(variable))
  174. .append(" ")
  175. .append($("<em></em>").text(help.type))
  176. );
  177. switch(help.description.constructor){
  178. case String:
  179. $node.append($("<p></p>").text(help.description));
  180. break;
  181. case Object:
  182. var $dl = $('<dl>').appendTo($node);
  183. $.each(help.description, function(key, val){
  184. $dl.append("<dt>" + safeText( key ) + "</dt><dd>" + safeText( val ) + "</dd>")
  185. })
  186. break;
  187. default:
  188. $node.append("<p><em>Not available</em></p>")
  189. break;
  190. }
  191. },
  192. objectBuilder = function(){ // creates an interface to allow the user to enter values for each key in an object, also shows hints if available
  193. var $builder = $("<div>").addClass('object-builder'), values ={}, keys = [], help = {};
  194. $builder.setObject = function(object, helpValues){
  195. $builder.html('');
  196. keys = []
  197. help = helpValues;
  198. $.each(object, function(key, value){
  199. if(keys.indexOf(key) == -1) keys.push(key);
  200. values[key] = object[key] ? object[key] : values[key];
  201. $builder.append(
  202. $("<div>")
  203. .addClass('object-property')
  204. .append(
  205. $('<div>').addClass('object-property-key').text(key).bind('click', function(e){
  206. $(this).next().focus();
  207. })
  208. )
  209. .append(
  210. $('<div>').addClass('object-property-value').attr('contenteditable', true).text(values[key] || "").bind('keyup', function(){
  211. values[key] = $(this).text();
  212. $builder.trigger('valuechanged', [key, values[key]]);
  213. }).bind('focus', function(){
  214. // show the help
  215. formatHelp(key, helpValues[key])
  216. $helpBubble.show().css({
  217. left:panel.width()+3,
  218. top: $(this).parent().offset().top - $helpBubble.height()*0.5 + $(this).parent().height()*0.5
  219. })
  220. }).bind('blur', function(){
  221. $helpBubble.hide();
  222. })
  223. )
  224. .hover(
  225. function(){
  226. var $row = $(this);
  227. helpTimer = setTimeout(function(){
  228. formatHelp(key, helpValues[key])
  229. $helpBubble.show().css({
  230. left:panel.width() + 3,
  231. top: $row.offset().top - $helpBubble.height()*0.5 + $row.height()*0.5
  232. });
  233. }, 1000);
  234. },
  235. function(){
  236. clearTimeout(helpTimer);
  237. $helpBubble.hide();
  238. }
  239. )
  240. )
  241. }); // each
  242. }
  243. var empty = function(item){
  244. return !item || item == "";
  245. }
  246. $builder.getObject = function(){
  247. var output = {};
  248. $.each(keys, function(index, key){
  249. if ( !empty(values[key]) ){
  250. output[key] = values[key];
  251. }
  252. });
  253. return output;
  254. }
  255. return $builder;
  256. },
  257. $pathBuilder = objectBuilder(),
  258. $queryBuilder = objectBuilder(),
  259. $bodyBuilder = objectBuilder();
  260. // $path, $query, $body
  261. $pathField.parents('div').first().append($pathBuilder);
  262. $queryField.parents('div').first().append($queryBuilder);
  263. $bodyField.parents('div').first().append($bodyBuilder);
  264. // the help bubble used by the object_builder to display contextual help information
  265. var $helpBubble = $('<div id="help-bubble"><div></div></div>');
  266. $helpBubble.appendTo(document.body).hide();
  267. inputs.bind('scroll', function(){
  268. $helpBubble.hide();
  269. });
  270. // load up the help docs from the API and build a clickable list of API endpoints
  271. var $reference = $('#reference'), $list = $reference.find('ul'), $fields = $('.inputs > div').not('#reference');
  272. $reference.append($("<div><div></div></div>").addClass('throbber'));
  273. $.ajax( { url: config.api_url + "/" } ).done( function( response ) {
  274. $reference.find('.throbber').remove();
  275. $.each(response.routes, function(index){
  276. var help = response.routes[index];
  277. var helpGroup = safeText( help.group ),
  278. group = $list.find('.group-'+helpGroup);
  279. if (!group.is('li')) group = $('<li><strong>'+helpGroup+'</strong><ul></ul></li>').addClass('group-'+helpGroup).appendTo($list);
  280. console.log(help);
  281. help.supports = help.supports || [];
  282. $.each(help.supports, function (method_index, method) {
  283. if (method === 'HEAD') {
  284. return;
  285. }
  286. group.find('ul').append(
  287. $('<li>')
  288. .append($('<span class="path-details"></span>').text(method + " " + index))
  289. // .append($('<span class="description"></span>').text(help.description))
  290. .click(function(e){
  291. var $target = $(e.target),
  292. $li = $target.is('li') ? $target : $target.parents('li').first()
  293. if($li.hasClass('selected')) return;
  294. $reference.find('li.selected').removeClass('selected');
  295. $li.addClass('selected');
  296. $.each({'path':$pathBuilder, 'query':$queryBuilder, 'body':$bodyBuilder}, function(prop, builder){
  297. var o = {};
  298. // $.each(help.request[prop], function(key, settings){
  299. // o[key] = ""
  300. // });
  301. // builder.setObject(o, help.request[prop]);
  302. });
  303. $pathField.val(index).trigger('keyup');
  304. $fields.find('.raw-toggle').removeClass('on').show().trigger('toggle', [false]);
  305. $fields.first().hide().find('select').val(help.supports[method]);
  306. $fields.last()[method == 'POST' ? 'show' : 'hide']();
  307. })
  308. )
  309. });
  310. });
  311. })
  312. // create a toggle so the object_builder can be replaced with a raw input field
  313. $fields.has('input, textarea').each(function(){
  314. var $field = $(this),
  315. $toggle = $('<span class="raw-toggle">Raw</span>')
  316. .click(function(){ $(this).toggleClass('on').trigger('toggle', [$(this).hasClass('on')]) })
  317. $field.append($toggle);
  318. $toggle
  319. .bind('toggle', function(e, on){
  320. $field.find('input, textarea')[on ? 'show' : 'hide']();
  321. $field.find('.object-builder')[on ? 'hide' : 'show']();
  322. })
  323. .hide()
  324. .addClass('on')
  325. .trigger('toggle', [true])
  326. });
  327. // clear the form
  328. $('form').bind('reset', function(){
  329. $fields.show();
  330. $fields.find('.raw-toggle').hide().addClass('on').trigger('toggle', [true]);
  331. $reference.find('.selected').removeClass('selected');
  332. })
  333. // replace a string with placeholders with the given values
  334. var interpolate = function(string, values){
  335. var string = string.slice();
  336. $.each(values, function(key, value){
  337. string = string.replace(key, value)
  338. })
  339. return string;
  340. }
  341. // User has submitted the form, build the request and send it
  342. $('form[action="#request"]').submit(function(e){
  343. e.preventDefault();
  344. // let's build a request;
  345. var req = {
  346. path: $(this.path).parent().siblings().is('.raw-toggle.on') ? this.path.value : interpolate(this.path.value, $pathBuilder.getObject()),
  347. method:this.method.value,
  348. data:$(this.body).parent().siblings().is('.raw-toggle.on') ? this.body.value : $bodyBuilder.getObject(),
  349. beforeSend: auth.ajaxBeforeSend
  350. };
  351. req.query = query = $(this.query).parent().siblings().is('.raw-toggle.on') ? this.query.value : $queryBuilder.getObject();
  352. console.log(query);
  353. req.url = config.api_url + req.path + '?' + $.param( query );
  354. request = $('<li></li>').prependTo(log).addClass('loading'),
  355. title = $("<h2><small>"+safeText( req.method )+"</small> "+safeText( req.path )+"</h2>").appendTo(request);
  356. var q;
  357. if (typeof(req.query) == 'object') {
  358. q = $.param(req.query);
  359. } else {
  360. q = req.query
  361. }
  362. if(typeof(q) == 'string' && q.trim() != "") {
  363. title.append($('<em>').text((q[0] == "?" ? "" : "?") + q))
  364. }
  365. throbber = $('<div><div></div></div>').addClass('throbber').appendTo(request),
  366. timer = (new Date).getTime();
  367. console.log('test');
  368. console.log(req);
  369. $.ajax( req ).done( function(response, statusCode){
  370. var responseTime = (new Date).getTime() - timer;
  371. request
  372. .append(
  373. $('<span class="response-meta"></span>')
  374. .append($('<span class="response-time"></span>').text(responseTime + " ms "))
  375. .append($('<span class="status-code"></span>').text(statusCode || "?").attr('data-status-code', statusCode || '4xx'))
  376. )
  377. setTimeout(function(){
  378. request.addClass('done').removeClass("loading");
  379. }, 10);
  380. var formats = $('<div>')
  381. .addClass('structured')
  382. .append(
  383. response ?
  384. initializeResponse(formatResponse(response)) :
  385. $('<span class="empty-response">empty response</span>')
  386. );
  387. formats = formats.add($('<pre>')
  388. .addClass('pretty')
  389. .text(rawResponse(response, "\t"))
  390. );
  391. formats = formats.add($('<pre>')
  392. .addClass('raw')
  393. .text(rawResponse(response))
  394. );
  395. $("<div>")
  396. .addClass("response")
  397. .append(formats.hide())
  398. .appendTo(request);
  399. $('<ul>')
  400. .addClass('tabs')
  401. .append("<li>Structured</li><li>Pretty</li><li>Raw</li>").click(function(e){
  402. var $this = $(this), $children = $this.children(), $li = $children.filter(e.target);
  403. $children.not($li).removeClass('selected');
  404. $li.addClass('selected');
  405. formats.hide().eq($children.index($li)).show();
  406. })
  407. .appendTo(request)
  408. .children().first().trigger('click');
  409. request.addClass('expanded');
  410. /* $.post( '<?php echo admin_url( 'admin-ajax.php' ); ?>', { action: 'console_request_performed', path: req.path, nonce: '<?php echo wp_create_nonce( 'console_request_km_nonce' ); ?>' } ); */
  411. });
  412. });
  413. });
  414. var QueryStringToHash = function QueryStringToHash (query) {
  415. var query_string = {};
  416. var vars = query.split("&");
  417. for (var i=0;i<vars.length;i++) {
  418. var pair = vars[i].split("=");
  419. pair[0] = decodeURIComponent(pair[0]);
  420. pair[1] = decodeURIComponent(pair[1]);
  421. // If first entry with this name
  422. if (typeof query_string[pair[0]] === "undefined") {
  423. query_string[pair[0]] = pair[1];
  424. // If second entry with this name
  425. } else if (typeof query_string[pair[0]] === "string") {
  426. var arr = [ query_string[pair[0]], pair[1] ];
  427. query_string[pair[0]] = arr;
  428. // If third or later entry with this name
  429. } else {
  430. query_string[pair[0]].push(pair[1]);
  431. }
  432. }
  433. return query_string;
  434. };
  435. });