/**
 * Fabrique mini javascript framework containing tools
 * for easy client-sided development
 *
 * @author         MDI
 * @version        1.0 MDI: Creation
 *                 1.1 DRA: Documentation added
 *
 * TOC
 * ##BROWSER - Browser sniffer
 * ##SELECTORS - Element selectors
 */



/*** 
 * ##BROWSER
 * Browser sniffer
 *
 * Registers the browserbrand and versionnumber
 *
 * Usage:
 * if(browser.iE == 1) {
 *   //Code specifically for IE here
 * }
 */
// browser sniffer
function Browser(){
  this.uA = navigator.userAgent.toLowerCase();
  this.aN = navigator.appName.toLowerCase();
  this.iE = this.aN.indexOf('microsoft') != -1 ? 1 : 0;
  this.mac =  this.uA.indexOf('mac') != -1 ? 1 : 0;
  this.win = this.uA.indexOf('windows') != -1 ? 1 : 0;
  this.safari =  this.uA.indexOf('webkit') != -1 ? 1 : 0;
  this.opera =  this.uA.indexOf('opera') != -1 ? 1 : 0;    
  this.mozilla = this.aN.indexOf('netscape') != -1 && !this.safari ? 1 : 0;
  this.winMozilla = this.mozilla && this.win ? 1 : 0;
  this.winIE = this.iE && this.win && !this.opera ? 1 : 0;
  this.winIE6Down = this.winIE && parseInt(this.uA.split('msie ')[1].substring(0,1)) <= 6 ? 1: 0;
  this.macIE = this.iE && this.mac ? 1 : 0;
};
var browser = new Browser();




/*** 
 * ##SELECTORS
 * Methods of selecting DOM elements
 * 
 * getElementById wrapper
 *
 * Usage:
 *   var example = getEl('example');
 *   
 *   <div id="example">
 *     <p>Lorem ipsum</p>
 *   </div>
 */
if (typeof document.getElementById != 'undefined'){
  var getEl = function(id){
    var el = document.getElementById(id);
    if (!el){
      //error('elById: element width id "' + id + '" not found in DOM');
      return false;
    }
    return(el);
  };
}
else {
  error('document.getElementById not supported');
}

/*** 
 * getElementsByCSSSelector
 *
 * Usage:
 *   var examples = getElementsByCSSSelector('.example',body);
 *   
 *   <div>
 *     <p class="example">Lorem ipsum</p>
 *     <p class="example">dolor sit amet</p>
 *   </div>
 */
function getElementsByCSSSelector(selector,pN){
  var els = [];
  var iterator = 0;
  var pNs = pN;
  if (!pNs){
    pNs = Array(document);
  }
  var selectorArr = selector.split(' ');
  var cN = false;
  var currSelector = selectorArr[0];
  if (currSelector.indexOf('#') != -1){
    var elFromId  = document.getElementById(currSelector.split('#')[1]);
    if (!elFromId){
      return els;
    }
    els[0] = elFromId;
  }
  else {
    if (currSelector.indexOf('.') != -1){
      var currSelectorArr = currSelector.split('.');
      currSelector = currSelectorArr[0];
      cN = currSelectorArr[1];
      
    }
    for (var i = 0; i < pNs.length; i++){
      var elsFromTN = pNs[i].getElementsByTagName(currSelector);
      for (var j = 0; j < elsFromTN.length; j++){
        var matchedClassNames = true;
        if (cN){
          for (var k = 1; k < currSelectorArr.length; k++){
            var elCN = elsFromTN[j].className.split(' ');
            for (var l = 0; l < elCN.length; l++){
              matchedClassNames = false;
              if (currSelectorArr[k] == elCN[l]){
                matchedClassNames = true;
                //alert(elCN);
                break;
              }
            }
            if (matchedClassNames){
              continue;
            }
          }
        }
        if (!matchedClassNames){
          continue;
        }
        els[iterator] = elsFromTN[j];
        iterator++;
      }
    }
    if (iterator == 0){
      return [];
    }        
  }

  if (selectorArr.length == 1){
    return els;
  }
  var s = '';
  var space = '';
  for (var i = 1; i < selectorArr.length; i++){
    s += space + selectorArr[i];
    space = ' ';
  }
  return getElementsByCSSSelector(s,els);
};

