ChangeLog

31 August 2013

  • Added a live example, can be accessed by clicking the Live button at the bottom of this page.

18 March 2012

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

17 March 2012

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

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 on iOS so that we can run this on a low-end server.

System Overview

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

Setup

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

live.sqlite
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

live.php
<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>
 
<div class="fade">
  <ul class="tabs">
    <li><a href="#item1">1</a></li>
    <li><a href="#item2">2</a></li>
    <li><a href="#item3">3</a></li>
  </ul>
</div>
<div class="items">
  <?php
    $db = new PDO('sqlite:live.sqlite');
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $q = $db->prepare('SELECT txt FROM data');
    $q->execute();
    echo <<< EOH
  <div id="item1"><pre class="autogrow">
EOH;
    print stripslashes($q->fetchObject()->txt);
    echo <<< EOH
  </pre></div><div id="item2"><pre class="autogrow">
EOH;
    print stripslashes($q->fetchObject()->txt);
    echo <<< EOH
  </pre></div><div id="item3"><pre class="autogrow">
EOH;
    print stripslashes($q->fetchObject()->txt);
    echo <<< EOH
  </pre></div>
EOH;
  ?>
</div>
 
</body>
</html>

Code: load.php

load.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;
  }
?>

Code: save.php

save.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;
  }
 
?>

Code: jquery.autogrow.js

This is a mirror, the project seems to be down. You can use this one till [http://www.chrysbader.com Chrys] updates his script.

jquery.jeditable.autogrow.js
* 
 * 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('<div></div>');
                                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, '<BR>new');
                        }
                        else
                        {
                                html = html.replace(/\n/g, '<br>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);

Code: jquery.jeditable.autogrow.js

Also as a mirror from the jeditable project:

jquery.jeditable.autogrow.js
/*
 * 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);
    }
});

web/collaborative_coding.txt · Last modified: 2022/04/19 08:28 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.