Collaborative Coding

From Second Life Wiki
Jump to: navigation, search

Created by Kira Komarov.

Web-Related Tree

+-------------+ +-------------+ +---------+ +-----------------------------+ +--------------------------+ | Prowler | | Gremlin | | N2K | | Permanent Primitive URL | | Collaborative Coding | +------+------+ +-------------+ +---------+ +------------+----------------+ +--------------------------+ | | +--------+--------+ +-------------------+-------------------+ | SIM Status | | Population Genetics and Selection | +-----------------+ +-------------------+-------------------+ | +------------+-------------+ | Interactive Bacteria | +--------------------------+

ChangeLog

  • 2 days later...

Added tabs so that the system maintains a history in case a rollback is needed.

  • 500oz of lube later...

Eliminated the need for save/load/etc... Using the snippet highlighter. The G3S41 highlighting really didn't cut it for the iDevice.

Demo

You can find a demo at Collaborative Coding Page which will allow you to test out the features.

Introduction

Sometimes, I've been asked to fix some code that was not working properly or explain why it is not working properly. We ended up pasting stuff through the clunky interface which trimmed the script due to limitations. The other variant was to just exchange scripts but that lead to appending numbers to the script name in order to keep track of the changes.

Since that proved itself to be majorly inconvenient, the following system was created using PHP, JS/JSON, SQLite, GeSHi and plain HTML. I've also paid attention that the result can be projected onto a primitive using prim media so that it may be used in teaching classes. It is also one of the reasons we needed to create Homebrew iOS so that we can run this on a low-end server.

System Overview

                |
 +----------+        +----------+   +------------+
 |          |   |    |          |   |            |
 | live.php +------->| save.php +-->|   sqlite   |
 |          |<-------+ load.php |<--+  Database  |
 |          |   |    |          |   |            |
 +----------+        +----------+   +------------+
                |

The system has four components:

  • a save.php script used to commit the changes to the database.
  • a load.php script used to load the changes once they have been committed to the database.
  • a live.php script which offers a simple text-only editor.
  • a class.php script which can be projected onto a primitive so that all the changes appear automatically without needed to reload the page.

... and a bunch of Javascript files (read on).

Requirements

  • a HTTP server with PHP PDO->SQLite support.
  • jeditable for the nice JQuery enabled div.
  • Tabby jQuery for allowing tabs in the div.
  • ... additionally, I've provided a mirror of Autogrow Textareas since the project is in development.
  • JQuery, I used version 1.6.2 - it might work on newer versions (untested).
  • snippet - jquery syntax highlighting.
  • [1] - jquery id tabs, the 3.0 beta version by Sean Catchpole.

Setup

First, create an sqlite database called live.sqlite with the following structure:

CREATE TABLE "data" (
	 "txt" blob,
	 "idx" integer NOT NULL,
	PRIMARY KEY("idx")
);

Then, dump the save.php, load.php, live.php and class.php in the same directory as the sqlite database you created. You need to place all the files in the same directory and the javascript files in a top-level directory under /js/libs/. You can find the tree-structure of the filesystem in the next section.

In order to project the script in-world, create a primitive and set the media url to:

http://webserver.com/cc/live.php

assuming that your webserver is pointed at by webserver.com.

Filestructure Overview

Here is an overview of the filesystem assuming root stands for the top-level directory:

root                                  | <--- webroot
 |                                    |
 +-js/                                |
   |                                  |
   +-libs/                            |
   |  |                               |
   |  +-jquery-1.6.2.min.js           | -+
   |  +-jquery.autogrow.js            |  |
   |  +-jquery.jeditable.autogrow.js  |  |=- Javascript
   |  +-jquery.jeditable.js           |  |   components
   |  +-jquery.textarea.js            | -+
   |  +-jquery.snippet.js             | <--- syntax highlighting
   |  +-sh_c.js                       | <--- c-style highlighting
   |                                  |
   +-css/
   |  |
   |  +-jquery.snippet.css            | <--- idk wtf this does
   |                                  |      but it certainly
   +cc/                               |        does it =))
    |                                 |
    +-live.sqlite                     | <--- sqlite database
    +-live.php                        | -+
    +-save.php                        |  |=- PHP pages
    +-load.php                        | -+

You may alter the paths but that would require changing the PHP files.

Code: live.php

Until we can solve this mindblowing experience with G35h11, Wizardry and Steamworks offers M$1000 for any (l)user that can write a funny page about MathJax, which apparently:

It doesn't matter if you have $wgUseTeX set to true or false; Extension:MathJax will take over rendering of the standard <math> math tags.

takes over the wiki, and:

NB: For those wanting to add a php closing tag: There is no php closing tag at the end of this file; this is assumed to be good style. Search the web for (too much) information.

