//----------------------------------
//- libatomos.js
//- 
//- (C)2008 Scott Elcomb
//-
//- See LICENSE file for legal information
//- See README file for usage information
//----------------------------------

//----------------------------------
//-node.js (v0.2)
//-
//-TODO: previousSibling, nextSibling, ...
//----------------------------------

// Retrieved from http://ejohn.org/blog/javascript-array-remove/
//
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

function Node(parentNode) {
  this.parentNode = (parentNode) ? parentNode : null;
  this.childNodes = new Array();
  this.firstChild = null;
  this.lastChild = null;

  this.data = new Object();

  this.appendChild = function(childNode) {
    if (! childNode.parentNode) { childNode.parentNode = this; }
    this.childNodes.push(childNode);
    this._fixChildLinks();
    return childNode;
  }

  this.removeChild = function(childNode) {
    var childIndex = -1;
    for (var i=0; i<this.childNodes.length; i++) {
      if (this.childNodes[i] === childNode) {
        childIndex = i;
      }
    }

    if (childIndex > -1) {
      this.childNodes.remove(childIndex);
      this._fixChildLinks();
    }

    return childNode;
  }

  this._fixChildLinks = function() {
    this.firstChild = this.childNodes[0];
    this.lastChild = this.childNodes[this.childNodes.length-1];
  }
}

//----------------------------------
//- system.js
//-
//- Atomic OS System Structure
//----------------------------------

var dt = new Date();

window.System = {
  Data : {
      Version : '0.0.1',
      StartTime : dt.toLocaleDateString() + ' ' + dt.toLocaleTimeString(),
    },

  Counters : {
      Libraries : 0,
      Scripts : 0,
    },

  // Special Containers...
  Libraries : {},    //... Lib container
  Root : {},         //... FS container
}

/* wash.js

    LEGAL
      WASH SE, the Web Application SHell - Simplified Edition
      (C)2004,2008 psema4Technologies
      Licensed under the LGPL v2.1

    SYNOPSIS
      <html>
        <head><title>WASH: The Web Application SHell</title>
          <script type="text/javascript" src="wash.js"></script>
          <style type="text/css">.code,.data {display: none;}</style>
        </head>
        <body onload="window.Console = new WASH({show:true});">
          <div id="myFile" class="code">alert('This is myFile!');</div>
          <div id="myOtherFile" class="data">This is myOtherFile!</div>
        </body>
       </html>

    ABOUT
      WASH is a core component of the Atomic OS project.  This implementation creates a small
      (< 10Kb) console object - providing a simple command line interpreter and access to a
      single-folder filesystem simulated using JavaScript, DOM, and CSS.

      To add a WASH console to any web application simply reference this JavaScript file from the
      applications' HTML source file and create an instance of the WASH object:

        <script type="text/javascript" src="wash.js"></script>
        <script type="text/javascript">
          // Create a default panel (hidden!) ...
          window.Console = new WASH();

          // ... or show it ...
          // window.Console = new WASH({show:true});

          // ... or position and resize it
          // window.Console = new WASH({top:0, left:0, width:400, height:200});
        </script>

      By default, WASH will be hidden when it's created.  Hide & show the WASH panel with
      Console.hide() and Console.show() respectively.

      To add mini-programs to a web application, create a <DIV> in the HTML document's body with
      an ID="{your-filename}" and a CLASS="code".  Fill the <DIV> with JavaScript.

      To add other microfiles, create a <DIV> in the HTML document's body with an
      ID="{your-filename}" and a CLASS="data".  Fill the <DIV> with text content.

    AUTHOR
      Scott Elcomb (psema4-at-gmail-dot-com)

    SEE ALSO
      http://www.psema4.com/
      http://atomos.sourceforge.net/
*/