// Simplified getElementsByClassName
function getElByClass(classStr) {
  var elWithClass = new Array();
  var el = document.getElementsByTagName('*');
  for(var i = 0; i < el.length; i++) {
    if(hasClass(el[i], classStr))
      elWithClass.push(el[i]);
  }
  return elWithClass;
}



/*** 
 * ##ELEMENTS
 * Easy methods for DOM modifying
 * 
 * emptyNode
 *
 * Usage:
 *   emptyNode(getEl('example'));
 *   
 *   <div id="example">
 *     <p>Lorem ipsum</p>
 *   </div>
 */
function emptyNode(n) {
  if(!n)
    return false;
  while (n.firstChild) {
    n.removeChild(n.firstChild);
  }
  return true;
}
// Move node
function moveNode(n, targetParentNode) {
  if(!n || targetParentNode)
    return false;
  targetParentNode.appendChild(n);
  return true;
}

// getClassNames
function getClassNames(n) {
  if(!n)
    return false;
  return n.className.split(' ');
}

// addClass
function addClass(n, classStr) {
  if(!n)
    return false;
  n.className += (n.className ? ' ' : '') + classStr;
  return true;
}

// removeClass
function removeClass(n, classStr) {
  if(!n)
    return false;

  var classNames = getClassNames(n);
  for(var i = classNames.length; i >= 0; i--) {
    if(classStr === classNames[i])
      delete(classNames[i]);
  }
  return n.className = classNames.join(' ');
}

// hasClass
function hasClass(n, classStr) {
  if(!n)
    return false;
  return inArray(classStr, getClassNames(n));
}

// in Array
function inArray(str, arr) {
  for(var i = 0; i < arr.length; i++) {
    if(str == arr[i])
      return true;
  }
  return false;
}





/*** 
 * ##EVENTS
 * Methods for handling events
 * 
 * array for DOMContentLoaded events
 *
 * Usage:
 *   ....
 */

function onLoaded(func){
  addEvent(window, 'DOMContentLoaded', func);
}

var DOMCLEvents = [];  
var executeDOMCLEvents = function(){
  if(DOMCLEvents.preventExecution){
    return;
  }
  DOMCLEvents.preventExecution = true;
  for (var i = 0; i < DOMCLEvents.length; i++){
    DOMCLEvents[i]();
  }
};

/*** 
 * event listening
 *
 * Usage:
 *   addEvent(obj, eventType, functionName);
 */
if (window.addEventListener){
  var addEvent = function(obj, eventType, functionName){
    if (eventType == 'DOMContentLoaded'){
      DOMCLEvents[DOMCLEvents.length] = functionName;
    }
    obj.addEventListener(eventType, functionName, false);
    return true;
  };
  if (/WebKit/i.test(navigator.userAgent)){ //for safari
    var _timer = setInterval(function() {
    if (/loaded|complete/.test(document.readyState)) {            
    clearInterval(_timer); executeDOMCLEvents();}}, 10);
  };
  addEvent(window,'load',executeDOMCLEvents); //for opera < 9, ..
  addEvent(window,'DOMContentLoaded',function(){DOMCLEvents.preventExecution = true;}); //not for firefox
}
else if(window.attachEvent && Function.apply){ //for ie 5.5+
  var addEvent = function(obj, eventType, functionName){
    if (eventType == 'DOMContentLoaded'){
      DOMCLEvents[DOMCLEvents.length] = functionName;
      return true;
    }
    var r = obj.attachEvent("on"+eventType, function() { functionName.apply(obj); });
    return r;
  };
  
  //document.write('<script type="text/javascript" id="__ie_onload" defer="defer" src="javascript:void(0);"><\/script>');
  
  // RMA 09-08-2008, hack to avoid message 'contains secure and nonsecure items' under https
  document.write('<script type="text/javascript" id="__ie_onload" defer="defer" src="//:"><\/script>');

  var script = document.getElementById("__ie_onload");
  script.onreadystatechange = function() {
    if (this.readyState == "complete") {
      executeDOMCLEvents();
    }
  }
}
else {
  error("event handling not supported");
}

/*** 
 * event listening
 *
 * Usage:
 *   addEvent(obj, eventType, function() {
 *     stopDefault(this);
 *   });
 */
