layout.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493
  1. // var actionStatus = 'unrezzed'; // The state of the page. Goes to 'rezzed' when you hit the 'rez' button.
  2. var timeoutMilliseconds = 30000; // How long until we give up waiting and try again. NB even after this a response may still show up, so we need to be able to handle it.
  3. var pendingRequests = new Object(); // A list of entries that we've made a request for, and the timestamp in milliseconds when we made them
  4. var numPendingRequests = 0; // Should correspond to the number of elements in pendingRequests
  5. var maxPendingRequests = 2; // Max outstanding requests we should have at one time. NB if requests time out but still return, there may be more outstanding.
  6. var statusPollingInterval= 5; // How often we should poll the server for activeobject updates
  7. var lastActiveObjectPollMillisecondTS = 0; // Millisecond TS for when we last polled the server for active object changes
  8. var timer; // Timer var used to coordinate the eventLoop, which checks for outstanding tasks and kicks them off.
  9. var heartbeatTimer;
  10. var isRezzerConfigured = false;
  11. var rezzerControllerID = null;
  12. var activeLayoutID = 0;
  13. function purgeRequestList() {
  14. // TODO: Purge the request list of things that have timed out.
  15. // That will cause the script to try them again next time they get their turn in the eventLoop.
  16. for (entryid in pendingRequests) {
  17. if ( ( pendingRequests[""+entryid] + timeoutMilliseconds ) < ( new Date().getTime() ) ) {
  18. if ( $('#layoutentryid_'+entryid).hasClass('rezzing') ) {
  19. $('#layoutentryid_'+entryid).removeClass('rezzing').addClass('rezzing_timed_out');
  20. } else if ( $('#layoutentryid_'+entryid).hasClass('derezzing') ) {
  21. $('#layoutentryid_'+entryid).removeClass('derezzing').addClass('derezzing_timed_out');
  22. }
  23. delete pendingRequests[""+entryid];
  24. numPendingRequests--;
  25. }
  26. }
  27. }
  28. function update_labels() {
  29. // Make the text of the status descriptions match their CSS classes.
  30. // (There's probably a cleaner way to do this - maybe better to have the text always be there and visibility depends on the CSS class?
  31. $('li.waiting_to_rez span.rezzable_item_status').html('Waiting to rez');
  32. $('li.rezzing_timed_out span.rezzable_item_status').html('Timed out');
  33. $('li.rezzed span.rezzable_item_status').html('Rezzed');
  34. $('li.rezzing span.rezzable_item_status').html('Rezzing');
  35. $('li.rezzing_failed span.rezzable_item_status').html('Rez Failed');
  36. $('li.derezzing_failed span.rezzable_item_status').html('Derez Failed');
  37. $('li.waiting_to_derez span.rezzable_item_status').html('Waiting to derez');
  38. $('li.derezzing_timed_out span.rezzable_item_status').html('Timed out');
  39. $('li.derezzed span.rezzable_item_status').html('Derezzed');
  40. $('li.derezzed span.rezzable_item_positioning').html('&nbsp;');
  41. $('li.derezzing span.rezzable_item_status').html('Derezzing');
  42. $('li.configured span.rezzable_item_status').html('Ready');
  43. $('li.syncing span.rezzable_item_positioning').html('Syncing position');
  44. $('li.synced span.rezzable_item_positioning').html('Synced');
  45. $('li.sync_failed span.rezzable_item_positioning').html('Sync failed');
  46. $('li.waiting_to_sync span.rezzable_item_positioning').html('Waiting to sync');
  47. }
  48. function start_waiting_tasks( itemspan, itemspanjq, parentjq) {
  49. // Do whatever needs doing for an item.
  50. // If the class is "waiting_to" something, do that something.
  51. var bits = itemspan.id.split("_").pop().split("-"); // layoutentryid_1-2-3 becomes an Array(1,2,3)
  52. var entryid = bits.pop();
  53. var layoutid = bits.pop();
  54. var controllerid = bits.pop();
  55. if ( itemspanjq.hasClass( 'waiting_to_rez' ) ) {
  56. if (!pendingRequests[""+entryid]) {
  57. numPendingRequests++;
  58. }
  59. pendingRequests[""+entryid] = new Date().getTime();
  60. itemspanjq.removeClass( 'waiting_to_rez' );
  61. itemspanjq.addClass( 'rezzing' );
  62. // TODO: Stop sharing layouts between controllers and kill this
  63. $(".rezzable_item [data-layoutentryid='"+itemspanjq.attr('data-layoutentryid')+"']").removeClass( 'waiting_to_rez' );
  64. $(".rezzable_item [data-layoutentryid='"+itemspanjq.attr('data-layoutentryid')+"']").addClass( 'rezzing' );
  65. rez_layout_item( itemspanjq, entryid, controllerid, parentjq );
  66. } else if ( itemspanjq.hasClass( 'waiting_to_derez' ) ) {
  67. if (!pendingRequests[""+entryid]) {
  68. numPendingRequests++;
  69. }
  70. pendingRequests[""+entryid] = new Date().getTime();
  71. itemspanjq.removeClass( 'waiting_to_derez' );
  72. itemspanjq.addClass( 'derezzing' );
  73. // TODO: Stop sharing layouts between controllers and kill this
  74. $(".rezzable_item [data-layoutentryid='"+itemspanjq.attr('data-layoutentryid')+"']").removeClass( 'waiting_to_derez' );
  75. $(".rezzable_item [data-layoutentryid='"+itemspanjq.attr('data-layoutentryid')+"']").addClass( 'derezzing' );
  76. derez_layout_item( itemspanjq, entryid, controllerid, parentjq );
  77. } else if ( itemspanjq.hasClass( 'waiting_to_sync' ) ) {
  78. if (!pendingRequests[""+entryid]) {
  79. numPendingRequests++;
  80. }
  81. pendingRequests[""+entryid] = new Date().getTime();
  82. itemspanjq.removeClass( 'waiting_to_sync' );
  83. itemspanjq.addClass( 'syncing' );
  84. // TODO: Stop sharing layouts between controllers and kill this
  85. $(".rezzable_item [data-layoutentryid='"+itemspanjq.attr('data-layoutentryid')+"']").removeClass( 'waiting_to_sync' );
  86. $(".rezzable_item [data-layoutentryid='"+itemspanjq.attr('data-layoutentryid')+"']").addClass( 'syncing' );
  87. sync_layout_item( itemspanjq, entryid, controllerid );
  88. }
  89. }
  90. function check_done_tasks(parentjq) {
  91. // Check if all the waiting tasks are complete.
  92. // If they are, we can change the actionStatus.
  93. actionStatus = parentjq.attr('data-action-status');
  94. if (actionStatus == 'rezzing') {
  95. if ( ( parentjq.children('li.rezzing').length == 0 ) && ( parentjq.children('li.waiting_to_rez').length == 0 ) ) {
  96. parentjq.attr('data-action-status', 'rezzed');
  97. parentjq.find('.rez_all_objects').html('Rez all');
  98. parentjq.find('.rez_all_objects').unbind('click');
  99. parentjq.find('.rez_all_objects').click(function() {
  100. start_rez_all(parentjq);
  101. });
  102. }
  103. } else if (actionStatus == 'derezzing') {
  104. if ( ( parentjq.children('li.derezzing').length == 0 ) && ( parentjq.children('li.waiting_to_derez').length == 0 ) ) {
  105. parentjq.attr('data-action-status', 'derezzed');
  106. parentjq.find('.derez_all_objects').html('Derez all');
  107. parentjq.find('.derez_all_objects').unbind('click');
  108. parentjq.find('.derez_all_objects').click(function() {
  109. start_derez_all(parentjq);
  110. });
  111. }
  112. }
  113. }
  114. function eventLoop(parentjq) {
  115. clearTimeout( timer );
  116. // Make the text labels match the CSS classes
  117. update_labels(parentjq);
  118. update_buttons(parentjq);
  119. // Clear out any timed-out requests.
  120. purgeRequestList();
  121. if (numPendingRequests < maxPendingRequests) { // Go easy on the server / rezzer
  122. // Check for anything that needs to be done
  123. parentjq.children('.rezzable_item').each(function(itempos,itemspan) {
  124. if (numPendingRequests < maxPendingRequests) {
  125. start_waiting_tasks( itemspan, $(this), parentjq );
  126. }
  127. });
  128. }
  129. check_done_tasks(parentjq);
  130. update_labels(parentjq);
  131. update_buttons(parentjq);
  132. // timer = setTimeout( 'eventLoop()', 10000 );
  133. // timer = setTimeout( function(){ eventLoop(parentjq); }, 10000 );
  134. }
  135. function update_buttons(parentjq) {
  136. // If there are no objects to rez, show the generated button
  137. var rezzable_items_jq = parentjq.children('.rezzable_item');
  138. var num_rezzable = rezzable_items_jq.length;
  139. if (num_rezzable == 0 ) {
  140. parentjq.find('.sync_object_positions').css('visibility','hidden');
  141. // allow layout deletion
  142. parentjq.find('.delete_layout_button').css('visibility','visible')
  143. parentjq.find('.derez_all_objects').css('visibility','hidden');
  144. // The generate button takes the place of the rez all button, so we hide() one and show() the other.
  145. // All the other buttons keep their positions, so we make them invisible with css.
  146. parentjq.find('.rez_all_objects').hide();
  147. parentjq.find('.generate_standard_layout').show();
  148. } else {
  149. parentjq.find('.rez_all_objects').show();
  150. parentjq.find('.generate_standard_layout').hide(); // can't generate a layout if we already have entries
  151. // Have items listed, but none of them are rezzed.
  152. var num_rezzed = (rezzable_items_jq.filter('.rezzed, .rezzing, .rezzing_failed, .rezzing_timed_out, .derezzing, .derezzing_failed, .derezzing_timed_out')).length;
  153. var num_derezzed = num_rezzable - num_rezzed;
  154. if (num_rezzed > 0) {
  155. parentjq.find('.sync_object_positions').css('visibility','visible');
  156. if (num_rezzed < num_rezzable) {
  157. parentjq.find('.derez_all_objects').css('visibility','visible');
  158. //parentjq.find('.rez_all_objects').css('visibility','visible');
  159. } else {
  160. parentjq.find('.derez_all_objects').css('visibility','visible');
  161. //parentjq.find('.rez_all_objects').css('visibility','hidden');
  162. }
  163. // allow layout deletion
  164. parentjq.find('.delete_layout_button').css('visibility','hidden');
  165. } else {
  166. parentjq.find('.sync_object_positions').css('visibility','hidden');
  167. //parentjq.find('.rez_all_objects').css('visibility','visible');
  168. parentjq.find('.derez_all_objects').css('visibility','hidden');
  169. // allow layout deletion
  170. parentjq.find('.delete_layout_button').css('visibility','visible');
  171. }
  172. }
  173. }
  174. function heartbeat_refresh(parentjq, layoutid, rezzeruuid) {
  175. if (!isRezzerConfigured) {
  176. return false;
  177. }
  178. $.post(
  179. "refresh_rezzer.php",
  180. {
  181. layoutid: layoutid,
  182. rezzeruuid: rezzer_uuid,
  183. ts: new Date().getTime()
  184. },
  185. function(json) {
  186. var result = json.result;
  187. var changed = false;
  188. if (result == 'refreshed') {
  189. //itemjq.removeClass('syncing').addClass("synced");
  190. var leiduuids = json.layoutentries_to_uuids;
  191. parentjq.find('.rezzable_item').each( function() {
  192. var leid = $(this).attr('data-layoutentryid');
  193. var exists = leiduuids[ leid ];
  194. var deleted = false;
  195. /*
  196. This gives us an update of the server's representation of the in-world scene.
  197. In theory we should already know what's happening in the scene, because we gave the commands to do things and got the results.
  198. But some communication may have got lost, or the rezzer may have been controlled in a different window.
  199. There are some delays between the rezzer updating the server and this script polling the server.
  200. */
  201. if ( $(this).hasClass('waiting_to_derez') || $(this).hasClass('waiting_to_rez') || $(this).hasClass('derezzing') || $(this).hasClass('rezzing') ) {
  202. // Leave this to either catch its reponse or handle its time out.
  203. } else if ( $(this).hasClass('rezzing_failed') || $(this).hasClass('rezzing_timed_out') ) {
  204. // We thought it failed, but it seemed to have worked - we must have just not got the response.
  205. // Mark it as rezzed.
  206. if (exists) {
  207. $(this).removeClass('rezzing_failed').removeClass('rezzing_timed_out').addClass('rezzed');
  208. changed = true;
  209. }
  210. } else if ( $(this).hasClass('derezzing_failed') || $(this).hasClass('derezzing_timed_out') ) {
  211. // We thought derezzing failed but it's gone.
  212. // Mark it as derezzed.
  213. if (!exists) {
  214. $(this).removeClass('derezzing_failed').removeClass('derezzing_timed_out').addClass('derezzed');
  215. changed = true;
  216. deleted = true;
  217. }
  218. } else if ( $(this).hasClass('rezzed') ) {
  219. if (!exists) {
  220. var lastupdatets = $(this).attr('data-lastupdatets');
  221. if (!lastupdatets) {
  222. lastupdatets = 0;
  223. }
  224. var nowts = new Date().getTime();
  225. if ( ( nowts - lastupdatets ) > 10000 ) {
  226. $(this).removeClass('rezzed').addClass('derezzed');
  227. $(this).removeAttr('data-lastupdatets');
  228. deleted = true;
  229. changed = true;
  230. }
  231. }
  232. } else {
  233. // Wasn't there originally but is now
  234. // Mark it rezzed, but only if the derezzing we thought worked was a while ago.
  235. if (exists) {
  236. if ( $(this).hasClass('derezzed') ) {
  237. var lastupdatets = $(this).attr('data-lastupdatets');
  238. if (!lastupdatets) {
  239. lastupdatets = 0;
  240. }
  241. var nowts = new Date().getTime();
  242. if ( ( nowts - lastupdatets ) > 10000 ) {
  243. $(this).removeClass('derezzed');
  244. $(this).removeAttr('data-lastupdatets');
  245. changed = true;
  246. }
  247. }
  248. $(this).addClass('rezzed');
  249. changed = true;
  250. }
  251. }
  252. if (deleted && $(this).hasClass('deleted_from_layout') ) {
  253. itemjq.remove(); // TODO: Remove the config form too
  254. }
  255. });
  256. // Check if anything is marked as rezzed but gone, and remove the rezzed .
  257. // Check if anything is marked as unrezzed but present, and create the rezzed label
  258. } else if (result == 'failed') {
  259. // This can often happen legitimately, ie layout is deleted.
  260. // Just ignore the failure and carry on.
  261. //alert('failed');
  262. } else {
  263. //alert('refresh returned unknown status');
  264. }
  265. if (changed) {
  266. eventLoop(parentjq);
  267. // Changed layout
  268. if (layoutid != activeLayoutID) {
  269. return;
  270. }
  271. }
  272. clearTimeout(heartbeatTimer);
  273. heartbeatTimer = setTimeout( function(){ heartbeat_refresh( parentjq, layoutid, rezzeruuid ); }, heartbeatMilliseconds );
  274. }
  275. ,'json');
  276. }
  277. function refresh_misc_object_group( parentjq) {
  278. var item_list_jq = $('ul[data-parent*="'+parentjq.attr('id')+'"].object_group_misc');
  279. var bits = item_list_jq.attr('id').split("_").pop().split("-"); // layoutentryid_1-2-3 becomes an Array(1,2,3)
  280. var layoutid = bits.pop();
  281. var controllerid = bits.pop();
  282. var courseid = bits.pop();
  283. $.post(
  284. "update_rezzer_contents.php",
  285. {
  286. layoutid: layoutid,
  287. rezzeruuid: rezzer_uuid,
  288. controllerid: controllerid,
  289. courseid: courseid,
  290. ts: new Date().getTime()
  291. },
  292. function(json) {
  293. var result = json.result;
  294. if (result == 'refreshed') {
  295. insert_additional_object_html_items( item_list_jq, json.html_list_items, json.add_object_forms, json.edit_object_forms );
  296. //itemjq.removeClass('syncing').addClass("synced");
  297. } else if (result == 'failed') {
  298. // This can often happen legitimately, ie layout is deleted.
  299. // Just ignore the failure and carry on.
  300. //alert('failed');
  301. } else {
  302. //alert('refresh returned unknown status');
  303. }
  304. }
  305. ,'json');
  306. }
  307. function sync_layout_item(itemjq, entryid, controllerid) {
  308. $.post(
  309. "sync_layout_position.php",
  310. {
  311. layoutentryid: entryid,
  312. rezzeruuid: rezzer_uuid,
  313. controllerid: controllerid,
  314. ts: new Date().getTime()
  315. },
  316. function(json) {
  317. var result = json.result;
  318. if (result == 'synced') {
  319. itemjq.removeClass('syncing').addClass("synced");
  320. var itemjqid = itemjq.attr('id');
  321. setTimeout( function() {
  322. $('#'+itemjqid).removeClass('synced').find('span.rezzable_item_positioning').html('&nbsp;');
  323. }, 10000);
  324. } else if (result == 'failed') {
  325. itemjq.removeClass('syncing').addClass('syncing_failed');;
  326. }
  327. if (pendingRequests[""+entryid]) {
  328. delete pendingRequests[""+entryid];
  329. numPendingRequests--;
  330. }
  331. eventLoop( itemjq.closest('.layout_container') );
  332. }
  333. ,'json');
  334. }
  335. function rez_layout_item(itemjq, entryid, controllerid, parentjq) {
  336. $.post(
  337. "rez_object.php",
  338. {
  339. layoutentryid: entryid,
  340. rezzeruuid: rezzer_uuid,
  341. controllerid: controllerid,
  342. ts: new Date().getTime()
  343. },
  344. function(json) {
  345. var result = json.result;
  346. if (result == 'rezzed') {
  347. itemjq.removeClass('rezzing').addClass('rezzed');;
  348. itemjq.attr('data-lastupdatets', new Date().getTime());
  349. } else if (result == 'failed') {
  350. itemjq.removeClass('rezzing').addClass('rezzing_failed');;
  351. }
  352. if (pendingRequests[""+entryid]) {
  353. delete pendingRequests[""+entryid];
  354. numPendingRequests--;
  355. }
  356. eventLoop( parentjq );
  357. }
  358. ,'json');
  359. }
  360. function derez_layout_item(itemjq, entryid, controllerid, parentjq) {
  361. //alert('sending derez request with rezzeruuid '+rezzer_uuid);
  362. $.post(
  363. "derez_object.php",
  364. {
  365. layoutentryid: entryid,
  366. rezzeruuid: rezzer_uuid,
  367. controllerid: controllerid,
  368. synchronous: '0',
  369. ts: new Date().getTime()
  370. },
  371. function(json) {
  372. var result = json.result;
  373. //alert('derez result:'+result);
  374. if (result == 'queued') {
  375. // do nothing
  376. } else if (true || result == 'derezzed') {
  377. itemjq.removeClass('derezzing').addClass('derezzed');;
  378. itemjq.removeClass('syncing');
  379. itemjq.removeClass('synced');
  380. itemjq.removeClass('waiting_to_sync');
  381. itemjq.removeClass('sync_failed');
  382. if (itemjq.hasClass('deleted_from_layout')) {
  383. itemjq.remove(); // TODO: Remove the config form too
  384. }
  385. itemjq.attr('data-lastupdatets', new Date().getTime());
  386. } else if (result == 'failed') {
  387. itemjq.removeClass('derezzing').addClass('derezzing_failed');;
  388. }
  389. if (pendingRequests[""+entryid]) {
  390. delete pendingRequests[""+entryid];
  391. numPendingRequests--;
  392. }
  393. eventLoop( parentjq );
  394. }
  395. ,'json');
  396. }
  397. function mark_for_derez( btnjq, e ) {
  398. var lijq = btnjq.closest('li');
  399. if (!lijq.hasClass('rezzed')) {
  400. return false;
  401. }
  402. if (lijq.hasClass('waiting_to_derez')) {
  403. return false;
  404. }
  405. lijq.addClass( 'waiting_to_derez' ).removeClass('rezzed');
  406. eventLoop( (lijq.closest('.layout_container') ) );
  407. e.stopPropagation();
  408. return false;
  409. }
  410. function mark_for_rez( btnjq, e ) {
  411. var lijq = btnjq.closest('li');
  412. if (lijq.hasClass('rezzed')) {
  413. return false;
  414. }
  415. if (lijq.hasClass('waiting_to_rez')) {
  416. return false;
  417. }
  418. lijq.addClass( 'waiting_to_rez' ).removeClass('derezzed').removeClass('waiting_to_derez');
  419. eventLoop( (lijq.closest('.layout_container') ) );
  420. e.stopPropagation();
  421. return false;
  422. }
  423. function start_derez_all(parentjq) {
  424. /*
  425. parentjq.find('.derez_all_objects').html('Stop derez');
  426. parentjq.find('.derez_all_objects').unbind('click');
  427. parentjq.find('.derez_all_objects').click(function() {
  428. stop_derez_all(parentjq);
  429. });
  430. */
  431. var oldstatus = parentjq.attr('data-action-status');
  432. parentjq.attr('data-action-status', 'derezzing');
  433. parentjq.attr('data-rez-mode', 'derezzed');
  434. if (oldstatus == 'rezzing') {
  435. stop_rez_all(parentjq);
  436. }
  437. parentjq.children('li.rezzed').addClass( 'waiting_to_derez' );
  438. parentjq.children('li.waiting_to_derez').removeClass('rezzed');
  439. eventLoop( parentjq );
  440. }
  441. function start_rez_all(parentjq) {
  442. /*
  443. parentjq.find('.rez_all_objects').html('Stop rez');
  444. parentjq.find('.rez_all_objects').unbind('click');
  445. parentjq.find('.rez_all_objects').click(function() {
  446. stop_rez_all(parentjq);
  447. });
  448. */
  449. var oldstatus = parentjq.attr('data-action-status');
  450. parentjq.attr('data-action-status', 'rezzing');
  451. parentjq.attr('data-rez-mode', 'rezzed');
  452. if (oldstatus == 'derezzing') {
  453. stop_derez_all(parentjq);
  454. }
  455. parentjq.children('li.derezzing_failed').removeClass('derezzing_failed');
  456. parentjq.children('li.rezzing_failed').removeClass('rezzing_failed');
  457. parentjq.children('li.derezzed').removeClass('derezzed');
  458. parentjq.children('li.rezzable_item').not('li.rezzed').not('li.rezzing').addClass( 'waiting_to_rez' );
  459. eventLoop( parentjq );
  460. }
  461. function start_sync_all(parentjq) {
  462. parentjq.attr('data-action-status', 'syncing');
  463. parentjq.children('li.rezzed').addClass( 'waiting_to_sync' );
  464. eventLoop( parentjq );
  465. }
  466. function stop_derez_all(parentjq) {
  467. parentjq.children('.derez_all_objects').html('Derez all');
  468. parentjq.children('.derez_all_objects').unbind('click');
  469. parentjq.children('.derez_all_objects').click(function() {
  470. start_derez_all(parentjq);
  471. });
  472. parentjq.children('li.waiting_to_derez').addClass( 'rezzed' ).removeClass('waiting_to_derez');;
  473. eventLoop( parentjq );
  474. }
  475. function stop_rez_all(parentjq) {
  476. parentjq.children('.rez_all_objects').html('Rez all');
  477. parentjq.children('.rez_all_objects').unbind('click');
  478. parentjq.children('.rez_all_objects').click(function() {
  479. start_rez_all(parentjq);
  480. });
  481. parentjq.children('li.rezzable_item').removeClass( 'waiting_to_rez' ).find('.rezzable_item_status').html('Derezzed');
  482. eventLoop( parentjq );
  483. }
  484. function create_layout( buttonjq ) {
  485. var frmjq = buttonjq.closest("form");
  486. buttonjq.html( buttonjq.attr('data-creating-text') );
  487. $.post(
  488. "add_layout.php",
  489. frmjq.serialize(),
  490. function(json) {
  491. var result = json.result;
  492. var layoutid = json.layoutid;
  493. var courseid = json.courseid;
  494. var controllerid = json.controllerid;
  495. var layoutname = json.layoutname;
  496. if (result == 'added') {
  497. buttonjq.html( buttonjq.attr('data-create-text') );
  498. insert_layout_into_course_divs( layoutid, courseid, controllerid, layoutname, frmjq);
  499. $('#add_layout_lists_above_me').before(json.add_layout_lists);
  500. $('#add_edit_object_forms_above_me').before(json.edit_object_forms);
  501. $('#add_add_object_forms_above_me').before(json.add_object_forms);
  502. $('#add_add_object_groups_above_me').before(json.add_object_groups);
  503. attach_event_handlers();
  504. backLevels( frmjq.attr('id'), 1 );
  505. //eventLoop( $('.layout_container_'+layoutid) );
  506. //history.back();
  507. //history.go(-1);
  508. } else if (result == 'failed') {
  509. //alert('Adding layout entry failed');
  510. buttonjq.html( buttonjq.attr('data-create-text') );
  511. }
  512. }
  513. ,'json');
  514. return false;
  515. }
  516. function insert_layout_into_course_divs( layoutid, courseid, controllerid, layoutname, frmjq ) {
  517. $('#controller_'+courseid+'-'+controllerid).each( function() {
  518. var newformid = $(this).attr('data-id-prefix') + layoutid;
  519. var newli = '<li data-layout-link-li-id="'+layoutid+'"><a class="layout_link" href="#' + newformid + '">'+layoutname+'</a></li>';
  520. $(this).children('.add_layout_above_me').before( newli );
  521. });
  522. }
  523. function generate_standard_layout( buttonjq ) {
  524. var frmjq = buttonjq.closest("form");
  525. buttonjq.html( buttonjq.attr('data-generating-text') );
  526. var layoutid = buttonjq.attr('data-layoutid');
  527. $.post(
  528. "generate_layout_entries.php",
  529. {
  530. layoutid: layoutid,
  531. rezzeruuid: rezzer_uuid,
  532. ts: new Date().getTime()
  533. },
  534. function(json) {
  535. var result = json.result;
  536. if (result == 'generated') {
  537. buttonjq.html( buttonjq.attr('data-generate-text') );
  538. var addedentries = json.addedentries;
  539. var entryi;
  540. for (entryi = 0; entryi<addedentries.length; entryi++) {
  541. thisentry = addedentries[entryi];
  542. $('.addobject_layout_'+layoutid+'_'+thisentry['objecttypelinkable']).each( function() {
  543. insert_layout_entry_into_layout_divs( thisentry['layoutid'], thisentry['layoutentryid'], thisentry['objectname'], thisentry['objectgroup'], thisentry['objectgrouptext'], thisentry['objecttypelinkable'], thisentry['moduletitle'], $(this), thisentry['html_list_item'], thisentry['edit_object_form'] );
  544. });
  545. }
  546. eventLoop( $('.layout_container_'+layoutid) );
  547. } else if (result == 'failed') {
  548. //alert('Adding layout entry failed');
  549. buttonjq.html( buttonjq.attr('data-generate-text') );
  550. }
  551. }
  552. ,'json');
  553. }
  554. function validate_for_submit(frmjq) {
  555. // We have a space for the module id, which is required.
  556. // This may only be a placeholder if there are no selected options.
  557. if (frmjq.find('[data-fieldname="sloodlemoduleid"]').length > 0) {
  558. if (frmjq.find('input:radio[name=sloodlemoduleid]:checked').length == 0) {
  559. frmjq.find('[data-fieldname="sloodlemoduleid"]').addClass('validation_error');
  560. // Show in red for 10 seconds
  561. setTimeout(function(){
  562. $(frmjq.find('[data-fieldname="sloodlemoduleid"]').removeClass('validation_error'));
  563. }, 5000);
  564. return false;
  565. }
  566. }
  567. return true;
  568. }
  569. function add_to_layout( buttonjq ) {
  570. var frmjq = buttonjq.closest("form");
  571. if (!validate_for_submit(frmjq)) {
  572. return false;
  573. }
  574. buttonjq.html( buttonjq.attr('data-adding-text') );
  575. $.post(
  576. "add_layout_entry.php",
  577. frmjq.serialize(),
  578. function(json) {
  579. var result = json.result;
  580. var objectgroup = json.objectgroup;
  581. var objectgrouptext = json.objectgrouptext;
  582. var objectname = json.objectname;
  583. var layoutid = json.layoutid;
  584. var objecttypelinkable = json.objecttypelinkable;
  585. var moduletitle = json.moduletitle;
  586. var layoutentryid = json.layoutentryid;
  587. var edit_object_form = json.edit_object_form;
  588. var html_list_item = json.html_list_item;
  589. if (layoutid == '') {
  590. //alert('error: missing layoutid after adding to layout');
  591. }
  592. if (result == 'added') {
  593. buttonjq.html( buttonjq.attr('data-add-text') );
  594. insert_layout_entry_into_layout_divs( layoutid, layoutentryid, objectname, objectgroup, objectgrouptext, objecttypelinkable, moduletitle, frmjq, html_list_item, edit_object_form);
  595. eventLoop( $('.layout_container_'+layoutid) );
  596. //history.back();
  597. backLevels( frmjq.attr('id'), 2 );
  598. //history.go(-2);
  599. } else if (result == 'failed') {
  600. //alert('Adding layout entry failed');
  601. buttonjq.html( buttonjq.attr('data-add-text') );
  602. }
  603. }
  604. ,'json');
  605. return false;
  606. }
  607. // NB This just removes the object from the layout and marks it for deletion.
  608. // If rezzed, we'll do the actual of the object with another request.
  609. function delete_layout_configuration( buttonjq ) {
  610. var frmjq = buttonjq.closest("form");
  611. buttonjq.html( buttonjq.attr('data-deleting-text') );
  612. $.post(
  613. "delete_layout_entry.php",
  614. frmjq.serialize(),
  615. function(json) {
  616. var result = json.result;
  617. var layoutentryid = json.layoutentryid;
  618. var layoutid = json.layoutid;
  619. if ( (result == 'deleted') || (result == 'notfound') ){
  620. //alert('deleted');
  621. buttonjq.html( buttonjq.attr('data-delete-text') );
  622. regexPtn = '^layoutentryid_.+-.+-.+-'+layoutentryid+'$';
  623. var re = new RegExp(regexPtn,"");
  624. $('li').filter(function() {
  625. return this.id.match(regexPtn);
  626. }).each( function() {
  627. //$(this).remove();
  628. // TODO: What happens if we're rezzing or waiting to rez?
  629. // Mark it as deleted and queue it for derezzing, then let the eventLoop deal with it in its own time
  630. if ($(this).hasClass('rezzed')) {
  631. $(this).addClass('waiting_to_derez');
  632. $(this).addClass('deleted_from_layout');
  633. } else { // Not rezzed, just get rid of it
  634. $(this).remove();
  635. }
  636. });
  637. eventLoop( $('.layout_container_'+layoutid) );
  638. backLevels( frmjq.attr('id'), 1 );
  639. } else { //if (result == 'failed')
  640. // alert('Deleting layout entry failed');
  641. buttonjq.html( buttonjq.attr('data-delete-text') );
  642. }
  643. }
  644. ,'json');
  645. return false;
  646. }
  647. function confirm_clone_layout(buttonjq) {
  648. var frmjq = buttonjq.closest('ul');
  649. var confirm_section = frmjq.find('.clone_confirmation_zone');
  650. if (confirm_section.is(":visible")) {
  651. confirm_section.hide();
  652. return false;
  653. }
  654. confirm_section.show();
  655. confirm_section.find('.clone_confirmation_button_ok').unbind('click').bind('click', function() {
  656. clone_layout(buttonjq);
  657. confirm_section.hide();
  658. });
  659. confirm_section.find('.clone_confirmation_button_cancel').unbind('click').bind('click', function() {
  660. confirm_section.hide();
  661. });
  662. }
  663. function confirm_delete_layout(buttonjq) {
  664. var frmjq = buttonjq.closest('ul');
  665. var confirm_section = frmjq.find('.delete_confirmation_zone');
  666. if (confirm_section.is(":visible")) {
  667. confirm_section.hide();
  668. return false;
  669. }
  670. confirm_section.show();
  671. confirm_section.find('.delete_confirmation_button_ok').unbind('click').bind('click', function() {
  672. delete_layout(buttonjq);
  673. confirm_section.hide();
  674. });
  675. confirm_section.find('.delete_confirmation_button_cancel').unbind('click').bind('click', function() {
  676. confirm_section.hide();
  677. });
  678. }
  679. // NB This just removes the layout from the server and marks it for deletion.
  680. // If rezzed, we'll derez the objects separately, and only remove the layout from view when they're done
  681. function delete_layout( buttonjq ) {
  682. frmjq = buttonjq.closest('ul');
  683. buttonjq.html( buttonjq.attr('data-deleting-text') );
  684. var layoutid = buttonjq.attr('data-layoutid');
  685. $.post(
  686. "delete_layout.php",
  687. {
  688. layoutid: layoutid,
  689. ts: new Date().getTime()
  690. },
  691. function(json) {
  692. var result = json.result;
  693. if (result == 'deleted') {
  694. //alert('deleted');
  695. $('[data-layout-link-li-id*="'+layoutid+'"]').remove();
  696. buttonjq.html( buttonjq.attr('data-deleted-text') );
  697. backLevels(frmjq.attr('id'), 1);
  698. //history.back();
  699. } else { //if (result == 'failed')
  700. // For now we'll just live with the failure - it's probably that it's already gone
  701. //alert('Deleting layout entry failed');
  702. buttonjq.html( buttonjq.attr('data-delete-text') );
  703. }
  704. }
  705. ,'json');
  706. return false;
  707. }
  708. function rename_layout( spanjq ) {
  709. var parentjq = spanjq.closest('ul');
  710. var inputjq = parentjq.find('.rename_layout_input');
  711. var newname = inputjq.val();
  712. var layoutid = inputjq.attr('data-rename-input-layoutid');
  713. $.post(
  714. "rename_layout.php",
  715. {
  716. layoutid: layoutid,
  717. layoutname: newname,
  718. ts: new Date().getTime()
  719. },
  720. function(json) {
  721. if (json.result == 'renamed') {
  722. var orig_title = $('.layout_container_'+layoutid).attr('title');
  723. $('li[data-layout-link-li-id*="'+layoutid+'"]').find('.layout_link').html(newname);
  724. $('.layout_container_'+layoutid).attr('title', newname);
  725. $('.layout_container_'+layoutid).find('.group:first').html(newname);
  726. // The page title is managed by IUI.
  727. // It may have changed while we were waiting for a response, in which case we leave it alone.
  728. // It'll be fixed next time we click on it.
  729. if ( $('h1#pageTitle').html() == orig_title ) {
  730. $('h1#pageTitle').html(newname);
  731. }
  732. parentjq.find('.rename_input').hide();
  733. parentjq.find('.rename_input_text').html(newname);
  734. parentjq.find('.rename_input_text').show();
  735. } else {
  736. //alert('Rename failed');
  737. }
  738. }
  739. ,'json');
  740. }
  741. function clone_layout( buttonjq ) {
  742. buttonjq.html( buttonjq.attr('data-cloning-text') );
  743. var layoutid = buttonjq.attr('data-layoutid');
  744. var frmjq = buttonjq.closest('ul');
  745. $.post(
  746. "clone_layout.php",
  747. {
  748. layoutid: layoutid,
  749. rezzeruuid: rezzer_uuid,
  750. ts: new Date().getTime()
  751. },
  752. function(json) {
  753. var result = json.result;
  754. if (result == 'cloned') {
  755. buttonjq.html( buttonjq.attr('data-cloned-text') );
  756. layoutid = json.layoutid;
  757. var layoutname = json.layoutname;
  758. var courseid = json.courseid;
  759. var controllerid = json.controllerid;
  760. insert_layout_into_course_divs( layoutid, courseid, controllerid, layoutname, null);
  761. $('#add_layout_lists_above_me').before(json.add_layout_lists);
  762. $('#add_edit_object_forms_above_me').before(json.edit_object_forms);
  763. $('#add_add_object_forms_above_me').before(json.add_object_forms);
  764. $('#add_add_object_groups_above_me').before(json.add_object_groups);
  765. attach_event_handlers();
  766. //eventLoop( $('.layout_container_'+layoutid) );
  767. //history.back();
  768. //history.go(-1);
  769. backLevels( frmjq.attr('id'), 1 );
  770. } else { //if (result == 'failed')
  771. // For now we'll just live with the failure - it's probably that it's already gone
  772. //alert('Cloning layout entry failed');
  773. buttonjq.html( buttonjq.attr('data-clone-text') );
  774. }
  775. }
  776. ,'json');
  777. return false;
  778. }
  779. function update_layout_position( buttonjq ) {
  780. var frmjq = buttonjq.closest("form");
  781. $.post(
  782. "sync_layout_position.php",
  783. frmjq.serialize(),
  784. function(json) {
  785. var result = json.result;
  786. }
  787. ,'json');
  788. return false;
  789. }
  790. function update_layout_configuration( buttonjq ) {
  791. var frmjq = buttonjq.closest("form");
  792. buttonjq.html( buttonjq.attr('data-updating-text') );
  793. $.post(
  794. "update_layout_entry.php",
  795. frmjq.serialize(),
  796. function(json) {
  797. var result = json.result;
  798. var objectgroup = json.objectgroup;
  799. var objectgrouptext = json.objectgrouptext;
  800. var layoutentryid = json.layoutentryid;
  801. var moduletitle= json.moduletitle;
  802. if (result == 'updated') {
  803. buttonjq.html( buttonjq.attr('data-update-text') );
  804. $('li[data-layoutentryid*="'+layoutentryid+'"]').find('.module_info').html(moduletitle);
  805. backLevels( frmjq.attr('id'), 1 );
  806. //history.back();
  807. //history.go(-2);
  808. } else { //if (result == 'failed') {
  809. //alert('Updating layout entry failed');
  810. buttonjq.html( buttonjq.attr('data-update-text') );
  811. }
  812. }
  813. ,'json');
  814. return false;
  815. }
  816. function insert_additional_object_html_items( objectlistjq, new_object_html_items, add_object_forms, edit_object_forms ) {
  817. for( var id in add_object_forms) {
  818. if ($("#"+id).length == 0){
  819. $('#add_add_object_forms_above_me').before(add_object_forms[id]);
  820. }
  821. }
  822. for( var id in edit_object_forms) {
  823. if ($("#"+id).length == 0){
  824. $('#add_edit_object_forms_above_me').before(edit_object_forms[id]);
  825. }
  826. }
  827. for( var id in new_object_html_items) {
  828. if ($("#"+id).length == 0){
  829. objectlistjq.append(new_object_html_items[id]);
  830. }
  831. }
  832. attach_event_handlers();
  833. }
  834. function insert_layout_entry_into_layout_divs( layoutid, layoutentryid, objectname, objectgroup, objectgrouptext, objecttypelinkable, moduletitle, addfrmjq, html_list_item, edit_object_form ) {
  835. regexPtn = '^layout_.+-.+-'+layoutid+'$';
  836. var re = new RegExp(regexPtn,"");
  837. $('ul').filter(function() {
  838. return this.id.match(regexPtn);
  839. }).each( function() {
  840. // make an id for the new element
  841. var newElementID = $(this).attr('id').replace('layout_','layoutentryid_')+'-'+layoutentryid;
  842. // already there
  843. if ( $(this).children('#'+newElementID).length > 0 ) {
  844. return;
  845. }
  846. // If we don't yet have a group to put this item in, create it
  847. if ( $(this).children(".after_group_"+objectgroup).size() == 0 ) {
  848. var groupLi = '<li class="group">'+objectgrouptext+'</li>' + '<li class="after_group_'+objectgroup+'"></li>';
  849. $(this).children(".add_object_group").before( groupLi );
  850. }
  851. $(this).children(".after_group_"+objectgroup).before(html_list_item);
  852. attach_event_handlers();
  853. var editFrm = $(edit_object_form);
  854. // Seems like the original click handler doesn't get created initially - maybe because it's hidden?
  855. editFrm.find('.delete_layout_entry_button').unbind('click').click(function() {
  856. return delete_layout_configuration($(this));
  857. });
  858. $('#add_configuration_above_me_'+$(this).attr('id')).before(editFrm);
  859. editFrm.find('.update_layout_entry_button').unbind('click').click(function() {
  860. return update_layout_configuration($(this));
  861. });
  862. editFrm.find('.refresh_config_button').unbind('click').click( function() {
  863. refresh_form_options( $(this) );
  864. });
  865. });
  866. }
  867. function refresh_form_options( buttonjq) {
  868. var frmjq = buttonjq.closest('form')
  869. var courseid = frmjq.attr('data-courseid');
  870. var primname = frmjq.attr('data-primname');
  871. buttonjq.html( buttonjq.attr('data-refreshing-text') );
  872. $.post(
  873. "refresh_options.php",
  874. {
  875. courseid: courseid,
  876. primname: primname,
  877. ts: new Date().getTime()
  878. },
  879. function(json) {
  880. var result = json.result;
  881. if (result == 'refreshed') {
  882. var fields = json.fields;
  883. for (var fn in fields) {
  884. // This assumes we only have radios, not selects etc.
  885. var ops = fields[fn];
  886. var existing_ops = frmjq.find('.sloodle_config').find('input:radio');
  887. for (var o in ops) {
  888. if ( existing_ops.filter('[name='+fn+']').filter('[value='+o+']').length > 0 ) {
  889. // already got it
  890. } else {
  891. var newinput = '<input type="radio" name="'+fn+'" value="'+o+'" />' + ops[o] + '&nbsp; &nbsp;';
  892. var existing_op_items = existing_ops.filter('[name='+fn+']');
  893. if (existing_op_items.length == 0) {
  894. // not there yet - should have a placeholder instead
  895. frmjq.find('.no_options_placeholder').filter('[data-fieldname='+fn+']').before(newinput);
  896. frmjq.find('.no_options_placeholder').filter('[data-fieldname='+fn+']').remove();
  897. } else {
  898. existing_op_items.filter(':first').before( newinput );
  899. }
  900. }
  901. }
  902. // TODO: Should really remove old ones that no longer exist...
  903. }
  904. } else if (result == 'failed') {
  905. //alert('refresh failed');
  906. }
  907. buttonjq.html( buttonjq.attr('data-refresh-text') );
  908. }
  909. ,'json');
  910. }
  911. function configure_rezzer( parentjq, controllerid, layoutid) {
  912. // Layout ID has changed
  913. // This can happen if we fail, then try to reconnect
  914. if (activeLayoutID != layoutid) {
  915. return;
  916. }
  917. $.post(
  918. "configure_rezzer.php",
  919. {
  920. controllerid: controllerid,
  921. rezzeruuid: rezzer_uuid,
  922. ts: new Date().getTime()
  923. },
  924. function(json) {
  925. var result = json.result;
  926. if (result == 'configured') {
  927. isRezzerConfigured = true;
  928. rezzerControllerID = controllerid;
  929. parentjq.attr('data-connection-status', 'connected');
  930. update_buttons( parentjq );
  931. refresh_misc_object_group( parentjq );
  932. rezzerControllerID = controllerid;
  933. heartbeat_refresh( parentjq, layoutid, rezzer_uuid)
  934. parentjq.find('.fatal_error_zone').hide();
  935. } else if (result == 'failed') {
  936. parentjq.find('.fatal_error_zone').show();
  937. parentjq.children('.rez_all_objects').hide();
  938. // Try again in 30 seconds
  939. setTimeout(function() {
  940. configure_rezzer( parentjq, controllerid, layoutid );
  941. }, 30000);
  942. }
  943. }
  944. ,'json');
  945. }
  946. function handle_scene_selection( layoutlinkjq ) {
  947. var layoutjqid = layoutlinkjq.attr('href'); // looks like #layout_2-2-1
  948. var parentjq = $(layoutjqid); // the "#" happens to be the same notation as that used for the jquery id selector
  949. var bits = parentjq.attr('id').split("_").pop().split("-"); // layoutentryid_1-2-3 becomes an Array(1,2,3)
  950. var layoutid = bits.pop(); // pop this off - don't think we need it
  951. activeLayoutID = layoutid;
  952. var controllerid = bits.pop();
  953. /*
  954. if ( isRezzerConfigured && ( rezzerControllerID == controllerid) ) {
  955. return;
  956. }
  957. */
  958. configure_rezzer( parentjq, controllerid, layoutid );
  959. return true;
  960. }
  961. function attach_event_handlers() {
  962. $().find('.create_layout_button').unbind('click').click(function() {
  963. return create_layout($(this));
  964. });
  965. $().find('.layout_link').unbind('click').click(function() {
  966. return handle_scene_selection($(this));
  967. });
  968. $().find('.rez_all_objects').unbind('click').click(function() {
  969. start_rez_all($(this).closest('.layout_container'));
  970. });
  971. $().find('.derez_all_objects').unbind('click').click(function() {
  972. start_derez_all($(this).closest('.layout_container'));
  973. });
  974. $().find('.add_to_layout_button').unbind('click').click(function() {
  975. return add_to_layout($(this));
  976. });
  977. $().find('.update_layout_entry_button').unbind('click').click(function() {
  978. return update_layout_configuration($(this));
  979. });
  980. $().find('.delete_layout_entry_button').unbind('click').click(function() {
  981. return delete_layout_configuration($(this));
  982. });
  983. $().find('.sync_object_positions').unbind('click').click(function() {
  984. start_sync_all( $(this).closest('.layout_container') );
  985. });
  986. $().find('.delete_layout_button').unbind('click').click(function() {
  987. return confirm_delete_layout( $(this) );
  988. });
  989. $().find('.clone_layout_button').unbind('click').click(function() {
  990. return confirm_clone_layout( $(this) );
  991. });
  992. $().find('.generate_standard_layout').unbind('click').click(function() {
  993. return generate_standard_layout( $(this) );
  994. });
  995. $().find('.layout_container').each(function() {
  996. update_buttons($(this));
  997. update_labels($(this));
  998. });
  999. // $().find('.rename_input').hide();
  1000. $('.rename_layout_button').unbind('click').click( function(){
  1001. handle_rename_button_click($(this));
  1002. });
  1003. $().find('.populate_object_group').unbind('click').click( function() {
  1004. return refresh_misc_object_group( $(this).closest('ul') );
  1005. });
  1006. $('.refresh_config_button').unbind('click').click( function() {
  1007. refresh_form_options( $(this) );
  1008. });
  1009. $('.reload_page_button').unbind('click').click( function() {
  1010. location.reload();
  1011. });
  1012. $().find('.rezzable_item_derez_button').unbind('click').click( function(e) {
  1013. mark_for_derez( $(this), e );
  1014. });
  1015. $().find('.rezzable_item_rez_button').unbind('click').click( function(e) {
  1016. mark_for_rez( $(this), e );
  1017. });
  1018. }
  1019. function handle_rename_button_click(btnjq) {
  1020. var parentjq = btnjq.closest('ul');
  1021. if ( btnjq.hasClass('shown_textbox') ) {
  1022. rename_layout( btnjq );
  1023. btnjq.removeClass('shown_textbox');
  1024. } else {
  1025. btnjq.addClass('shown_textbox');
  1026. parentjq.find('.rename_input_text').hide();
  1027. parentjq.find('.rename_input').show();
  1028. }
  1029. }
  1030. $(document).ready(function () {
  1031. attach_event_handlers();
  1032. enable_slide_navigation();
  1033. $('#backButton').show();
  1034. // iui.animOn = true;
  1035. });
  1036. function backLevels(fromPageID, num) {
  1037. if (isBusy) {
  1038. return;
  1039. }
  1040. if (num < 1) {
  1041. return false;
  1042. }
  1043. // Already moved on? Leave the navigation alone
  1044. if ( $('#'+fromPageID).attr('selected') != 'true' ) {
  1045. //alert(fromPageID + ' not currently selected, not going back levels - attr is '+$('#'+fromPageID).attr('id'));
  1046. return false;
  1047. }
  1048. var nextPageID = $('#'+fromPageID).attr('data-parent');
  1049. if (nextPageID == null) {
  1050. //alert('no next page to go to, giving up');
  1051. return false;
  1052. }
  1053. fromPage = document.getElementById( fromPageID );
  1054. toPage = document.getElementById( nextPageID );
  1055. //targetjq.attr('selected','true'); // Select the target
  1056. targetjq = $('#'+nextPageID);
  1057. // Something went wrong - do the best we can, using jquery
  1058. if ( (fromPage == null) || (toPage == null) ) {
  1059. targetjq.attr('selected','true'); // Select the target
  1060. $('[selected*="true"]').attr('selected','');
  1061. } else {
  1062. slidePages(fromPage, toPage, true);
  1063. }
  1064. var parentid = targetjq.attr('data-parent');
  1065. /*
  1066. if ( (parentid == '') || (parentid == null) ){
  1067. alert('Error: parent id not set in link to '+clickedid);
  1068. return false;
  1069. }
  1070. */
  1071. $('#backButton').attr('href', '#'+parentid);
  1072. $('#backButton').html( $('#'+parentid).attr('title') );
  1073. $('#backButton').show();
  1074. $('#pageTitle').html( targetjq.attr('title') );
  1075. fromPageID = nextPageID;
  1076. num--;
  1077. setTimeout( "backLevels('"+fromPageID+"', "+num+")", 1000 );
  1078. }
  1079. // More-or-less duplicates the IUI functionality
  1080. // ...but does it without messing with the URL
  1081. // ...as the on-prim browser loses the pending javascript events when it changes the URL #hash
  1082. // Also, uses the
  1083. function enable_slide_navigation() {
  1084. $('a').live('click', function() {
  1085. if (isBusy) {
  1086. return;
  1087. }
  1088. var clickedid=this.hash;
  1089. if ( clickedid == null ) {
  1090. //alert('Error: no hash found');
  1091. return false;
  1092. }
  1093. if (clickedid == "#sitelist") {
  1094. window.location = $('#sitelist').attr('data-parent-url');
  1095. return false;
  1096. }
  1097. var targetjq = $(clickedid); // already begins with #
  1098. if (targetjq.size() == 0) {
  1099. //alert('Error: no target found for id '+clickedid);
  1100. return false;
  1101. }
  1102. var backwards = ($(this).attr('id') == 'backButton');
  1103. fromPage = document.getElementById( $('[selected*="true"]').attr('id') );
  1104. toPage = document.getElementById( targetjq.attr('id') );
  1105. //targetjq.attr('selected','true'); // Select the target
  1106. if ( (targetjq.hasClass('add_object_form')) || (targetjq.hasClass('edit_object_form')) ) {
  1107. refresh_form_options( targetjq );
  1108. }
  1109. // Something went wrong - do the best we can, using jquery
  1110. if ( (fromPage == null) || (toPage == null) ) {
  1111. targetjq.attr('selected','true'); // Select the target
  1112. $('[selected*="true"]').attr('selected','');
  1113. } else {
  1114. slidePages(fromPage, toPage, backwards);
  1115. }
  1116. var parentid = targetjq.attr('data-parent');
  1117. if ( (parentid == '') || (parentid == null) ){
  1118. //alert('Error: parent id not set in link to '+clickedid);
  1119. return false;
  1120. }
  1121. $('#backButton').attr('href', '#'+parentid);
  1122. $('#backButton').html( $('#'+parentid).attr('title') );
  1123. $('#backButton').show();
  1124. $('#pageTitle').html( targetjq.attr('title') );
  1125. return false;
  1126. });
  1127. }
  1128. var slideSpeed = 20;
  1129. var slideInterval = 0;
  1130. var currentPage = null;
  1131. var currentDialog = null;
  1132. var currentWidth = 0;
  1133. var currentHash = location.hash;
  1134. var hashPrefix = "#_";
  1135. var pageHistory = [];
  1136. var newPageCount = 0;
  1137. var checkTimer;
  1138. var hasOrientationEvent = false;
  1139. var portraitVal = "portrait";
  1140. var landscapeVal = "landscape";
  1141. var isBusy = false;
  1142. // The following comes from iui
  1143. function slide1(fromPage, toPage, backwards, axis, cb)
  1144. {
  1145. if (axis == "y")
  1146. (backwards ? fromPage : toPage).style.top = "100%";
  1147. else
  1148. toPage.style.left = "100%";
  1149. scrollTo(0, 1);
  1150. toPage.setAttribute("selected", "true");
  1151. var percent = 100;
  1152. slide();
  1153. var timer = setInterval(slide, slideInterval);
  1154. function slide()
  1155. {
  1156. percent -= slideSpeed;
  1157. if (percent <= 0)
  1158. {
  1159. percent = 0;
  1160. clearInterval(timer);
  1161. cb();
  1162. }
  1163. if (axis == "y")
  1164. {
  1165. backwards
  1166. ? fromPage.style.top = (100-percent) + "%"
  1167. : toPage.style.top = percent + "%";
  1168. }
  1169. else
  1170. {
  1171. fromPage.style.left = (backwards ? (100-percent) : (percent-100)) + "%";
  1172. toPage.style.left = (backwards ? -percent : percent) + "%";
  1173. }
  1174. }
  1175. }
  1176. function slidePages(fromPage, toPage, backwards)
  1177. {
  1178. if (isBusy) {
  1179. return;
  1180. }
  1181. isBusy = true;
  1182. var axis = (backwards ? fromPage : toPage).getAttribute("axis");
  1183. clearInterval(checkTimer);
  1184. slide1(fromPage, toPage, backwards, axis, slideDone);
  1185. function slideDone()
  1186. {
  1187. if (!hasClass(toPage, "dialog"))
  1188. fromPage.removeAttribute("selected");
  1189. checkTimer = setInterval(checkOrientAndLocation, 300);
  1190. //setTimeout(updatePage, 0, toPage, fromPage);
  1191. fromPage.removeEventListener('webkitTransitionEnd', slideDone, false);
  1192. isBusy = false;
  1193. }
  1194. }
  1195. function findParent(node, localName)
  1196. {
  1197. while (node && (node.nodeType != 1 || node.localName.toLowerCase() != localName))
  1198. node = node.parentNode;
  1199. return node;
  1200. }
  1201. function hasClass(self, name)
  1202. {
  1203. var re = new RegExp("(^|\\s)"+name+"(iui_gid|\\s)");
  1204. return re.exec(self.getAttribute("class")) != null;
  1205. }
  1206. function orientChangeHandler()
  1207. {
  1208. var orientation=window.orientation;
  1209. switch(orientation)
  1210. {
  1211. case 0:
  1212. setOrientation(portraitVal);
  1213. break;
  1214. case 90:
  1215. case -90:
  1216. setOrientation(landscapeVal);
  1217. break;
  1218. }
  1219. }
  1220. function checkOrientAndLocation()
  1221. {
  1222. if (!hasOrientationEvent)
  1223. {
  1224. if (window.innerWidth != currentWidth)
  1225. {
  1226. currentWidth = window.innerWidth;
  1227. var orient = currentWidth == 320 ? portraitVal : landscapeVal;
  1228. setOrientation(orient);
  1229. }
  1230. }
  1231. if (location.hash != currentHash)
  1232. {
  1233. var pageId = location.hash.substr(hashPrefix.length);
  1234. //iui.showPageById(pageId);
  1235. }
  1236. }
  1237. function setOrientation(orient)
  1238. {
  1239. document.body.setAttribute("orient", orient);
  1240. setTimeout(scrollTo, 100, 0, 1);
  1241. }