function WASH(optsObj) {
  this.opts = (optsObj) ? optsObj : {};

  this.init = function(optsObj) {
    var opts = (optsObj) ? optsObj : {};
    var top = (opts.top) ? opts.top : 20;
    var left = (opts.left) ? opts.left : 20;
    var width = (opts.width) ? opts.width : 800;
    var height = (opts.height) ? opts.height : 500;

    this.panel = document.createElement('div');
    this.stdout = document.createElement('textarea');
    this.stdin = document.createElement('input');

    this.panel.id = 'PANEL';
    this.panel.style.position = 'absolute';
    this.panel.style.top = top + 'px';
    this.panel.style.left = left + 'px';
    this.panel.style.width = width + 'px';
    this.panel.style.height = height + 'px';
    this.panel.style.padding = '4px';
    this.panel.style.backgroundColor = '#ddd';
    this.panel.style.border = '2px outset #ddd';

    if (opts && opts.show) {
      this.panel.style.visibility = 'visible';
    } else {
      this.panel.style.visibility = 'hidden';
    }

    this.stdout.id = 'STDOUT';
    this.stdout.style.width = '100%';
    this.stdout.style.height = (height - 40) + 'px';
    this.stdout.style.backgroundColor = '#ddd';

    this.stdin.id = 'STDIN';
    this.stdin.type = 'text';
    this.stdin.style.width = '100%';

    this.panel.appendChild(this.stdout);
    this.panel.appendChild(this.stdin);
    document.getElementsByTagName('body')[0].appendChild(this.panel);

    this.panel.component = this;
    this.stdin.onkeypress = this.panel.component.keyHandler;
    this.print("Welcome to WASH SE, the Web Application SHell - Simplified Edition\n");
    this.print("(C)2004,2008 psema4Technologies\n");
    this.print("Licensed under the LGPL v2.1\n\n");
    this.print("Type ? for help.\n\n");
    if (opts && opts.show) { this.stdin.focus(); }
  }

  this.show = function() {
    this.panel.style.visibility = 'visible';
    this.stdin.focus();
  }

  this.hide = function() {
    this.panel.style.visibility = 'hidden';
  }

  this.print = function(buf) {
    this.stdout.value += buf;
    this.stdout.scrollTop = this.stdout.scrollHeight;
  }

  this.keyHandler = function(evt) {
    // NOTE: In this routine the keyword 'this' points to the element that received the event -
    //        not the WASH object.
    var e = (evt) ? evt : window.event;

    if (e.keyCode && e.keyCode === 13) {
      var Console = this.parentNode.component;
      if (Console) {
        Console.exec(Console.stdin.value);
        Console.stdin.value = '';
        Console.print("\n");
      } else {
        alert('WASH.keyHandler(): NO CONSOLE!');
      }
    }
  }

  this.exec = function(cmdline) {
    var argv = cmdline.split(/\s+/);
    var cmd = (argv[0]) ? argv[0] : 'NOP';
    var args = '';
    for (var i=1; i<argv.length; i++) { args += argv[i] + ' '; }
    args = args.replace(/\s+$/, '');

    if (cmd.toLowerCase() === 'nop') { return; }
    this.print(cmdline + "\n");

    switch(cmd.toLowerCase()) {
      case 'cat':
        var microFile = document.getElementById(argv[1]);
        if (microFile
            && (microFile.className === 'code'
             || microFile.className === 'data')
            && microFile.childNodes[0].nodeType === 3
           ) { 
          var code = microFile.childNodes[0].data + ''
          code = code.replace(/&lt;/g, '<'); // fix html entities
          this.print(code + "\n");
        }
        break;

      case 'clear':
        this.stdout.value = '';
        break;

      case 'echo':
        this.print(args + "\n");
        break;

      case 'eval':
        var retval = eval(args+'');
        if (retval !== undefined) { this.print(retval + "\n"); }
        break;

      case 'go':
        window.Console.print("Going to '" + argv[1] + "'\n");
        window.location = argv[1];
        break;

      case '?':
      case 'help':
        this.print("WASH Builtins\n\n");
        this.print("  [filename] .............. Read and execute (if possible) a microfile\n");
        this.print("  cat [filename] .......... Dumps the contents of a microfile\n");
        this.print("  clear ................... Clears the console output\n");
        this.print("  echo [text] ............. Echoes [text] to the console output\n");
        this.print("  eval [expression] ....... Evaluates a JavaScript [expression]\n");
        this.print("  go [url] ................ Go to a new URL.  Warning: Replaces this web page!\n");
        this.print("  help, ? ................. Displays the WASH Builtins help screen\n");
        this.print("  ls ...................... Lists microfiles in this HTML document\n");
        this.print("  reboot .................. Reload the current URL.  Warning for 'go' applies here too.\n");
        break;

      case 'ls':
        var files = new Array();
        var els = document.getElementsByTagName('div');
        this.print("Listing microfiles:\n\n");

        for (var i=0; i < els.length; i++) {
          if (els[i].className === 'code' || els[i].className === 'data') { files.push(els[i]); }
        }
        for (var i=0; i < files.length; i++) {
          Console.print("  " + files[i].id + "\n");
        }
        break;

      case 'reboot':
        window.location = window.location + '';
        break;

      default:
        var microFile = document.getElementById(cmd);
        if (microFile) {
          if (microFile.className === 'code' && microFile.childNodes[0].nodeType === 3) {
            var code = microFile.childNodes[0].data + ''
            code = code.replace(/&lt;/g, '<'); // fix html entities
            var retval = eval(code);
            if (retval !== undefined) { this.print(retval + "\n"); }
          } else {
            this.print("Command not executable.\n");
          }
        } else {
          this.print("Command not found.\n");
        }
    }
  }

  this.init(this.opts);
}
//----------------------------------
//- lib.js
//-
//- DOMFS Support Routines
//-   $(ref)                   Returns a DOM element reference given a ref
//-   $append(ref, buf)      * Appends buf to the innerHTML property of a given ref
//-   $delete(ref)             Removes the DOM element of a given ref
//-   $read(ref)               Returns the innerHTML property of a given ref
//-   $write(ref, buf)       * Writes buf to the innerHTML property of a given ref
//-
//-   * If a DIV with ID="StoreArea" exists in the current HTML document, this function will 
//-     autocreate a DIV (within StoreArea) if the passed reference or ID string does not
//-     already exist.
//-
//- External Module Loaders
//-   loadNewScript(url, id)   Loads a new script from url with optional id DOM identifier
//-   loadNewStyle(url, id)    (NOT IMPLEMENTED)
//-
//- Tagging Routines
//-   $getTags(ref)            Returns a list of (CSS class) tags associated with ref
//-   $hasTag(ref, tag)        Returns true if ref has the specified (CSS class) tag
//-   $setTag(ref, tag)        Sets a (CSS class) tag on the associated ref
//-
//----------------------------------