interprets everything till the bitter end as PHP. As a contribution to Uncl'Pedia.

<html4strict> <html> <head>

 <title>Colaborative Coding</title>
 <link rel=stylesheet href='/css/jquery.snippet.css'>
 <style>

* { margin:0; padding:0; } a { outline:none; } .fade { width:300px; margin:20px auto; background:#EEE; padding:12px; overflow:hidden; } .fade .tabs { float:left; overflow:auto; } .fade .tabs li { float:left; list-style:none; border:1px solid #444; margin:1px; -moz-border-radius:2px; } .fade .tabs li a { display:block; float:left; width:16px; height:16px; text-align:center; color:#000; text-decoration:none; font:bold 10pt Verdana; background:#CCC; border:1px solid #FFF; } .fade .tabs li:hover { margin:0; border-width:2px; } .fade .tabs li a.selected { border-color:#444; } .fade .items { clear:both; padding:6px 0; position:relative; top:0; left:0; height:1em; } .fade .items div { display:none; position:absolute; top:0; left:0; padding-top:6px; }

 </style>
 <script src="/js/libs/jquery-1.6.2.min.js"></script>
 <script src="/js/libs/jquery.jeditable.js"></script>
 <script src="/js/libs/jquery.jeditable.autogrow.js"></script>
 <script src="/js/libs/jquery.autogrow.js"></script>
 <script src="/js/libs/jquery.textarea.js"></script>
 <script src="/js/libs/jquery.snippet.js"></script>
 <script src="/js/libs/sh_c.js"></script>
 <script src="/js/libs/jquery.idTabs.js"></script>
 <script type="text/javascript">
 var fade = function(id,s){
   s.tabs.removeClass(s.selected);
   s.tab(id).addClass(s.selected);
   s.items.fadeOut();
   s.item(id).fadeIn();
   return false;
 };
 </script>
 <script>
   $(document).ready(function() {
     $('.autogrow').editable('save.php', {
       loadurl   : 'load.php?r=' + Math.random(),
       type      : 'autogrow',
       submit    : 'OK',
       cancel    : 'cancel',
       onblur    : 'submit',
       autogrow : {
         lineHeight : 16,
         minHeight  : 32
       },
       callback : function(value, settings) {
         location.reload();
       }
     });
     $('.autogrow').tabby();
     $('.autogrow').snippet("c",{style:"vim",transparent:true,showNum:true});
     $.fn.fadeTabs = $.idTabs.extend(fade);
     $(".fade").fadeTabs();
  });
 </script>

</head> <body>

  • <a href="#item1">1</a>
  • <a href="#item2">2</a>
  • <a href="#item3">3</a>
 <?php
   $db = new PDO('sqlite:live.sqlite'); 
   $q = $db->prepare('SELECT txt FROM data');
   $q->execute();
   echo <<< EOH
EOH;
    print stripslashes($q->fetchObject()->txt);
    echo <<< EOH
  

EOH;

   print stripslashes($q->fetchObject()->txt);
   echo <<< EOH

EOH;

   print stripslashes($q->fetchObject()->txt);
   echo <<< EOH

EOH;

 ?>

</body> </html> </html4strict>

Code: load.php

<php> <?php

 try {
   $db = new PDO('sqlite:live.sqlite');
   $q = $db->prepare('SELECT txt FROM data WHERE idx=1');
   $q->execute();
   print stripslashes($q->fetchObject()->txt);
 } catch(PDOException $e) {
   print $e->getMessage();
   return;
 }

?> </php>

Code: save.php

<php> <?php

 if(!isset($_POST['value'])) {
   print 'ERROR: No POST data.';
   return;
 }
 $data = $_POST['value'];
 try {
   $db = new PDO('sqlite:live.sqlite');
   $db->beginTransaction();
   $q = $db->prepare('UPDATE data SET txt=(SELECT txt FROM data WHERE idx=2) where idx=3');
   $q->execute();
   $q = $db->prepare('UPDATE data SET txt=(SELECT txt FROM data WHERE idx=1) where idx=2');
   $q->execute();
   $q = $db->prepare('UPDATE data SET txt=:txt WHERE idx=1');
   $q->execute(array(':txt' => addslashes($data)));
   $db->commit();
 } catch(PDOException $e) {
   die;
 }

?> </php>

Code: jquery.autogrow.js

This is a mirror, the project seems to be down. You can use this one till Chrys updates his script.

<javascript>

* Auto Expanding Text Area (1.2.2)
* by Chrys Bader (www.chrysbader.com)
* chrysb@gmail.com
*
* Special thanks to:
* Jake Chapa - jake@hybridstudio.com
* John Resig - jeresig@gmail.com
*
* Copyright (c) 2008 Chrys Bader (www.chrysbader.com)
* Licensed under the GPL (GPL-LICENSE.txt) license. 
*
*
* NOTE: This script requires jQuery to work.  Download jQuery at www.jquery.com
*
*/

(function(jQuery) {

       var self = null;

       jQuery.fn.autogrow = function(o)
       {       
               return this.each(function() {
                       new jQuery.autogrow(this, o);
               });
       };
       
   /**
    * The autogrow object.
    *
    * @constructor
    * @name jQuery.autogrow
    * @param Object e The textarea to create the autogrow for.
    * @param Hash o A set of key/value pairs to set as configuration properties.
    * @cat Plugins/autogrow
    */
       
       jQuery.autogrow = function (e, o)
       {
               this.options                    = o || {};
               this.dummy                              = null;
               this.interval                   = null;
               this.line_height                = this.options.lineHeight || parseInt(jQuery(e).css('line-height'));
               this.min_height                 = this.options.minHeight || parseInt(jQuery(e).css('min-height'));
               this.max_height                 = this.options.maxHeight || parseInt(jQuery(e).css('max-height'));;
               this.textarea                   = jQuery(e);
               
               if(this.line_height == NaN)
                 this.line_height = 0;
               
               // Only one textarea activated at a time, the one being used
               this.init();
       };
       
       jQuery.autogrow.fn = jQuery.autogrow.prototype = {
   autogrow: '1.2.2'
 };
       
       jQuery.autogrow.fn.extend = jQuery.autogrow.extend = jQuery.extend;
       
       jQuery.autogrow.fn.extend({
                                                
               init: function() {      
                       var self = this;                        
                       this.textarea.css({overflow: 'hidden', display: 'block'});
                       this.textarea.bind('focus', function() { self.startExpand() } ).bind('blur', function() { self.stopExpand() });
                       this.checkExpand();     
               },
                                                
               startExpand: function() {                               
                 var self = this;
                       this.interval = window.setInterval(function() {self.checkExpand()}, 400);
               },
               
               stopExpand: function() {
                       clearInterval(this.interval);   
               },
               
               checkExpand: function() {
                   
                       if (this.dummy == null)
                       {
this.dummy = jQuery('
');
                               this.dummy.css({
                                                                                               'font-size'  : this.textarea.css('font-size'),
                                                                                               'font-family': this.textarea.css('font-family'),
                                                                                               'width'      : this.textarea.css('width'),
                                                                                               'padding'    : this.textarea.css('padding'),
                                                                                               'line-height': this.line_height + 'px',
                                                                                               'overflow-x' : 'hidden',
                                                                                               'position'   : 'absolute',
                                                                                               'top'        : 0,
                                                                                               'left'           : -9999
                                                                                               }).appendTo('body');
                       }
                       
                       // Strip HTML tags
                       var html = this.textarea.val().replace(/(<|>)/g, );
                       
                       // IE is different, as per usual
                       if ($.browser.msie)
                       {
                               html = html.replace(/\n/g, '
new'); } else { html = html.replace(/\n/g, '
new'); } if (this.dummy.html() != html) { this.dummy.html(html); if (this.max_height > 0 && (this.dummy.height() + this.line_height > this.max_height)) { this.textarea.css('overflow-y', 'auto'); } else { this.textarea.css('overflow-y', 'hidden'); if (this.textarea.height() < this.dummy.height() + this.line_height || (this.dummy.height() < this.textarea.height())) { this.textarea.animate({height: (this.dummy.height() + this.line_height) + 'px'}, 100); } } } } });

})(jQuery); </javascript>

Code: jquery.jeditable.autogrow.js

Also as a mirror from the jeditable project:

<javascript> /*

* Autogrow textarea for Jeditable
*
* Copyright (c) 2008 Mika Tuupola
*
* Licensed under the MIT license:
*   http://www.opensource.org/licenses/mit-license.php
* 
* Depends on Autogrow jQuery plugin by Chrys Bader:
*   http://www.aclevercookie.com/facebook-like-auto-growing-textarea/
*
* Project home:
*   http://www.appelsiini.net/projects/jeditable
*
* Revision: $Id$
*
*/

$.editable.addInputType('autogrow', {

   element : function(settings, original) {
       var textarea = $('<textarea />');
       if (settings.rows) {
           textarea.attr('rows', settings.rows);
       } else {
           textarea.height(settings.height);
       }
       if (settings.cols) {
           textarea.attr('cols', settings.cols);
       } else {
           textarea.width(settings.width);
       }
       $(this).append(textarea);
       return(textarea);
   },
   plugin : function(settings, original) {
       $('textarea', this).autogrow(settings.autogrow);
   }

}); </javascript>