function stopDefault(e){
  if(e.preventDefault())
    e.preventDefault();
  else
    e.returnValue = false;
}




/***
 * ##COOKIES
 * Methods for handling cookies
 * 
 * get cookies
 *
 * Usage:
 *   setCookie('example','ape note mies',365);
 */
function setCookie(cookieName,cookieValue,nDays) {
  var today = new Date();
  var expire = new Date();
  if (nDays==null || nDays==0) nDays=1;
  expire.setTime(today.getTime() + 3600000*24*nDays);
  document.cookie = cookieName+"="+escape(cookieValue) + ";expires="+expire.toGMTString()+";path=/";
};

/***
 * get cookie
 *
 * Usage:
 *   getCookie('example');
 */
function getCookie(c_name){
  if (document.cookie.length>0){
    c_start=document.cookie.indexOf(c_name + "=");
    if (c_start!=-1){ 
      c_start=c_start + c_name.length+1 
      c_end=document.cookie.indexOf(";",c_start);
      if (c_end==-1) 
        c_end=document.cookie.length;
      return unescape(document.cookie.substring(c_start,c_end));
    } 
  }
  return null;
};


// get src Element
function getTarget(e,to){
  if (window.event){
    var t = to ? window.event.toElement : window.event.srcElement;
  }
  else if (e){
    var t = to ? e.relatedTarget : e.target;
  }
  if (t == null){
    window.body;
  }  
  return t;
};


// event on part of element
function eventOnPartOfElement(e,element,to){
  var t;
  if (window.event){
    t = to ? window.event.toElement : window.event.srcElement;
  }
  else if (e){
    t = to ? e.relatedTarget : e.target;
  } 
  else {
    return false;
  }
  if (t == null){
    return false;
  }
  while (t.tagName != 'BODY' && t.tagName != 'HTML'){
    if (t == element){
      return true;
    }
    t = t.parentNode;
  }
  return false;
};


// setInlineStyle 
function setInlineStyle(el,cssText){
  if (!cssText){
    return;
  }
  var elStyle = el.getAttribute('style');
  if (elStyle){
    elStyle.setAttribute('cssText',cssText);
  }
  else {
    el.setAttribute('style',cssText);
  }
};


// handy trace
function trace(str){
  var el = getEl('trace');
  if (!el){
    return;
  }
  el.value = str;
};


// error
function error (str){
  alert(str);
  //  console.log(str);
}


// add stylesheet
var styleSheetFromScript = false;
function addStyle(selector,properties){
  if (document.styleSheets) {
    if (!styleSheetFromScript){
      styleSheetFromScript = document.createElement('style');
      styleSheetFromScript.setAttribute('type','text/css');
      document.getElementsByTagName('HEAD')[0].appendChild(styleSheetFromScript);
    }

    var lastSheet = document.styleSheets[document.styleSheets.length - 1];
    if(lastSheet && typeof lastSheet.addRule == 'object'){
      lastSheet.addRule(selector, properties);
      //status = 'ie'; 
    }
    else {
      styleSheetFromScript.appendChild(document.createTextNode(selector + ' { ' + properties + ' }'));
      //status = 'iedd'; 
    }
  }
};




/***
 * ##DIMENSION
 * Methods for getting and setting element dimensions
 */
function getScrollingPosition() {
  var position = [0, 0];
  if (typeof window.pageYOffset != 'undefined') {
    position = [
      window.pageXOffset,
      window.pageYOffset
    ];
  }
  else if (typeof document.documentElement.scrollTop
           != 'undefined' && document.documentElement.scrollTop > 0) {
    position = [
      document.documentElement.scrollLeft,
      document.documentElement.scrollTop
    ];
  }
  else if (typeof document.body.scrollTop != 'undefined') {
    position = [
      document.body.scrollLeft,
      document.body.scrollTop
    ];
  }
  return position;
}


function setScrollTop(position) {
  if (typeof window.pageYOffset != 'undefined'){
    window.pageYOffset = position;
  }
  else if (typeof document.documentElement.scrollTop
           != 'undefined' && document.documentElement.scrollTop > 0) {
    document.documentElement.scrollTop = position;
  }
  else if (typeof document.body.scrollTop != 'undefined') {
    document.body.scrollTop = position;
  }
}