//----------------------------------
//- DOMFS Support Routines
//----------------------------------
function $(ref) {
  // Return ref if it's an HTML element.
  if (ref.nodeType) {
    return ref;

  } else { // Otherwise check to see if an element named by 'ref' exists...
    var tmpEl = document.getElementById(ref+'');
    if (tmpEl) {
      return tmpEl; // ... Returning it if so.
    }
  }

  return undefined; // Return undefined in any other case.
}

function $read(ref) {
  // Read the contents of an HTML element
  var el = $(ref);
  if (el) {
    return el.innerHTML;
  }
}

function $write(ref, buf) {
  // Write the contents of an HTML element
  if (! buf) { return; }

  var el = $(ref);
  if (el) {
    el.innerHTML = buf;

  } else { // Auto-create if element doesn't exist
    el = document.createElement('div');
    el.id = ref+'';
    el.style.display = 'none';
    $('StoreArea').appendChild(el);
    el.innerHTML = buf;
  }
}

function $append(ref, buf) {
  // Append to the contents of an HTML element
  if (! buf) { return; }

  var el = $(ref);
  if (el) {
    el.innerHTML += buf;

  } else { // Auto-create if element doesn't exist
    el = document.createElement('div');
    el.id = ref+'';
    el.style.display = 'none';
    $('StoreArea').appendChild(el);
    el.innerHTML = buf;
  }
}

function $delete(ref) {
  var el = $(ref);
  if (el) {
    var parentEl = el.parentNode;
    if (parentEl) { parentEl.removeChild(el); }
  }
}

//----------------------------------
//- External Module Loaders
//----------------------------------

function loadNewScript(url, id) {
  if (log) {
    log('loadNewScript("' + url + '", "' + id + '") called!<br/>');
    log('loadNewScript(): creating dom script element.<br/>');
  }
  var elScript = document.createElement('script');

  if (log) {
    log('loadNewScript(): setting ID and SRC properties.<br/>');
  }
  elScript.id = (id) ? id : 'CODE_' + (++System.Counters.Scripts);
  elScript.src = url;

  if (log) {
    log('loadNewScript(): appending element to dom document<br/>');
  }
  document.getElementsByTagName('head')[0].appendChild(elScript);

  if (log) { log('loadNewScript(): finished.<p/>'); }
}

function loadNewStyle(url, id) {
}


//----------------------------------
//- Tagging Routines
//----------------------------------
function $getTags(ref) {
  var el = $(ref);
  if (el) {
    var tags = el.className.split(/ /);
    return tags;
  }
}

function $hasTag(ref, tag) {
  var el = $(ref);
  if (el) {
    var tags = $getTags(el);
    for (var i=0; i<tags.length; i++) {
      if (tags[i] === tag) { return true; }
    }
  }
  return false;
}

function $setTag(ref, tag) {
  var el = $(ref);
  if (el) {
    el.className += ' ' + tag;
    el.className = el.className.replace(/^ /, '');
  }
}


//----------------------------------
//- 
//----------------------------------

