app.js 19 KB

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