/***
 * ##FLASH
 * Methods for handling Flash
 * 
 * flash adapted for this project, see wiki for last version, mdi 1.1
 * check if the proper version is installed
 *
 * Usage:
 *   hasMinFlashVersion(8);
 *
 *   createFlashObject({
 *    id:this.flashId,
 *    parentId:this.flashParentId,
 *    uri:this.uri + '?subject=' + this.subject + '&togglemode=' + this.state + '&menupath=' + this.menu.path + '&currentnodeid=' + this.menu.nodeId + '&colorscheme=' + this.menu.colorScheme,
 *    params:{'scale':'noscale','salign':'lt','wmode':'transparent'},
 *    className:'flashObject',
 *    requiredVersion:8
 *   });
 */
function hasMinFlashVersion(versionNumber){
  var versionInCache = hasMinFlashVersion.prototype.cache[versionNumber];
  if (versionInCache != undefined){
    return versionInCache;
  }
  var versionFound = false;
  if (window.ActiveXObject){
    try{
      var flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + versionNumber);
      versionFound = true;
    }
    catch(e){
      versionFound = false;
    }
  }
  else {
    if(navigator.plugins.length){
      for (var i=0; i < navigator.plugins.length; i++){
        var pluginIdent = navigator.plugins[i].description.split(" ");
        if(pluginIdent[0] == "Shockwave" && pluginIdent[1] == "Flash"){
          var versionArray = pluginIdent[2].split(".");
          versionFound = versionArray[0] >= versionNumber;
        }
      }   
    }
  }
  hasMinFlashVersion.prototype.cache[versionNumber] = versionFound;
  return versionFound;
};

hasMinFlashVersion.prototype.cache = [];

// Class FlashObject
flashObjects = [];
function FlashObject(oArg){

  this.hasRequiredVersion = hasMinFlashVersion(oArg.requiredVersion);
  this.id = oArg.id;
  this.parentId = oArg.parentId;      
  this.uri = oArg.uri;
  this.width = oArg.width ? 'width:' + oArg.width + 'px;' : '';
  this.height = oArg.height ? 'height:' + oArg.height + 'px;' : '';
  
  this.className = oArg.className ? oArg.className : '';
  this.params = oArg.params;
  this.noFocus = oArg.noFocus;
	this.inlineStyle = oArg.inlineStyle ? oArg.inlineStyle : '';
  if (this.hasRequiredVersion && (oArg.alternateContent == undefined)){ //FIXME??
    addStyle('#' + oArg.parentId + ' .alternate-content','display:none !important;');
  }
  
  // create the object
  this.create = function(){
    if ((!this.hasRequiredVersion) || (!getEl(this.parentId))){
      return;
    }
    var obj = document.createElement('object');
    obj.setAttribute('type','application/x-shockwave-flash');
    if (this.id){
      obj.setAttribute('id',this.id);
    }
    obj.setAttribute('data',this.uri);
    obj.setAttribute('src',this.uri);
     
    setInlineStyle(obj,'display:block;' + this.width + this.height + this.inlineStyle);
    if (this.noFocus){
      obj.setAttribute('tabIndex',-1);
    }
    obj.className = this.className;
    for (var i in this.params){
      var param = document.createElement('param');
      param.setAttribute('name',i);
      param.setAttribute('value',this.params[i]);
      obj.appendChild(param);
      obj.setAttribute(i,this.params[i]);
    };
    var title = document.title;
    emptyNode(getEl(this.parentId));
    getEl(this.parentId).appendChild(obj);
    document.title = title;
    
    try {
      obj.loadMovie(0,this.uri);
    }
    catch (e){
    }
  }
  
};

// make a flash object
function createFlashObject(oArg){
  //if() 
  flashObjects[flashObjects.length] = new FlashObject(oArg);
};

// create all the flash objects (onload)
function createFlashObjects(){
  for (var i in flashObjects){
    flashObjects[i].create();
  }
};

/*************************
end flash 
*************************/



// Class multiSelect, Mark Dibbets, april 2006
// Hash for external reference
var multiSelects = [];

// the Class
var MultiSelect = function(oArg){

  // variables
  this.myListId = oArg.myListId;
  this.bigListId = oArg.bigListId;
  this.buttonAddId = oArg.buttonAddId;
  this.buttonRemoveId = oArg.buttonRemoveId;
  this.ids = oArg;
  
  // add reference to Hash
  multiSelects[this.myListId] = this;
  
  
  // methods
  
  // setup the elements
  this.setup = function(){
    
    // set global elements
    this.myList = getEl(this.myListId);
    this.bigList = getEl(this.bigListId);
    
    // if select lists don't exist stop the setup
    if (!this.myList || !this.bigList){
      return;
    }
    
    // set reference to Class in all elements
    for (var i in this.ids){
      getEl(this.ids[i]).setAttribute('multiSelect',this.myListId);
    }
    
    // setup add button
    addEvent(getEl(this.buttonAddId),'click',
      function(){
        multiSelects[this.getAttribute('multiSelect')].add();
        rerenderEl(getEl('meta-nav'));
      }  
    );
    
    // setup add button
    addEvent(getEl(this.buttonRemoveId),'click',
      function(){
        multiSelects[this.getAttribute('multiSelect')].remove();
        rerenderEl(getEl('meta-nav'));
      }  
    );
    
    /* setup the same 'stop focus for options without values' 
    * eventListeners for all select boxes */ //stop focussing options without values on all list events
    var stopFocusElements = Array(this.myList,this.bigList);
    for (var i = 0 ; i < stopFocusElements.length; i++){
      addEvent(stopFocusElements[i],'change',
        function(){
          var list = this.options;
          for (var i = 0 ; i < list.length; i++){
            if (!list[i].getAttribute('value')){
              list[i].selected = false;
            }
          }
        }  
      );
    }
    
    // filter that big list
    this.filterBigList();
    
    // setup form on submit: select all options
    var form = this.myList.form;
    if (!form.getAttribute('multiSelectLength')){
      form.setAttribute('multiSelectLength',0);
      addEvent(form,'submit',
        function(){
          var listLength = this.getAttribute('multiSelectLength');
          for (var i = 1; i <= listLength; i++){
            multiSelects[this.getAttribute('multiSelect' + i)].selectAllOptionsFromMyList();
          }
        }
      );
    }
    var formListLength = form.getAttribute('multiSelectLength');
    formListLength++;
    form.setAttribute('multiSelectLength',formListLength);
    form.setAttribute('multiSelect' + formListLength,this.myListId);
  
  };
  
  // add options
  this.add = function(){
    this.transferOptions(this.bigList,this.myList);
  };
  
  // remove options
  this.remove = function(){
    this.transferOptions(this.myList,this.bigList);
  };
  
  // return an array with the selected options from a select box
  this.transferOptions = function(from,to){
    var list = from.options;
    var selectedOptions = [];
    for (var i = 0 ; i < list.length; i++){
      if (list[i].selected && list[i].getAttribute('value')){
        selectedOptions[selectedOptions.length] = list[i];
      }
    }
    /* loop again from reference 'selectedOptions' because you cannot
    *  remove elements while referecing to them from a running loop */
    for (var i = 0 ; i < selectedOptions.length; i++){
      to.appendChild(selectedOptions[i]);
    }
  };
  
  /* checks for matching values in the two lists, 
  *  if found the option in the big list will be removed 
  *  specially for lazy server sided programmers ;-) */   
  this.filterBigList = function(){
    var bigList = this.bigList.options;
    var myList = this.myList.options;
    for (var i = 0 ; i < bigList.length; i++){
      for (var j = 0 ; j < myList.length; j++){
        if (bigList[i].getAttribute('value') == myList[j].getAttribute('value') && bigList[i].getAttribute('value') != ''){
          bigList[i].parentNode.removeChild(bigList[i]);
        }
      }  
    }
  };
  
  // select all options on form submit
  this.selectAllOptionsFromMyList = function(){
    var list = this.myList.options;
    for (var i = 0 ; i < list.length; i++){
      if (list[i].getAttribute('value')){
        list[i].selected = 'selected';
      }
    }
  };
  
};

// setup all instances (use in the window onload)
var setupMultiSelects = function(){
  for (var i in multiSelects){
    multiSelects[i].setup();
  }
};

// add the event to the window onload
addEvent(window,'load',setupMultiSelects);

