<commit_before>/*
* Flow (NextGen) v0.X
* http://github.com/bemson/Flow/
*
* Copyright 2011, Bemi Faison
* Released under the MIT License
*/
!function (window, undefined) {
 // init vars
 var sig = {}, // private signature object for priviledged access
   pkgDefs = [], // collection of package-definitions
   pkgsIdx = {}, // name index of package-definitions
   genStates = new genData( // spawn state generator
     function (name, value, parent, dataset, flags) {
       // init vars
       var state = this, // alias state
         isInvalid = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this an invalid key
           // init vars
           var hdlr = pkgDef.pkg.invalidKey; // get invalidKey handler - returns/evaluates true when the name is invalid
           // return true when there is no handler, or it's function that returns true, or a regex that returns true
           return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
         }),
         isData = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this a data key
           // init vars
           var hdlr = pkgDef.pkg.dataKey; // get dataKey handler - returns/evaluates true when the name is invalid
           // return true when there is no handler, or it's function that returns true, or a regex that returns true
           return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
         });
       // if this key is invalid or flagged as data...
       if (isInvalid || isData) {
         // exclude from dataset and don't scan
         flags.exclude = 1;
         // don't scan this value
         flags.scanValue = 0;
         // if valid data, capture in parent data
         if (isData && !isInvalid) parent.data[name] = value;
       } else { // otherwise, when this key is not invalid or data...
         // set default property values to undefined (presence reduces prototype property lookups)
         state.inContext = state.parentIndex = state.previousIndex = state.nextIndex = state.firstChildIndex = undefined;
         // capture index of this item once added
         state.index = dataset.length + 1;
         // capture depth
         state.depth = parent ? parent.depth + 1 : 1; // start depth at 1, since _flow state will be prepended later
         // set name
         state.name = parent ? name : '_root';
         // init data property - holds any attributes of this state
         state.data = {};
         // start or extend parent location
         state.location = parent ? parent.location + name + '/' : '//';
         // init child collection
         state.children = [];
         // if there is a parent state...
         if (parent) {
           // set parent index
           state.parentIndex = parent.index;
           // if there are no children, set first child index
           if (!parent.children.length) parent.firstChildIndex = state.index;
           // capture the index of this state in the parent's child collection
           state.childIndex = parent.children.push(state.index) - 1;
           // set this state as the last child of the parent
           parent.lastChildIndex = state.index;
           // if not the first child...
           if (state.childIndex) {
             // reference index of previous state
             state.previousIndex = parent.children[state.childIndex - 1];
             // reference index of this state in the previous state
             dataset[state.previousIndex - 1].nextIndex = state.index;
           }
         }
       }
     }
   );
 // define prototype base for FlowAPI instances
 function ProxyModel() {}
 // create
 function definePackage(name) {
   // package returns the private instance of it's public proxy
   function pkg(pxy) {
     // init vars
     var flow = pxy && pxy.toString(sig); // pass secret to pxy's toString
     // return the package instance with this name
     return typeof flow === 'object' && (flow.pkgs.filter(function (pkgInst) {
       return pkgInst.name === name
     })[0] || {}).pkg
   }
   // set default static properties
   pkg.init = pkg.dataKey = pkg.invalidKey = pkg.onStart = pkg.onEnd = pkg.onFinish = pkg.onTraverse = 0;
   // define new prototype-model for this package
   function Model() {}
   // chain existing FlowModel to this model
   Model.prototype = new ProxyModel();
   // define new FlowModel from this extended chain
   ProxyModel = Model;
   // expose the model's prototype for adding API methods
   pkg.api = Model.prototype;
   // define package definition object, to capture and index this package and it's model
   pkgsIdx[name] = pkgDefs.push({
     name: name,
     pkg: pkg,
     model: Model
   }) - 1;
   // return package
   return pkg;
 }
 function getStates(program) {
   // init vars
   var states = genStates(program); // parse initial states
   // set parent and childIndex of root
   states[0].parentIndex = states[0].childIndex = 0;
   // prepend flow state
   states.unshift(genStates()[0]);
   // reference index of root as child of flow
   states[0].children.push(1);
   // set name
   states[0].name = '_flow';
   // set index
   states[0].index = 0;
   // set depth
   states[0].depth = 0;
   // set location
   states[0].location = '..//';
   // reference the first and last child index
   states[0].firstChildIndex = states[0].lastChildIndex = 1;
   // return states
   return states;
 }
 function Flow(program, proxy) {
   // init vars
   var flow = this; // alias this instance
   // states for this program
   flow.states = getStates(program);
   // define shared pkg api
   flow.shared = {
     // index of the current program state
     currentIndex: 0,
     // index of the target program state
     targetIndex: 0,
     // define scoped call to direct this flow
     go: function (idx) {
       // set target to the state at the given index
       flow.target = flow.states[idx];
       // set target index
       flow.shared.targetIndex = idx;
       // set internal stop flag
       flow.stop = 0;
       // traverse towards the target
       return flow.go();
     },
     // define scoped call to stop this flow
     stop: function () {
       // set internal stop flag
       flow.stop = 1;
       // flag success in setting the stop flag
       return true;
     }
   };
   // init current state reference
   flow.current = flow.states[0];
   // init target state and looping flags
   flow.target = flow.looping = 0;
   // define a pkg instance for each definition...
   flow.pkgs = pkgDefs.map(function (pkgDef) {
     // init vars
     var pkgInst = { // init pkg instance
       // capture package name for lookups
       name: pkgDef.name
     };
     // define base package
     function pkgBase() {}
     // extend the base package prototype
     pkgBase.prototype = new pkgDef.pkg();
     // set pkg to new pkgBase
     pkgInst.pkg = new pkgBase();
     // clone states from this flow
     pkgInst.pkg.states = flow.states.map(function (state) {
       // define anonymous constructor
       function copyState() {};
       // use state as prototype for this object
       copyState.prototype = state;
       // return copy of this state
       return new copyState();
     });
     // if there is a package initialization function...
     if (pkgDef.pkg.init) {
       // initialize package instance according to the definition's function
       pkgDef.pkg.init.call(pkgInst.pkg);
     }
     // add flow and proxy properties
     // expose shared api to this package instance
     pkgInst.pkg.flow = flow.shared;
     // expose public proxy to this package instance
     pkgInst.pkg.proxy = proxy;
     // return the pkgInst
     return pkgInst;
   });
   console.log('built flow',flow);
 }
 Flow.prototype = {
   // head towards the current target
   go: function () {
     // init vars
     var flow = this, // alias self
       states = flow.states, // alias states (for minification & performance)
       shared = flow.shared, // alias shared (for minification & performance)
       dir, // direction of traversal movement
       curState = flow.current, // alias the current state (for minification & performance)
       nextIsEvent = 0, // flag when the nextInt is an event (when 0) or state index (when 1)
       nextInt = 0, // integer representing the state index or event type
       firedStop, // flag when we've fired the stop event
       firedFinish; // flag when we've fired the finish event
     // if already looping...
     if (flow.looping) {
       // flag true if there is a current target
       return !!flow.target;
     }
     // flag that this flow is looping
     flow.looping = 1;
     // fire start event
     flow.fire('Start');
     // while looping...
     while (flow.looping) {
       // if there is a target and we haven't stopped...
       if (flow.target && !flow.stop) {
         // reset firedStop and firedFinish flags
         firedStop = firedFinish = 0;
         // get traversal direction
         dir = flow.target.index - curState.index;
         // if moving backwards...
         if (dir < 0) {
           // if within this state...
           if (curState.inContext) {
             // flag that we're doing an event
             nextIsEvent = 1;
             // set to out event
             nextInt = 2;
             // flag that we've set an out event for this state
             curState.setOut = 1;
             // flag that we're out of the current state
             curState.inContext = 0;
           } else if (curState.setOut) { // or, when we've already done an out event with this state...
             // flag that we're switch the current state
             nextIsEvent = 0;
             // set to the previous or parent state
             nextInt = curState.previousIndex || curState.parentIndex;
           } else { // otherwise, when we're not in context and haven't been set outside...
             // flag that we're doing an event
             nextIsEvent = 1;
             // set to bover event
             nextInt = 4;
             // flag that we've set an out event for this state
             curState.setOut = 1;
           }
         } else if (dir) { // or, when going forwards...
           // if the next state is less-than or equal-to the target state...
           if (curState.nextIndex <= flow.target.index) {
             // if within this state...
             if (curState.inContext) {
               // flag that we're doing an event
               nextIsEvent = 1;
               // set to out event
               nextInt = 2;
               // flag that we've set an out event for this state
               curState.setOut = 1;
               // flag that we're out of the current state
               curState.inContext = 0;
             } else if (curState.setOut) { // or, when we've already done an out event with this state...
               // flag that we're switch the current state
               nextIsEvent = 0;
               // set to the next index
               nextInt = curState.nextIndex;
             } else { // otherwise, when out of context and there hasn't been an out event...
               // flag that we're doing an event
               nextIsEvent = 1;
               // set to over event
               nextInt = 3;
               // flag that we've set an out event for this state
               curState.setOut = 1;
             }
           } else { // otherwise, when the next state is not less than the target....
             // if we're in this state...
             if (curState.inContext) {
               // flag that we're switching the current state
               nextIsEvent = 0;
               // set to the first child
               nextInt = curState.firstChildIndex;
             } else { // otherwise, when we're not in this state...
               // flag that we're doing an event
               nextIsEvent = 1;
               // set to in event
               nextInt = 1;
               // flag that we're in the current state
               curState.inContext = 1;
             }
           }
         } else { // (otherwise), when on the target state...
           // flag that we're doing an event
           nextIsEvent = 1;
           // set to in or on event, based on the current context
           nextInt = curState.inContext ? 0 : 1;
           // if already in context...
           if (curState.inContext) {
             // clear internal target
             flow.target = 0;
             // clear shared target (set to negative one)
             shared.targetIndex = -1;
           }
           // flag that we're in (or now on) the current state
           curState.inContext = 1;
         }
         // if doing an event...
         if (nextIsEvent) {
           // fire traverse event with the resolved next target
           flow.fire('Traverse', [nextInt]);
         } else { // otherwise, when changing the current state...
           // reset setOut flag from the current state
           curState.setOut <commit_msg>Faster tank navigation, to support starting and stopping<commit_after>/*
* Flow (NextGen) v0.X
* http://github.com/bemson/Flow/
*
* Copyright 2011, Bemi Faison
* Released under the MIT License
*/
!function (window, undefined) {
  // init vars
  var sig = {}, // private signature object for priviledged access
    pkgDefs = [], // collection of package-definitions
    pkgsIdx = {}, // name index of package-definitions
    genStates = new genData( // spawn state generator
      function (name, value, parent, dataset, flags) {
        // init vars
        var state = this, // alias state
          isInvalid = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this an invalid key
            // init vars
            var hdlr = pkgDef.pkg.invalidKey; // get invalidKey handler - returns/evaluates true when the name is invalid
            // return true when there is no handler, or it's function that returns true, or a regex that returns true
            return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
          }),
          isData = name && !pkgDefs.every(function (pkgDef) { // flag true when a package deems this a data key
            // init vars
            var hdlr = pkgDef.pkg.dataKey; // get dataKey handler - returns/evaluates true when the name is invalid
            // return true when there is no handler, or it's function that returns true, or a regex that returns true
            return !hdlr || !(typeof hdlr === 'function' ? hdlr(name, value) : hdlr.test(name));
          });
        // if this key is invalid or flagged as data...
        if (isInvalid || isData) {
          // exclude from dataset and don't scan
          flags.exclude = 1;
          // don't scan this value
          flags.scanValue = 0;
          // if valid data, capture in parent data
          if (isData && !isInvalid) parent.data[name] = value;
        } else { // otherwise, when this key is not invalid or data...
          // set default property values to undefined (presence reduces prototype property lookups)
          state.inContext = state.parentIndex = state.previousIndex = state.nextIndex = state.firstChildIndex = undefined;
          // capture index of this item once added
          state.index = dataset.length + 1;
          // capture depth
          state.depth = parent ? parent.depth + 1 : 1; // start depth at 1, since _flow state will be prepended later
          // set name
          state.name = parent ? name : '_root';
          // init data property - holds any attributes of this state
          state.data = {};
          // start or extend parent location
          state.location = parent ? parent.location + name + '/' : '//';
          // init child collection
          state.children = [];
          // if there is a parent state...
          if (parent) {
            // set parent index
            state.parentIndex = parent.index;
            // if there are no children, set first child index
            if (!parent.children.length) parent.firstChildIndex = state.index;
            // capture the index of this state in the parent's child collection
            state.childIndex = parent.children.push(state.index) - 1;
            // set this state as the last child of the parent
            parent.lastChildIndex = state.index;
            // if not the first child...
            if (state.childIndex) {
              // reference index of previous state
              state.previousIndex = parent.children[state.childIndex - 1];
              // reference index of this state in the previous state
              dataset[state.previousIndex - 1].nextIndex = state.index;
            }
          }
        }
      }
    );
  // define prototype base for FlowAPI instances
  function ProxyModel() {}
  // create
  function definePackage(name) {
    // package returns the private instance of it's public proxy
    function pkg(pxy) {
      // init vars
      var flow = pxy && pxy.toString(sig); // pass secret to pxy's toString
      // return the package instance with this name
      return typeof flow === 'object' && (flow.pkgs.filter(function (pkgInst) {
        return pkgInst.name === name
      })[0] || {}).pkg
    }
    // set default static properties
    pkg.init = pkg.dataKey = pkg.invalidKey = pkg.onStart = pkg.onEnd = pkg.onFinish = pkg.onTraverse = 0;
    // define new prototype-model for this package
    function Model() {}
    // chain existing FlowModel to this model
    Model.prototype = new ProxyModel();
    // define new FlowModel from this extended chain
    ProxyModel = Model;
    // expose the model's prototype for adding API methods
    pkg.api = Model.prototype;
    // define package definition object, to capture and index this package and it's model
    pkgsIdx[name] = pkgDefs.push({
      name: name,
      pkg: pkg,
      model: Model
    }) - 1;
    // return package
    return pkg;
  }
  function getStates(program) {
    // init vars
    var states = genStates(program); // parse initial states
    // set parent and childIndex of root
    states[0].parentIndex = states[0].childIndex = 0;
    // prepend flow state
    states.unshift(genStates()[0]);
    // reference index of root as child of flow
    states[0].children.push(1);
    // set name
    states[0].name = '_flow';
    // set index
    states[0].index = 0;
    // set depth
    states[0].depth = 0;
    // set location
    states[0].location = '..//';
    // reference the first and last child index
    states[0].firstChildIndex = states[0].lastChildIndex = 1;
    // return states
    return states;
  }
  function Flow(program, proxy) {
    // init vars
    var flow = this; // alias this instance
    // states for this program
    flow.states = getStates(program);
    // define shared pkg api
    flow.shared = {
      // index of the current program state
      currentIndex: 0,
      // index of the target program state
      targetIndex: 0,
      // define scoped call to direct this flow
      go: function (idx) {
        // set target to the state at the given index
        flow.target = flow.states[idx];
        // set target index
        flow.shared.targetIndex = idx;
        // set internal stop flag
        flow.stop = 0;
        // traverse towards the target
        return flow.go();
      },
      // define scoped call to stop this flow
      stop: function () {
        // set internal stop flag
        flow.stop = 1;
        // flag success in setting the stop flag
        return true;
      }
    };
    // init current state reference
    flow.current = flow.states[0];
    // init target state and looping flags
    flow.target = flow.looping = 0;
    // define a pkg instance for each definition...
    flow.pkgs = pkgDefs.map(function (pkgDef) {
      // init vars
      var pkgInst = { // init pkg instance
        // capture package name for lookups
        name: pkgDef.name
      };
      // define base package
      function pkgBase() {}
      // extend the base package prototype
      pkgBase.prototype = new pkgDef.pkg();
      // set pkg to new pkgBase
      pkgInst.pkg = new pkgBase();
      // clone states from this flow
      pkgInst.pkg.states = flow.states.map(function (state) {
        // define anonymous constructor
        function copyState() {};
        // use state as prototype for this object
        copyState.prototype = state;
        // return copy of this state
        return new copyState();
      });
      // if there is a package initialization function...
      if (pkgDef.pkg.init) {
        // initialize package instance according to the definition's function
        pkgDef.pkg.init.call(pkgInst.pkg);
      }
      // add flow and proxy properties
      // expose shared api to this package instance
      pkgInst.pkg.flow = flow.shared;
      // expose public proxy to this package instance
      pkgInst.pkg.proxy = proxy;
      // return the pkgInst
      return pkgInst;
    });
  }
  Flow.prototype = {
    // head towards the current target
    go: function () {
      // init vars
      var flow = this, // alias self
        states = flow.states, // alias states (for minification & performance)
        shared = flow.shared, // alias shared (for minification & performance)
        dir, // direction of traversal movement
        curState = flow.current, // alias the current state (for minification & performance)
        nextIsEvent = 0, // flag when the nextInt is an event (when 0) or state index (when 1)
        nextInt = 0, // integer representing the state index or event type
        firedStop, // flag when we've fired the stop event
        firedFinish; // flag when we've fired the finish event
      // if already looping...
      if (flow.looping) {
        // flag true if there is a current target
        return !!flow.target;
      }
      // flag that this flow is looping
      flow.looping = 1;
      // fire start event
      flow.fire('Start');
      // while looping...
      while (flow.looping) {
        // if there is a target and we haven't stopped...
        if (flow.target && !flow.stop) {
          // reset firedStop and firedFinish flags
          firedStop = firedFinish = 0;
          // get traversal direction
          dir = flow.target.index - curState.index;
          // if going forwards or backwards...
          if (dir) {
            // if going forward on the _flow or _root state, or the target path contains the current path...
            if ((dir > 0 && curState.index < 2) || !flow.target.location.indexOf(curState.location)) {
              // if already in context...
              if (curState.inContext) {
                // flag that we're switching states
                nextIsEvent = 0;
                // target the first child
                nextInt = curState.firstChildIndex;
              } else { // otherwise, if not in context...
                // flag that we're doing an event
                nextIsEvent = 1;
                // set to in event
                nextInt = 1;
                // flag that we're in the current state
                curState.inContext = 1;
              }
            } else { // otherwise, if the target path is not in the current path...
              // if in the context of the current state...
              if (curState.inContext) {
                // flag that we're doing an event
                nextIsEvent = 1;
                // set to out event
                nextInt = 2;
                // flag that we're out of the current state
                curState.inContext = 0;
              } else { // otherwise, when out of this state...
                // if the current state's parent is an ancestor of the target state...
                if (flow.target.location.indexOf(states[curState.parentIndex].location)) {
                  // set direction to backwards
                  dir = -1;
                }
                // predict next event based on the direction
                nextInt = dir < 0 ? 4 : 3;
                // if the last event was out, or matches the calculated one...
                if (curState.lastEvent === 2 || curState.lastEvent === nextInt) {
                  // flag that we're changing states
                  nextIsEvent = 0;
                  // go forward, backward, or up based on direction
                  nextInt = dir > 0 ? curState.nextIndex : (curState.previousIndex || curState.parentIndex);
                } else { // otherwise, when the last event was not out and won't be repeated...
                  // flag that we're doing an event (the one previously calculated)
                  nextIsEvent = 1;
                }
              }
            }
          } else { // otherwise, when on the target state...
            // flag that we're doing an event
            nextIsEvent = 1;
            // set event to on or in, based on the current context
            nextInt = curState.inContext ? 0 : 1;
            // if already in context...
            if (curState.inContext) {
              // clear internal target
              flow.target = 0;
              // clear shared target (set to negative one)
              shared.targetIndex = -1;
            }
            // set context to in
            curState.inContext = 1;
          }
          // if doing an event...
          if (nextIsEvent) {
            // capture last event
            curState.lastEvent = nextInt;
            // fire traverse event with the resolved next target
            flow.fire('Traverse', [nextInt]);
          } else { // otherwise, when changing the current state...
            // reset lastEvent flag from the current state
            curState.lastEvent = 0;
            // set internal current state
            curState = flow.current = states[nextInt];
            // set shared target
            shared.currentIndex = nextInt;
          }
        } else if (!firedStop && (flow.stop || !flow.target)) { // or, when stopped and we did not fire the stop event and we've stopped...
          // note that we've fired the stop event
          firedStop = 1;
          // clear firedFinish flag
          firedFinish = 0;
          // fire stop event
          flow.fire('Stop');
        } else if (firedStop && !firedFinish && !flow.target) { // or, when we've fired stop, haven't fired finish, and there is no target...
          // set firedFinish flag
          firedFinish = 1;
          // fire finish event
          flow.fire('Finish');
        } else { // (otherwise), when none of these conditions are met...
          // flag that we're done looping
          flow.looping = 0;
        }
      }
      // return the index of the current state
      return curState.index;
    },
    // fire package event
    fire: function (evtName, args) {
      // prepend evtName with "on" prefix
      evtName = 'on' + evtName;
      // use args or array - assumes args is an array
      args = args || [];
      // with each package instance...
      this.pkgs.forEach(function (pkgInst) {
        // init vars
        var callback = pkgDefs[pkgsIdx[pkgInst.name]].pkg[evtName]; // get callback from this package instance's definition
        // if the callback is valid...
        if (callback) {
          // execute call back in scope of the package instance, with given args
          callback.apply(pkgInst.pkg, args);
        }
      });
    }
  };

  function FlowAPI(program) {
    // init vars
    var apiPkgs = {}, // define pkgs collection for this Flow
      flowProxy = new (getFlowProxy()), // create initial flow proxy
      flow = new Flow(program, flowProxy); // define (private) flow, and pass flowProxy for any packages
    // faux toString method, for accessing the private flow
    function proxyToString(key) { // faux toString method
      // return corresponding flow or default toString result
      return key === sig ? flow : Object.prototype.toString.apply(this, arguments);
    }
    // return an API container for the private flow
    function getFlowProxy() {
      // return anonymous pointer to the private flow
      function api() {
        // reference pkgs for targeted method calls
        this.pkgs = apiPkgs;
        // add custom
        this.toString = proxyToString;
      }
      // use the ProxyModel as the default prototype
      api.prototype = new ProxyModel();
      // return anonymous proxy instance
      return api;
    }
    // with each package-definition...
    pkgDefs.forEach(function (pkgDef) {
      // init vars
      var apiProxy = getFlowProxy(); // get unique proxy constructor
      // prototype this proxy for this package
      apiProxy.prototype = new pkgDef.model();
      // add package methods to this proxy's pkgs object
      apiPkgs[pkgDef.name] = new apiProxy();
    });
    // return the flow proxy
    return flowProxy;
  }
  // define object pointing to flow
  // set prototype to given model
  FlowAPI.pkg = function (name) {
    // if arguments passed...
    if (arguments.length) {
      // if given a valid name to resolve...
      if (typeof name === 'string' && /\w/.test(name)) {
        // if no package has this name, create new package definition
        if (!pkgsIdx.hasOwnProperty(name)) definePackage(name);
        // return the package definition's pkg function
        return pkgDefs[pkgsIdx[name]].pkg;
      }
      // (otherwise) return false - fire error in the future?
      return !1;
    }
    // (otherwise) return list of package names
    return pkgDefs.map(function (pkgDef) {
      // extract the package name
      return pkgDef.name;
    });
  }
  window.Flow = FlowAPI;
}(this);<|endoftext|>


<commit_before>/* jRumble v1.3 - http://jackrugile.com/jrumble - MIT License */
(function(f){f.fn.jrumble=function(g){var a=f.extend({x:2,y:2,rotation:1,speed:15,opacity:false,opacityMin:0.5},g);return this.each(function(){var b=f(this),h=a.x*2,i=a.y*2,k=a.rotation*2,g=a.speed===0?1:a.speed,m=a.opacity,n=a.opacityMin,l,j,o=function(){var e=Math.floor(Math.random()*(h+1))-h/2,a=Math.floor(Math.random()*(i+1))-i/2,c=Math.floor(Math.random()*(k+1))-k/2,d=m?Math.random()+n:1,e=e===0&&h!==0?Math.random()<0.5?1:-1:e,a=a===0&&i!==0?Math.random()<0.5?1:-1:a;b.css("display")==="inline"&&(l=true,b.css("display","inline-block"));b.css({position:"relative",left:e+"px",top:a+"px","-ms-filter":"progid:DXImageTransform.Microsoft.Alpha(Opacity="+d*100+")",filter:"alpha(opacity="+d*100+")","-moz-opacity":d,"-khtml-opacity":d,opacity:d,"-webkit-transform":"rotate("+c+"deg)","-moz-transform":"rotate("+c+"deg)","-ms-transform":"rotate("+c+"deg)","-o-transform":"rotate("+c+"deg)",transform:"rotate("+c+"deg)"})},p={left:0,top:0,"-ms-filter":"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)",filter:"alpha(opacity=100)","-moz-opacity":1,"-khtml-opacity":1,opacity:1,"-webkit-transform":"rotate(0deg)","-moz-transform":"rotate(0deg)","-ms-transform":"rotate(0deg)","-o-transform":"rotate(0deg)",transform:"rotate(0deg)"};b.bind({startRumble:function(a){a.stopPropagation();clearInterval(j);j=setInterval(o,g)},stopRumble:function(a){a.stopPropagation();clearInterval(j);l&&b.css("display","inline");b.css(p)}})})}})(jQuery);

function isValidPhoneNumber(value) {
  if (!value) return false;
  var count = value.length;
  return count == 10 || count == 11;
}
function rumbleEl(el) {
  var prevBorder = el.css('border');
  el.css('border', '1px solid #ff0000');
  el.jrumble({});
  el.trigger('startRumble');
  var demoTimeout = setTimeout(function(){el.trigger('stopRumble');
      el.css('border', prevBorder);
  }, 500);
}

$(document).on('ready', function() {
  jQuery(".timeago").timeago();
});
// Enables popover on the action pages.
$('.customize-message-popover').popover();
$('.privacy-notice-popover').popover();

// Checkbox replacement

/*!
 * ScrewDefaultButtons v2.0.6
 * http://screwdefaultbuttons.com/
 *
 * Licensed under the MIT license.
 * Copyright 2013 Matt Solano http://mattsolano.com
 * EFF/Sina custom fork: https://github.com/sinak/ScrewDefaultButtonsV2
 *
 * Date: Mon February 25 2013
 */
 (function(e,t,n,r){var i={init:function(t){var n=e.extend({image:null,width:50,height:50,disabled:false},t);return this.each(function(t){var r=e(this);var i=n.image;var s=r.data("sdb-image");if(s){i=s}if(!i){e.error("There is no image assigned for ScrewDefaultButtons")}r.wrap("
").css({display:"none"});var o=r.attr("class");var u=r.attr("onclick");var a=r.parent("div");var f=r.attr("id");var l=f&&e('label[for="'+f+'"]');if(l){a.attr("title",l.text())}a.addClass(o);a.attr("onclick",u);a.css({"background-image":i,width:n.width,height:n.height,cursor:"pointer"});var c=0;var h=-n.height;if(r.is(":disabled")){c=-(n.height*2);h=-(n.height*3)}r.on("disableBtn",function(){r.attr("disabled","disabled");c=-(n.height*2);h=-(n.height*3);r.trigger("resetBackground")});r.on("enableBtn",function(){r.removeAttr("disabled");c=0;h=-n.height;r.trigger("resetBackground")});r.on("resetBackground",function(){if(r.is(":checked")){a.css({backgroundPosition:"0 "+h+"px"})}else{a.css({backgroundPosition:"0 "+c+"px"})}});r.trigger("resetBackground");if(r.is(":checkbox")){a.on("click",function(){if(!r.is(":disabled")){r.change()}});a.on("keydown",function(e){if(e.which===32){e.preventDefault();a.trigger("click")}});a.addClass("styledCheckbox");a.attr("tabindex","0");a.attr("role","checkbox");r.on("change",function(){if(r.prop("checked")){r.prop("checked",false);a.css({backgroundPosition:"0 "+c+"px"});a.attr("aria-checked","false")}else{r.prop("checked",true);a.css({backgroundPosition:"0 "+h+"px"});a.attr("aria-checked","true")}})}else if(r.is(":radio")){a.addClass("styledRadio");var p=r.attr("name");var d=e('input[name="'+p+'"]:checked');a.attr("tabindex","-1");if(d.length>0){e(d[0].parentNode).attr("tabindex","0")}else if(t===0){a.attr("tabindex","0")}a.attr("role","radio");a.on("click",function(){if(!r.prop("checked")&&!r.is(":disabled")){r.change()}e('input[name="'+p+'"]').each(function(){e(this).parent().attr("tabindex","-1").attr("aria-checked","false")});a.attr("tabindex","0").attr("aria-checked","true");a[0].focus()});var v=e('input[name="'+p+'"]');a.on("keydown",function(t){var n;var i=e.inArray(r[0],v);var s=t.which;if(s==13||s===32){t.preventDefault();a.trigger("click")}else if(s===37||s===38){if(i>-1&&v[i-1]){n=v[i-1].parentNode;t.preventDefault();e(n).trigger("click")}}else if(s===39||s===40){if(i>-1&&v[i+1]){n=v[i+1].parentNode;t.preventDefault();e(n).trigger("click")}}});r.on("change",function(){if(r.prop("checked")){r.prop("checked",false);a.css({backgroundPosition:"0 "+c+"px"})}else{r.prop("checked",true);a.css({backgroundPosition:"0 "+h+"px"});var t=e('input[name="'+p+'"]').not(r);t.trigger("radioSwitch")}});r.on("radioSwitch",function(){a.css({backgroundPosition:"0 "+c+"px"})});var m=e(this).attr("id");var g=e('label[for="'+m+'"]');g.on("click",function(){a.trigger("click")})}if(!e.support.leadingWhitespace){var m=e(this).attr("id");var g=e('label[for="'+m+'"]');g.on("click",function(){a.trigger("click")})}})},check:function(){return this.each(function(){var t=e(this);if(!i.isChecked(t)){t.change()}})},uncheck:function(){return this.each(function(){var t=e(this);if(i.isChecked(t)){t.change()}})},toggle:function(){return this.each(function(){var t=e(this);t.change()})},disable:function(){return this.each(function(){var t=e(this);t.trigger("disableBtn")})},enable:function(){return this.each(function(){var t=e(this);t.trigger("enableBtn")})},isChecked:function(e){if(e.prop("checked")){return true}return false}};e.fn.screwDefaultButtons=function(t,n){if(i[t]){return i[t].apply(this,Array.prototype.slice.call(arguments,1))}else if(typeof t==="object"||!t){return i.init.apply(this,arguments)}else{e.error("Method "+t+" does not exist on jQuery.screwDefaultButtons")}};return this})(jQuery);

$('input:radio').screwDefaultButtons({
  image: 'url("/checkbox.png")',
  width: 15,
  height: 15
});

// Give an approximate character count guide to see how many character's are
// left in a tweet or social media card.  Tweet length isn't so accurate due to
// url shorteners and handle names (if they're inserted after the tweet is
// crafted)
function addCharacterCount(input) {
  var $input    = $(input),
      maxLength = $input.data('maxlength'),
      remaining = maxLength - $input.val().length;
  $('
~ ' + remaining + ' characters remaining (max ' + maxLength + ')
').insertAfter(input);
}

$('.charactercount').each(function () {
  addCharacterCount(this);
});

$('.charactercount').keyup(function() {
  var $element = $(this),
      maxLength = $element.data('maxlength');
      $("#" + this.id + "-counter").html("~ " + (maxLength - $element.val().length));
});
<commit_msg>Only apply radio button style override once<commit_after>/* jRumble v1.3 - http://jackrugile.com/jrumble - MIT License */
(function(f){f.fn.jrumble=function(g){var a=f.extend({x:2,y:2,rotation:1,speed:15,opacity:false,opacityMin:0.5},g);return this.each(function(){var b=f(this),h=a.x*2,i=a.y*2,k=a.rotation*2,g=a.speed===0?1:a.speed,m=a.opacity,n=a.opacityMin,l,j,o=function(){var e=Math.floor(Math.random()*(h+1))-h/2,a=Math.floor(Math.random()*(i+1))-i/2,c=Math.floor(Math.random()*(k+1))-k/2,d=m?Math.random()+n:1,e=e===0&&h!==0?Math.random()<0.5?1:-1:e,a=a===0&&i!==0?Math.random()<0.5?1:-1:a;b.css("display")==="inline"&&(l=true,b.css("display","inline-block"));b.css({position:"relative",left:e+"px",top:a+"px","-ms-filter":"progid:DXImageTransform.Microsoft.Alpha(Opacity="+d*100+")",filter:"alpha(opacity="+d*100+")","-moz-opacity":d,"-khtml-opacity":d,opacity:d,"-webkit-transform":"rotate("+c+"deg)","-moz-transform":"rotate("+c+"deg)","-ms-transform":"rotate("+c+"deg)","-o-transform":"rotate("+c+"deg)",transform:"rotate("+c+"deg)"})},p={left:0,top:0,"-ms-filter":"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)",filter:"alpha(opacity=100)","-moz-opacity":1,"-khtml-opacity":1,opacity:1,"-webkit-transform":"rotate(0deg)","-moz-transform":"rotate(0deg)","-ms-transform":"rotate(0deg)","-o-transform":"rotate(0deg)",transform:"rotate(0deg)"};b.bind({startRumble:function(a){a.stopPropagation();clearInterval(j);j=setInterval(o,g)},stopRumble:function(a){a.stopPropagation();clearInterval(j);l&&b.css("display","inline");b.css(p)}})})}})(jQuery);

function isValidPhoneNumber(value) {
  if (!value) return false;
  var count = value.length;
  return count == 10 || count == 11;
}
function rumbleEl(el) {
  var prevBorder = el.css('border');
  el.css('border', '1px solid #ff0000');
  el.jrumble({});
  el.trigger('startRumble');
  var demoTimeout = setTimeout(function(){el.trigger('stopRumble');
      el.css('border', prevBorder);
  }, 500);
}

$(document).on('ready', function() {
  jQuery(".timeago").timeago();
});
// Enables popover on the action pages.
$('.customize-message-popover').popover();
$('.privacy-notice-popover').popover();

// Checkbox replacement

/*!
 * ScrewDefaultButtons v2.0.6
 * http://screwdefaultbuttons.com/
 *
 * Licensed under the MIT license.
 * Copyright 2013 Matt Solano http://mattsolano.com
 * EFF/Sina custom fork: https://github.com/sinak/ScrewDefaultButtonsV2
 *
 * Date: Mon February 25 2013
 */
 (function(e,t,n,r){var i={init:function(t){var n=e.extend({image:null,width:50,height:50,disabled:false},t);return this.each(function(t){var r=e(this);var i=n.image;var s=r.data("sdb-image");if(s){i=s}if(!i){e.error("There is no image assigned for ScrewDefaultButtons")}r.wrap("
").css({display:"none"});var o=r.attr("class");var u=r.attr("onclick");var a=r.parent("div");var f=r.attr("id");var l=f&&e('label[for="'+f+'"]');if(l){a.attr("title",l.text())}a.addClass(o);a.attr("onclick",u);a.css({"background-image":i,width:n.width,height:n.height,cursor:"pointer"});var c=0;var h=-n.height;if(r.is(":disabled")){c=-(n.height*2);h=-(n.height*3)}r.on("disableBtn",function(){r.attr("disabled","disabled");c=-(n.height*2);h=-(n.height*3);r.trigger("resetBackground")});r.on("enableBtn",function(){r.removeAttr("disabled");c=0;h=-n.height;r.trigger("resetBackground")});r.on("resetBackground",function(){if(r.is(":checked")){a.css({backgroundPosition:"0 "+h+"px"})}else{a.css({backgroundPosition:"0 "+c+"px"})}});r.trigger("resetBackground");if(r.is(":checkbox")){a.on("click",function(){if(!r.is(":disabled")){r.change()}});a.on("keydown",function(e){if(e.which===32){e.preventDefault();a.trigger("click")}});a.addClass("styledCheckbox");a.attr("tabindex","0");a.attr("role","checkbox");r.on("change",function(){if(r.prop("checked")){r.prop("checked",false);a.css({backgroundPosition:"0 "+c+"px"});a.attr("aria-checked","false")}else{r.prop("checked",true);a.css({backgroundPosition:"0 "+h+"px"});a.attr("aria-checked","true")}})}else if(r.is(":radio")){a.addClass("styledRadio");var p=r.attr("name");var d=e('input[name="'+p+'"]:checked');a.attr("tabindex","-1");if(d.length>0){e(d[0].parentNode).attr("tabindex","0")}else if(t===0){a.attr("tabindex","0")}a.attr("role","radio");a.on("click",function(){if(!r.prop("checked")&&!r.is(":disabled")){r.change()}e('input[name="'+p+'"]').each(function(){e(this).parent().attr("tabindex","-1").attr("aria-checked","false")});a.attr("tabindex","0").attr("aria-checked","true");a[0].focus()});var v=e('input[name="'+p+'"]');a.on("keydown",function(t){var n;var i=e.inArray(r[0],v);var s=t.which;if(s==13||s===32){t.preventDefault();a.trigger("click")}else if(s===37||s===38){if(i>-1&&v[i-1]){n=v[i-1].parentNode;t.preventDefault();e(n).trigger("click")}}else if(s===39||s===40){if(i>-1&&v[i+1]){n=v[i+1].parentNode;t.preventDefault();e(n).trigger("click")}}});r.on("change",function(){if(r.prop("checked")){r.prop("checked",false);a.css({backgroundPosition:"0 "+c+"px"})}else{r.prop("checked",true);a.css({backgroundPosition:"0 "+h+"px"});var t=e('input[name="'+p+'"]').not(r);t.trigger("radioSwitch")}});r.on("radioSwitch",function(){a.css({backgroundPosition:"0 "+c+"px"})});var m=e(this).attr("id");var g=e('label[for="'+m+'"]');g.on("click",function(){a.trigger("click")})}if(!e.support.leadingWhitespace){var m=e(this).attr("id");var g=e('label[for="'+m+'"]');g.on("click",function(){a.trigger("click")})}})},check:function(){return this.each(function(){var t=e(this);if(!i.isChecked(t)){t.change()}})},uncheck:function(){return this.each(function(){var t=e(this);if(i.isChecked(t)){t.change()}})},toggle:function(){return this.each(function(){var t=e(this);t.change()})},disable:function(){return this.each(function(){var t=e(this);t.trigger("disableBtn")})},enable:function(){return this.each(function(){var t=e(this);t.trigger("enableBtn")})},isChecked:function(e){if(e.prop("checked")){return true}return false}};e.fn.screwDefaultButtons=function(t,n){if(i[t]){return i[t].apply(this,Array.prototype.slice.call(arguments,1))}else if(typeof t==="object"||!t){return i.init.apply(this,arguments)}else{e.error("Method "+t+" does not exist on jQuery.screwDefaultButtons")}};return this})(jQuery);

$('input:radio:visible').screwDefaultButtons({
  image: 'url("/checkbox.png")',
  width: 15,
  height: 15
});

// Give an approximate character count guide to see how many character's are
// left in a tweet or social media card.  Tweet length isn't so accurate due to
// url shorteners and handle names (if they're inserted after the tweet is
// crafted)
function addCharacterCount(input) {
  var $input    = $(input),
      maxLength = $input.data('maxlength'),
      remaining = maxLength - $input.val().length;
  $('
~ ' + remaining + ' characters remaining (max ' + maxLength + ')
').insertAfter(input);
}

$('.charactercount').each(function () {
  addCharacterCount(this);
});

$('.charactercount').keyup(function() {
  var $element = $(this),
      maxLength = $element.data('maxlength');
      $("#" + this.id + "-counter").html("~ " + (maxLength - $element.val().length));
});
<|endoftext|>


<commit_before>#= require "lib/jquery.smart_autocomplete"
(function() {
  Porthos.Item = (function() {
    var Ready = function(container) {
      var $container = $(container),
          $content = $('#content'),
          $sortables = $content.find('ul.sortable');

      $content.find('.datum .controls').append('
Ändra
');
      $content.find('div.editable').hide().find('div.submit').append('eller avbryt');
      $content.delegate('a.change, a.cancel', 'click', function(event) {
        event.preventDefault();
        $(this).parents('.datum, div.asset_association').find('.content > div.editable, .content > div.viewable, div.edit').toggle();
      });
      if (window.location.hash.match(/\_edit/)) {
        $(window.location.hash.replace(/\_edit/, '') + ' a.change').click();
      }

      $content.find('form.datum_edit').submit(function(event) {

        var updateView = function(datum) {
          var $datum = $('#datum_'+datum.id+' div.viewable');
          if (datum['_type'] == 'PageAssociation') {
            $('#datum_'+datum.id+' div.title').html($form.find('select option:selected').text());
          } else {
            switch(datum['_type']) {
              case 'AssetAssociation':
                $datum.find('div.title').html(datum.title);
                $datum.find('div.caption').html(datum.description);
                break;
              case 'StringField':
                if (datum.multiline && !datum.allow_rich_text) {
                  $datum.html(Porthos.Helpers.simpleFormat(datum.value));
                } else {
                  $datum.html(datum.value);
                }
                break;
              case 'LinkField':
                $datum.html($(''+datum.title+''));
                break;
              case 'Field':
                switch(datum.input_type) {
                  case 'date':
                    var date = new Date(Date.parse(datum.value));
                    $datum.html(Porthos.Helpers.strftime(date, "%Y-%m-%d"));
                    break;
                  case 'boolean':
                    $datum.html(!!datum.value ? 'Ja' : 'Nej');
                    break;
                }
                break;
              case 'FieldSet':
                var i = 0,
                    j = datum.data.length;
                for (i=0; i<j; i++) {
                  updateView(datum.data[i]);
                }
                break;
            }
          }
        };

        var disableForm = function(form) {
          form.find('input, textarea, select, checkbox, radio').attr('disabled', 'disabled');
        };

        var enableForm = function(form) {
          form.find('input, textarea, select, checkbox, radio').removeAttr('disabled');
        };

        event.preventDefault();
        var $form = $(this);
        $.ajax($form.attr('action'), {
          context: $form.parents('.datum'),
          dataType: 'json',
          data: $form.serialize(),
          type: 'PUT',
          success: function(datum, status) {
            updateView(datum);
            $(this).find('a.cancel').trigger('click');
            window.location.hash = '#done';
            enableForm($form);
          }
        });
        disableForm($form);
      });

      if ($.fn.hasOwnProperty('ckeditor')) {
        $('textarea.editor').ckeditor();
      }

      $('input.date').datepicker({
        dateFormat: 'yy-mm-dd'
      });

      $content.find('div.controls ul').hide();

      $content.delegate('h3.new', 'click', function(event) {
        event.preventDefault();
        $(this).toggleClass('active').parents('div.controls').find('ul').toggle();
      });

      $container.delegate('div.header a.toggler', 'click', function(event) {
        event.preventDefault();
        $container.find('div.header').toggle();
      });

      // TODO: Rewrite with nested containments when we have content collections
      $sortables.find('li .controls').prepend('');
      $sortables.sortable({
        handle: 'span.draghandle',
        connectWith: $sortables,
        axis: 'y',
        stop: function() {
          $sortables.each(function() {
            var $sortable = $(this),
                contents = $sortable.sortable('serialize');
            if (contents === '') {
              return;
            }
            $.ajax({
              type: 'PUT',
              url: $sortable.data('sort-uri'),
              data: contents,
              dataType: 'json'
            });
          });
        }
      });

      $('#page_tags_form').hide().find('div.submit').append('eller avbryt');
      $('#page_tags').delegate('a.edit, a.cancel', 'click', function(event) {
        event.preventDefault();
        $('#page_tags_list, #page_tags_form').toggle();
      }).find('#page_tags_list').append('
Ändra
');
      $('#item_tag_names').smartAutoComplete({minCharLimit: 3, source: '/admin/tags/autocomplete.json?taggable=Page'});
      $('#item_tag_names').bind({
        keyIn: function(ev) {
          var tag_list = ev.smartAutocompleteData.query.split(" ");
          ev.smartAutocompleteData.query = $.trim(tag_list[tag_list.length - 1]);
        },
        itemSelect: function(ev, selected_item) {
          var options = $(this).smartAutoComplete();
          var selected_value = $(selected_item).text();
          var cur_list = $(this).val().split(" ");

          cur_list[cur_list.length - 1] = selected_value;
          $(this).val(cur_list.join(" ") + " ");
          options.setItemSelected(true);
          $(this).trigger('lostFocus');
          ev.preventDefault();
        }
      });

      $('#page_publish_on_date a.toggle_publish_date, #page_published_on_form a').click(function(event) {
        event.preventDefault();
        $('#page_current_publish_on_date, #page_published_on_form').toggle();
      });

      $('#page_note').delegate('a', 'click', function(event) {
        event.preventDefault();
        $('#page_note_view, #page_note form').toggle();
      });

      $('#page_category').delegate('a.change, a.cancel', 'click', function(event) {
        event.preventDefault();
        $('#category_view, #choose_page_category_form').toggle();
      });

      $('#new_category, #page_categories_form a').click(function(event) {
        event.preventDefault();
        $('#choose_page_category_form, #page_categories_form').toggle();
      });

      (function() {
        $link_fields = $content.find('div.link_field_form');
        if ($link_fields.size() > 0) {
          var selects = [],
              $master_select = $('<select data-placeholder="Välj var du vill länka ..." style="width:450px"><option data-resource-type="" value="">Välj var du vill länka ...</option></select>');

          var updateSelects = function (selects, options) {
            $.each(selects, function(i, $select) {
              $select.append(options);
              var current_selected = $select.data('current-selected');
              if (current_selected !== '') {
                $select.find('option[value="'+current_selected+'"]').attr('selected', 'selected');
              }
              $select.trigger('liszt:updated');
            });
          };

          $link_fields.each(function() {
            var $container = $(this),
                $resource_id = $container.find('input.resource_id'),
                $resource_type = $container.find('input.resource_type'),
                $select = $master_select.clone().appendTo($container).chosen().bind('change', function(event) {
                  var $selected = $(this).find(':selected');
                  $resource_id.val($selected.val());
                  $resource_type.val($selected.data('resource-type'));
                }).data('current-selected', $resource_id.val());
            $container.find('div.link_field_resource').hide();
            selects.push($select);
          });
          $.getJSON('/admin/nodes', function(data) {
            var options = '<optgroup label="Nodes">',
                page_types = ['Page', 'Section', 'CustomPage'];
            $.each(data, function(i, node) {
              if ($.inArray(node['resource_type'], page_types) === -1) {
                options += '<option data-resource-type="Node" value="'+node['id']+'">'+node['name']+'</option>';
              }
            });
            options += '</optgroup>';
            updateSelects(selects, options);
          });

          $.getJSON('/admin/page_templates', function(data) {
            var types = {
              'none': {
                title: 'Anpassade sidor',
                options: ''
              }
            };
            $.each(data, function(i, type) {
              types[type.id] = {
                title: type.label,
                options: ''
              };
            });
            $.getJSON('/admin/items', function(data) {
              $.each(data, function(i, item) {
                var option = '<option data-resource-type="'+item['_type']+'" value="'+item['id']+'">'+item['title']+'</option>';
                if (item['_type'] === 'Page') {
                  types[item.page_template_id].options += option
                } else if (item['_type'] === 'CustomPage') {
                  types['none'].options += option;
                }
              });
              var optgroups = '';
              $.each(types, function(i, type) {
                optgroups += '<optgroup label="'+type.title+'">'+type.options+'</optgroup>';
              });

              updateSelects(selects, optgroups)
            });
          });
        }
      }).call(this);
    };

    return {
      init: function() {
        $(document).ready(function() {
          $('#items_view.show #workspace').each(function() {
            Ready(this);
          });
          Porthos.Helpers.cloneAsUrl('#item_title', '#item_uri');
        });
      }
    };
  })();

  Porthos.Item.init();
}).call(this);
<commit_msg>Fixed so that we see that an url gets cleared after saving a link field. Closes #107<commit_after>#= require "lib/jquery.smart_autocomplete"
(function() {
  Porthos.Item = (function() {
    var Ready = function(container) {
      var $container = $(container),
          $content = $('#content'),
          $sortables = $content.find('ul.sortable');

      $content.find('.datum .controls').append('
Ändra
');
      $content.find('div.editable').hide().find('div.submit').append('eller avbryt');
      $content.delegate('a.change, a.cancel', 'click', function(event) {
        event.preventDefault();
        $(this).parents('.datum, div.asset_association').find('.content > div.editable, .content > div.viewable, div.edit').toggle();
      });
      if (window.location.hash.match(/\_edit/)) {
        $(window.location.hash.replace(/\_edit/, '') + ' a.change').click();
      }

      $content.find('form.datum_edit').submit(function(event) {

        var updateView = function(datum) {
          var $datum = $('#datum_'+datum.id+' div.viewable');
          if (datum['_type'] == 'PageAssociation') {
            $('#datum_'+datum.id+' div.title').html($form.find('select option:selected').text());
          } else {
            switch(datum['_type']) {
              case 'AssetAssociation':
                $datum.find('div.title').html(datum.title);
                $datum.find('div.caption').html(datum.description);
                break;
              case 'StringField':
                if (datum.multiline && !datum.allow_rich_text) {
                  $datum.html(Porthos.Helpers.simpleFormat(datum.value));
                } else {
                  $datum.html(datum.value);
                }
                break;
              case 'LinkField':
                if (datum.url != '' || datum.resource_id != null) {
                  $datum.html($(''+datum.title+''));
                } else {
                  $datum.html('');
                }
                break;
              case 'Field':
                switch(datum.input_type) {
                  case 'date':
                    var date = new Date(Date.parse(datum.value));
                    $datum.html(Porthos.Helpers.strftime(date, "%Y-%m-%d"));
                    break;
                  case 'boolean':
                    $datum.html(!!datum.value ? 'Ja' : 'Nej');
                    break;
                }
                break;
              case 'FieldSet':
                var i = 0,
                    j = datum.data.length;
                for (i=0; i<j; i++) {
                  updateView(datum.data[i]);
                }
                break;
            }
          }
        };

        var disableForm = function(form) {
          form.find('input, textarea, select, checkbox, radio').attr('disabled', 'disabled');
        };

        var enableForm = function(form) {
          form.find('input, textarea, select, checkbox, radio').removeAttr('disabled');
        };

        event.preventDefault();
        var $form = $(this);
        $.ajax($form.attr('action'), {
          context: $form.parents('.datum'),
          dataType: 'json',
          data: $form.serialize(),
          type: 'PUT',
          success: function(datum, status) {
            updateView(datum);
            $(this).find('a.cancel').trigger('click');
            window.location.hash = '#done';
            enableForm($form);
          }
        });
        disableForm($form);
      });

      if ($.fn.hasOwnProperty('ckeditor')) {
        $('textarea.editor').ckeditor();
      }

      $('input.date').datepicker({
        dateFormat: 'yy-mm-dd'
      });

      $content.find('div.controls ul').hide();

      $content.delegate('h3.new', 'click', function(event) {
        event.preventDefault();
        $(this).toggleClass('active').parents('div.controls').find('ul').toggle();
      });

      $container.delegate('div.header a.toggler', 'click', function(event) {
        event.preventDefault();
        $container.find('div.header').toggle();
      });

      // TODO: Rewrite with nested containments when we have content collections
      $sortables.find('li .controls').prepend('');
      $sortables.sortable({
        handle: 'span.draghandle',
        connectWith: $sortables,
        axis: 'y',
        stop: function() {
          $sortables.each(function() {
            var $sortable = $(this),
                contents = $sortable.sortable('serialize');
            if (contents === '') {
              return;
            }
            $.ajax({
              type: 'PUT',
              url: $sortable.data('sort-uri'),
              data: contents,
              dataType: 'json'
            });
          });
        }
      });

      $('#page_tags_form').hide().find('div.submit').append('eller avbryt');
      $('#page_tags').delegate('a.edit, a.cancel', 'click', function(event) {
        event.preventDefault();
        $('#page_tags_list, #page_tags_form').toggle();
      }).find('#page_tags_list').append('
Ändra
');
      $('#item_tag_names').smartAutoComplete({minCharLimit: 3, source: '/admin/tags/autocomplete.json?taggable=Page'});
      $('#item_tag_names').bind({
        keyIn: function(ev) {
          var tag_list = ev.smartAutocompleteData.query.split(" ");
          ev.smartAutocompleteData.query = $.trim(tag_list[tag_list.length - 1]);
        },
        itemSelect: function(ev, selected_item) {
          var options = $(this).smartAutoComplete();
          var selected_value = $(selected_item).text();
          var cur_list = $(this).val().split(" ");

          cur_list[cur_list.length - 1] = selected_value;
          $(this).val(cur_list.join(" ") + " ");
          options.setItemSelected(true);
          $(this).trigger('lostFocus');
          ev.preventDefault();
        }
      });

      $('#page_publish_on_date a.toggle_publish_date, #page_published_on_form a').click(function(event) {
        event.preventDefault();
        $('#page_current_publish_on_date, #page_published_on_form').toggle();
      });

      $('#page_note').delegate('a', 'click', function(event) {
        event.preventDefault();
        $('#page_note_view, #page_note form').toggle();
      });

      $('#page_category').delegate('a.change, a.cancel', 'click', function(event) {
        event.preventDefault();
        $('#category_view, #choose_page_category_form').toggle();
      });

      $('#new_category, #page_categories_form a').click(function(event) {
        event.preventDefault();
        $('#choose_page_category_form, #page_categories_form').toggle();
      });

      (function() {
        var $link_fields = $content.find('div.link_field_form');
        if ($link_fields.size() > 0) {
          var selects = [],
              $master_select = $('<select data-placeholder="Ingen länk ..." style="width:450px"><option data-resource-type="" value="">Ingen länk ...</option></select>');

          var updateSelects = function (selects, options) {
            $.each(selects, function(i, $select) {
              $select.append(options);
              var current_selected = $select.data('current-selected');
              if (current_selected !== '') {
                $select.find('option[value="'+current_selected+'"]').attr('selected', 'selected');
              }
              $select.trigger('liszt:updated');
            });
          };

          $link_fields.each(function() {
            var $container = $(this),
                $resource_id = $container.find('input.resource_id'),
                $resource_type = $container.find('input.resource_type'),
                $select = $master_select.clone().appendTo($container).chosen().bind('change', function(event) {
                  var $selected = $(this).find(':selected');
                  $resource_id.val($selected.val());
                  $resource_type.val($selected.data('resource-type'));
                }).data('current-selected', $resource_id.val());
            $container.find('div.link_field_resource').hide();
            selects.push($select);
          });
          $.getJSON('/admin/nodes', function(data) {
            var options = '<optgroup label="Nodes">',
                page_types = ['Page', 'Section', 'CustomPage'];
            $.each(data, function(i, node) {
              if ($.inArray(node['resource_type'], page_types) === -1) {
                options += '<option data-resource-type="Node" value="'+node['id']+'">'+node['name']+'</option>';
              }
            });
            options += '</optgroup>';
            updateSelects(selects, options);
          });

          $.getJSON('/admin/page_templates', function(data) {
            var types = {
              'none': {
                title: 'Anpassade sidor',
                options: ''
              }
            };
            $.each(data, function(i, type) {
              types[type.id] = {
                title: type.label,
                options: ''
              };
            });
            $.getJSON('/admin/items', function(data) {
              $.each(data, function(i, item) {
                var option = '<option data-resource-type="'+item['_type']+'" value="'+item['id']+'">'+item['title']+'</option>';
                if (item['_type'] === 'Page') {
                  types[item.page_template_id].options += option
                } else if (item['_type'] === 'CustomPage') {
                  types['none'].options += option;
                }
              });
              var optgroups = '';
              $.each(types, function(i, type) {
                optgroups += '<optgroup label="'+type.title+'">'+type.options+'</optgroup>';
              });

              updateSelects(selects, optgroups)
            });
          });
        }
      }).call(this);
    };

    return {
      init: function() {
        $(document).ready(function() {
          $('#items_view.show #workspace').each(function() {
            Ready(this);
          });
          Porthos.Helpers.cloneAsUrl('#item_title', '#item_uri');
        });
      }
    };
  })();

  Porthos.Item.init();
}).call(this);
<|endoftext|>


<commit_before>enyo.kind({
	name: "moon.sample.DataListSample",
	classes: "moon enyo-fit enyo-unselectable",
	components: [
		{name: "drawers", kind: "moon.Drawers", drawers: [
			{handle: {}, controlsOpen: true, controlDrawerComponents: [
				{classes: "moon-hspacing", controlClasses: "moon-4h", components: [
					{name: "orientation", kind: "moon.ExpandablePicker", selectedIndex: 0, content: "Orientation", components: [
						{content: "vertical"},
						{content: "horizontal"}
					], style: "vertical-align: top;"},
					{name: "recordCount", kind: "moon.ExpandableInput", content: "Record Count", value: 1000, onchange: "updateRecords", style: "vertical-align: top;"},
					{name: "scrollIndex", kind: "moon.ExpandableInput", value: 0, content: "Scroll to Index", onchange: "scrollToIndex", style: "vertical-align: top;"},
					{name: "debugging", kind: "moon.ExpandablePicker", selectedIndex: 0, content: "Page Debugging", components: [
						{value: false, content: "off"},
						{value: true, content: "on"}
					], style: "vertical-align: top;"}
				]}
			]}
		], components: [
			{kind: "moon.Panels", pattern: "activity", classes: "enyo-fit", components: [
				{kind: "enyo.Control", components: [
					{name: "repeater", kind: "moon.DataList", components: [
						{classes: "enyo-border-box", style: "overflow: hidden; min-width: 230px; padding: 10px; margin: 10px;", components: [
							{name: "caption", controlClasses: "moon-2h", kind: "moon.CaptionDecorator", components: [
								{name: "button", kind: "moon.ToggleButton"}
							]}
						], bindings: [
							{from: ".model.caption", to: ".$.caption.content"},
							{from: ".repeater.side", to: ".$.caption.side"},
							{from: ".model.label", to: ".$.button.content"},
							{from: ".model.disabled", to: ".$.button.disabled"},
							{from: ".model.on", to: ".$.button.value"}
						]}
					]}
				]}
			]}
		]},
		{name: "collection", kind: "enyo.Collection"}
	],
	bindings: [
		{from: ".$.orientation.selected.content", to: ".orientation"},
		{from: ".$.debugging.selected.value", to: ".repeaterDebugging"},
		{from: ".$.recordCount.value", to: ".recordCount", debug: true, oneWay: false, transform: function (v) {return (v !== undefined && v !== null && !isNaN(v))? v: undefined;}},
		{from: ".$.collection", to: ".$.repeater.collection"},
		{from: ".side", to: ".$.repeater.side"}
	],
	generateRecords: function (amount) {
		var records = this.$.collection.records,
			add     = [],
			on;
		for (var i=records.length, len=(i+amount); i<len; ++i) {
			add.push({
				on: (on = Boolean(Math.floor(Math.random()*100) % 2 === 0)),
				disabled: on && Boolean(Math.floor(Math.random()*100) % 2 === 0),
				caption: "Item " + i,
				label: "Label " + i
			});
		}
		return add;
	},
	scrollToIndex: function (sender, event) {
		this.$.repeater.scrollToIndex(sender.getValue());
	},
	repeaterDebuggingChanged: function () {
		this.$.repeater.addRemoveClass("debug", this.repeaterDebugging);
	},
	orientationChanged: function () {
		this.set("side", this.orientation == "vertical"? "left": "bottom");
		this.$.repeater.set("orientation", this.orientation);
	},
	recordCountChanged: function () {
		var count   = this.get("recordCount"),
			num     = Math.min(Math.max(count, 0), 1000),
			records = this.$.collection.records;
		if (num != count) {
			this.set("recordCount", num);
		}
		if (records.length > num) {
			this.$.collection.remove(records.slice(num));
		} else if (records.length < num) {
			this.$.collection.add(this.generateRecords(Math.abs(records.length - num)));
		}
	}
});
<commit_msg>with the removal of the built-in orientationChanged handling in DataList we allow the original behavior to continue without it<commit_after>enyo.kind({
	name: "moon.sample.DataListSample",
	classes: "moon enyo-fit enyo-unselectable",
	components: [
		{name: "drawers", kind: "moon.Drawers", drawers: [
			{handle: {}, controlsOpen: true, controlDrawerComponents: [
				{classes: "moon-hspacing", controlClasses: "moon-4h", components: [
					{name: "orientation", kind: "moon.ExpandablePicker", selectedIndex: 0, content: "Orientation", components: [
						{content: "vertical"},
						{content: "horizontal"}
					], style: "vertical-align: top;"},
					{name: "recordCount", kind: "moon.ExpandableInput", content: "Record Count", value: 1000, onchange: "updateRecords", style: "vertical-align: top;"},
					{name: "scrollIndex", kind: "moon.ExpandableInput", value: 0, content: "Scroll to Index", onchange: "scrollToIndex", style: "vertical-align: top;"},
					{name: "debugging", kind: "moon.ExpandablePicker", selectedIndex: 0, content: "Page Debugging", components: [
						{value: false, content: "off"},
						{value: true, content: "on"}
					], style: "vertical-align: top;"}
				]}
			]}
		], components: [
			{kind: "moon.Panels", pattern: "activity", classes: "enyo-fit", components: [
				{name: "repeaterContainer", kind: "enyo.Control"}
			]}
		]},
		{name: "collection", kind: "enyo.Collection"}
	],
	bindings: [
		{from: ".$.orientation.selected.content", to: ".orientation"},
		{from: ".$.debugging.selected.value", to: ".repeaterDebugging"},
		{from: ".$.recordCount.value", to: ".recordCount", debug: true, oneWay: false, transform: function (v) {return (v !== undefined && v !== null && !isNaN(v))? v: undefined;}},
		{from: ".$.collection", to: ".$.repeater.collection"},
		{from: ".side", to: ".$.repeater.side"}
	],
	generateRecords: function (amount) {
		var records = this.$.collection.records,
			add     = [],
			on;
		for (var i=records.length, len=(i+amount); i<len; ++i) {
			add.push({
				on: (on = Boolean(Math.floor(Math.random()*100) % 2 === 0)),
				disabled: on && Boolean(Math.floor(Math.random()*100) % 2 === 0),
				caption: "Item " + i,
				label: "Label " + i
			});
		}
		return add;
	},
	scrollToIndex: function (sender, event) {
		this.$.repeater.scrollToIndex(sender.getValue());
	},
	repeaterDebuggingChanged: function () {
		if (this.$.repeater) {
			this.$.repeater.addRemoveClass("debug", this.repeaterDebugging);
		}
	},
	orientationChanged: function () {
		var props = enyo.mixin({}, [this.repeaterDefaults, {orientation: this.orientation}]),
			cp    = this.controlParent,
			c;
		if (this.$.repeater) {
			this.$.repeater.destroy();
		}
		this.set("side", this.orientation == "vertical"? "left": "bottom");
		this.controlParent = this.$.repeaterContainer;
		c = this.createComponent(props);
		c.render();
		this.controlParent = cp;
	},
	recordCountChanged: function () {
		var count   = this.get("recordCount"),
			num     = Math.min(Math.max(count, 0), 1000),
			records = this.$.collection.records;
		if (num != count) {
			this.set("recordCount", num);
		}
		if (records.length > num) {
			this.$.collection.remove(records.slice(num));
		} else if (records.length < num) {
			this.$.collection.add(this.generateRecords(Math.abs(records.length - num)));
		}
	},
	repeaterDefaults: {name: "repeater", kind: "moon.DataList", components: [
		{classes: "enyo-border-box", style: "overflow: hidden; min-width: 230px; padding: 10px; margin: 10px;", components: [
			{name: "caption", controlClasses: "moon-2h", kind: "moon.CaptionDecorator", components: [
				{name: "button", kind: "moon.ToggleButton"}
			]}
		], bindings: [
			{from: ".model.caption", to: ".$.caption.content"},
			{from: ".repeater.side", to: ".$.caption.side"},
			{from: ".model.label", to: ".$.button.content"},
			{from: ".model.disabled", to: ".$.button.disabled"},
			{from: ".model.on", to: ".$.button.value"}
		]}
	]}
});
<|endoftext|>


<commit_before>(function() {
  $(document).ready(function() {
    var $navigation = $('#navigation');

    $navigation.sortables = function() {
      if($navigation.hasClass('sortable')){
        return $navigation.find('ul');
      } else {
        return $([]);
      }
    }

    $navigation.setupSortables = function() {
      this.sortables().each(function() {
        var $list = $(this);
        $list.sortable({
          items: 'li',
          axis: 'y',
          stop: function() {
            $.ajax({
              type: 'PUT',
              url: $navigation.data('sort-uri'),
              data: $list.sortable('serialize'),
              dataType: 'json'
            });
          }
        });
      });
    }

    $navigation.teardownSortables = function() {
      this.sortables().each(function() {
        $(this).sortable('destroy');
      });
    }

    $navigation.setupSortables();

    $('a.toggle_handle').click(function(event) {
      event.preventDefault();

      $handle = $(this);
      $parent = $($handle.parents('li')[0]);
      $children = $($parent.children('ul')[0]);

      switch($handle.text()){
        case '+':
          if($children.size() > 0){
            $children.show();
          } else {
            $.ajax({
              type: 'GET',
              url: $handle.attr('href'),
              data: { 'partial': true },
              dataType: 'html',
              success: function(response){
                $parent.append(response);
                $navigation.teardownSortables();
                $navigation.setupSortables();
              }
            });
          }
          $handle.text('-');
          break;
        case '-':
          $handle.text('+');
          $children.hide();
          break;
      }
    });
  });
}).call(this);
<commit_msg>Fixed live click on open / close nodes. Closes #56<commit_after>(function() {
  $(document).ready(function() {
    var $navigation = $('#navigation');

    $navigation.sortables = function() {
      if($navigation.hasClass('sortable')){
        return $navigation.find('ul');
      } else {
        return $([]);
      }
    }

    $navigation.setupSortables = function() {
      this.sortables().each(function() {
        var $list = $(this);
        $list.sortable({
          items: 'li',
          axis: 'y',
          stop: function() {
            $.ajax({
              type: 'PUT',
              url: $navigation.data('sort-uri'),
              data: $list.sortable('serialize'),
              dataType: 'json'
            });
          }
        });
      });
    }

    $navigation.teardownSortables = function() {
      this.sortables().each(function() {
        $(this).sortable('destroy');
      });
    }

    $navigation.setupSortables();

    $navigation.find('a.toggle_handle').live('click', function(event) {
      event.preventDefault();

      $handle = $(this);
      $parent = $($handle.parents('li')[0]);
      $children = $($parent.children('ul')[0]);

      switch($handle.text()){
        case '+':
          if($children.size() > 0){
            $children.show();
          } else {
            $.ajax({
              type: 'GET',
              url: $handle.attr('href'),
              data: { 'partial': true },
              dataType: 'html',
              success: function(response){
                $parent.append(response);
                $navigation.teardownSortables();
                $navigation.setupSortables();
              }
            });
          }
          $handle.text('-');
          break;
        case '-':
          $handle.text('+');
          $children.hide();
          break;
      }
    });
  });
}).call(this);
<|endoftext|>


<commit_before>(function(µ,SMOD,GMOD,HMOD,SC){

	SC=SC({
		Config:"Config"
	});

	if(!µ.gui) µ.gui={};
	/**
	 * Creates a "form" with fieldsets from [config] data.
	 * uses "add" and "remove" actions.
	 */
	var FORM=µ.gui.form=function(config,value,header,path)
	{
		if(!(config instanceof SC.Config)) config=SC.Config.parse(config,value);
		path=Array.prototype.concat(path||[]);
		var form=parseContainer(config,header,path);
		form.classList.add("form");

		form.addEventListener("click",function(event)
		{
			var button=event.target;
			var action=button.dataset.action;
			event.stopPropagation();
			if(action)
			{
				event.stopPropagation();
				event.stopImmediatePropagation();
				event.preventDefault();

				switch (action)
				{
					case "add":
						if(button.classList.contains("addField")) button.parentNode.addField();
						else
						{
							var keyField=button.previousSibling;
							keyField.setCustomValidity("");
							if(keyField.checkValidity())
							{
								button.parentNode.parentNode.addField(keyField.value);
							}
							else keyField.setCustomValidity(keyField.validationMessage);
						}
						break;
					case "remove":
						var field=button.parentNode;
						var container=field.parentNode;
						var name=field.tagName=="LABEL"?field.dataset.name:field.firstElementChild.dataset.translation;
						container.removeField(name);
						break;
					default:
						µ.logger.warning(String.raw`unknown form action ${action}`);
				}
			}
		});

		return form;
	};
	var FIELD=µ.gui.form.field=function(config,name,path)
	{
		path=Array.prototype.concat(path||[]);
		name=name==null?"":name;
		var field;
		switch (config.type)
		{
			case "string":
				if(config.display=="textArea")
				{
					field=document.createElement("textArea");
				}
				else
				{
					field=document.createElement("input");
					field.type="text";
					if(config.pattern)field.pattern=config.pattern.toSource().match(/\/?\^?(.*?)\$?\//)[1]
				}
				field.value=config.get();
				break;
			case "boolean":
				field=document.createElement("input");
				field.type="checkbox";
				field.checked=config.get();
				break;
			case "number":
				field=document.createElement("input");
				field.type="number";
				field.value=config.get();
				field.min=config.min;
				field.step=config.step;
				field.max=config.max;
				break;
			case "select":
				field=document.createElement("select");
				for(var val of config.values)
				{
					var option=document.createElement("option");
					option.value=option.textContent=val;
					option.dataset.translation=name+"."+val;
					field.appendChild(option);
				}
				field.size=field.children.length;
				if(config.multiple)
				{
					field.multiple=true;
				}
				field.value=null;
				[].concat(config.get()).map(v=>config.values.indexOf(v)).filter(i=>i>=0).map(i=>field.children[i].selected=true);
				break;
			default:
				throw "unknown type "+config.type;
		}

		field.name=name;
		field.dataset.path=path.join(".");

		var getValue=function()
		{
			switch (config.type) {
				case "number":
					return isNaN(field.valueAsNumber)?field.value:field.valueAsNumber;
					break;
				case "boolean":
					return field.checked;
					break;
				case "select":
					if(config.multiple) return Array.from(field.selectedOptions).map(o=>o.value);
				default:
					return field.value;
			}
		}
		field.getConfig=function(){return config};
		field.isValid=function()
		{
			var valid=(!field.checkValidity||field.checkValidity()) && config.isValid(getValue());
			if(valid!==true)
            {
                field.setCustomValidity(valid||field.validationMessage||"invalid");
            }
			return valid;
		}
		field.addEventListener("change",function()
		{
			field.setCustomValidity("");
			if(field.isValid()===true)
			{
				var value=getValue();
				var formChangeEvent=new CustomEvent("formChange",{
					bubbles:true,
					detail:{
						oldValue:config.get(),
						value:value,
						key:name,
						path:path
					}
				});
				config.set(value);
				field.dispatchEvent(formChangeEvent);
			}
		},false);
		return field;
	};
	var parseContainer=function(config,header,path)
	{
		var container=document.createElement("fieldset");
		container.dataset.path=path.join(".");
		if(header!=null)
		{
			var legend=document.createElement("legend");
			legend.dataset.translation=legend.textContent=header;
			container.appendChild(legend);
		}
		else
		{
			container.classList.add("no-legend");
		}

		if(config instanceof SC.Config.Container.Array)
		{
			container.classList.add("Array");

			var addButton=document.createElement("button");
			addButton.classList.add("addField");
			addButton.dataset.action="add";
			addButton.dataset.translation="form.addButton";
			addButton.textContent="+";

			container.appendChild(addButton);
		}
		else if(config instanceof SC.Config.Container.Map)
		{
			container.classList.add("Map");

			var span=document.createElement("span");
			span.classList.add("addField");

			var input=document.createElement("input");
			input.type="text";
			input.required=true;
			span.appendChild(input);

			var addButton=document.createElement("button");
			addButton.dataset.action="add";
			addButton.dataset.translation="form.addButton";
			addButton.textContent="+";
			span.appendChild(addButton);

			container.appendChild(span);
		}
		var fields=new Map();
		for(var [name,field] of config)
		{
			var field=parseConfig(name,field,config,path);
			container.appendChild(field);
			fields.set(name,field);
		};
		container.getConfig=function(){return config};
		container.isValid=function()
		{
			return Array.from(fields.values()).reduce((v,f)=>f.isValid()&&v,true);
		}
		container.addField=function(key,value)
		{
			var field;
			if(config instanceof SC.Config.Container.Array)
			{
				value=key;
				key=config.length;
				field=config.push(value);
			}
			else if(config instanceof SC.Config.Container.Map)
			{
				field=config.add(key,value);
			}
			else return;

			field=parseConfig(key,field,config,path);
			container.appendChild(field);
			fields.set(key,field);
			var formAddEvent=new CustomEvent("formAdd",{
				bubbles:true,
				detail:{
					path:path,
					key:key,
					value:value,
					field:field
				}
			});
			container.dispatchEvent(formAddEvent);
		};
		container.removeField=function(key)
		{
			if(config instanceof SC.Config.Container.Array)
			{
				key=parseInt(key,10);
				var field=fields.get(key);
				if(field)
				{
					config.splice(key);
					container.removeChild(field);
					fields.delete(key);
					var oldIndex=key
					for(var index of fields.keys())
					{
						if(index>key)
						{
							var shiftField=fields.get(index);
							if(shiftField.tagName=="LABEL")
							{//Field
								shiftField.dataset.name=oldIndex;
								shiftField.querySelector("[name]").name=oldIndex;
							}
							else
							{//container
								shiftField.firstElementChild.dataset.translation=shiftField.firstElementChild.textContent=oldIndex;
								var oldPath=shiftField.dataset.path;
								var newPath=oldPath.replace(new RegExp(index+"$"),oldIndex);
								Array.from(shiftField.querySelectorAll('[data-path^="'+oldPath+'"]'))
								.forEach(e=>e.dataset.path=e.dataset.path.replace(oldPath,newPath));
								shiftField.dataset.path=newPath;
							}
							fields.set(oldIndex,shiftField);
							oldIndex=index;
						}
					}
				}
				else return;
			}
			else if(config instanceof SC.Config.Container.Map)
			{
				config.remove(key);
				var field=fields.get(key);
				container.removeChild(field);
				fields.delete(key);
			}
			else return;

			var formAddEvent=new CustomEvent("formRemove",{
				bubbles:true,
				detail:{
					key:key,
					path:path
				}
			});
			container.dispatchEvent(formAddEvent);
		}
		return container;
	};
	var parseConfig=function(name,config,parent,path)
	{
		if(config instanceof SC.Config.Container)
		{
			var subPath=path.concat(name);
			var container=parseContainer(config,name,subPath);
			if(parent instanceof SC.Config.Container.Array || parent instanceof SC.Config.Container.Map)
			{
				var removeButton=document.createElement("button")
				removeButton.classList.add("removeField");
				removeButton.dataset.action="remove";
				removeButton.dataset.translation="form.removeButton"
				removeButton.textContent="-";
				container.appendChild(removeButton);
			}
			return container;
		}
		else
		{
			var label=document.createElement("label");
			label.dataset.name=name;

			var span=document.createElement("span");
			span.classList.add("label");
			span.dataset.translation=span.textContent=name;
			label.appendChild(span);

			var field=FIELD(config,name,path);
			label.appendChild(field);
			label.isValid=()=>field.isValid();

			if(parent instanceof SC.Config.Container.Array || parent instanceof SC.Config.Container.Map)
			{
				var removeButton=document.createElement("button")
				removeButton.classList.add("removeField");
				removeButton.dataset.action="remove";
				removeButton.dataset.translation="form.removeButton"
				removeButton.textContent="-";
				label.appendChild(removeButton);
			}

			return label;
		}
	}
	SMOD("gui.form",FORM);

})(Morgas,Morgas.setModule,Morgas.getModule,Morgas.hasModule,Morgas.shortcut);
<commit_msg>configManager add fields<commit_after>(function(µ,SMOD,GMOD,HMOD,SC){

	SC=SC({
		Config:"Config"
	});

	if(!µ.gui) µ.gui={};
	/**
	 * Creates a "form" with fieldsets from [config] data.
	 * uses "add" and "remove" actions.
	 */
	var FORM=µ.gui.form=function(config,value,header,path)
	{
		if(!(config instanceof SC.Config)) config=SC.Config.parse(config,value);
		path=Array.prototype.concat(path||[]);
		var form=parseContainer(config,header,path);
		form.classList.add("form");

		form.addEventListener("click",function(event)
		{
			var button=event.target;
			var action=button.dataset.action;
			event.stopPropagation();
			if(action)
			{
				event.stopPropagation();
				event.stopImmediatePropagation();
				event.preventDefault();

				switch (action)
				{
					case "add":
						if(button.classList.contains("addField")) button.parentNode.addField();
						else
						{
							var keyField=button.previousSibling;
							keyField.setCustomValidity("");
							if(keyField.checkValidity())
							{
								button.parentNode.parentNode.addField(keyField.value);
							}
							else keyField.setCustomValidity(keyField.validationMessage);
						}
						break;
					case "remove":
						var field=button.parentNode;
						var container=field.parentNode;
						var name=field.tagName=="LABEL"?field.dataset.name:field.firstElementChild.dataset.translation;
						container.removeField(name);
						break;
					default:
						µ.logger.warning(String.raw`unknown form action ${action}`);
				}
			}
		});

		return form;
	};
	var FIELD=µ.gui.form.field=function(config,name,path)
	{
		path=Array.prototype.concat(path||[]);
		name=name==null?"":name;
		var field;
		switch (config.type)
		{
			case "string":
				if(config.display=="textArea")
				{
					field=document.createElement("textArea");
				}
				else
				{
					field=document.createElement("input");
					field.type="text";
					if(config.pattern)field.pattern=config.pattern.toSource().match(/\/?\^?(.*?)\$?\//)[1]
				}
				field.value=config.get();
				break;
			case "boolean":
				field=document.createElement("input");
				field.type="checkbox";
				field.checked=config.get();
				break;
			case "number":
				field=document.createElement("input");
				field.type="number";
				field.value=config.get();
				field.min=config.min;
				field.step=config.step;
				field.max=config.max;
				break;
			case "select":
				field=document.createElement("select");
				for(var val of config.values)
				{
					var option=document.createElement("option");
					option.value=option.textContent=val;
					option.dataset.translation=name+"."+val;
					field.appendChild(option);
				}
				field.size=field.children.length;
				if(config.multiple)
				{
					field.multiple=true;
				}
				field.value=null;
				[].concat(config.get()).map(v=>config.values.indexOf(v)).filter(i=>i>=0).map(i=>field.children[i].selected=true);
				break;
			default:
				throw "unknown type "+config.type;
		}

		field.name=name;
		field.dataset.path=path.join(".");

		var getValue=function()
		{
			switch (config.type) {
				case "number":
					return isNaN(field.valueAsNumber)?field.value:field.valueAsNumber;
					break;
				case "boolean":
					return field.checked;
					break;
				case "select":
					if(config.multiple) return Array.from(field.selectedOptions).map(o=>o.value);
				default:
					return field.value;
			}
		}
		field.getConfig=function(){return config};
		field.isValid=function()
		{
			var valid=(!field.checkValidity||field.checkValidity()) && config.isValid(getValue());
			if(valid!==true)
            {
                field.setCustomValidity(valid||field.validationMessage||"invalid");
            }
			return valid;
		}
		field.addEventListener("change",function()
		{
			field.setCustomValidity("");
			if(field.isValid()===true)
			{
				var value=getValue();
				var formChangeEvent=new CustomEvent("formChange",{
					bubbles:true,
					detail:{
						oldValue:config.get(),
						value:value,
						key:name,
						path:path
					}
				});
				config.set(value);
				field.dispatchEvent(formChangeEvent);
			}
		},false);
		return field;
	};
	var parseContainer=function(config,header,path)
	{
		var container=document.createElement("fieldset");
		container.dataset.path=path.join(".");
		if(header!=null)
		{
			var legend=document.createElement("legend");
			legend.dataset.translation=legend.textContent=header;
			container.appendChild(legend);
		}
		else
		{
			container.classList.add("no-legend");
		}

		if(config instanceof SC.Config.Container.Array)
		{
			container.classList.add("Array");

			var addButton=document.createElement("button");
			addButton.classList.add("addField");
			addButton.dataset.action="add";
			addButton.dataset.translation="form.addButton";
			addButton.textContent="+";

			container.appendChild(addButton);
		}
		else if(config instanceof SC.Config.Container.Map)
		{
			container.classList.add("Map");

			var span=document.createElement("span");
			span.classList.add("addField");

			var input=document.createElement("input");
			input.type="text";
			input.required=true;
			span.appendChild(input);

			var addButton=document.createElement("button");
			addButton.dataset.action="add";
			addButton.dataset.translation="form.addButton";
			addButton.textContent="+";
			span.appendChild(addButton);

			container.appendChild(span);
		}
		var fields=new Map();
		for(var [name,field] of config)
		{
			var field=parseConfig(name,field,config,path);
			container.appendChild(field);
			fields.set(name,field);
		};
		container.getConfig=function(){return config};
		container.isValid=function()
		{
			return Array.from(fields.values()).reduce((v,f)=>f.isValid()&&v,true);
		}
		container.addField=function(key,value)
		{
			var field;
			if(config instanceof SC.Config.Container.Array)
			{
				value=key;
				key=config.length;
				field=config.push(value);
			}
			else if(config instanceof SC.Config.Container.Map)
			{
				field=config.add(key,value);
			}
			else return;

			var domField=parseConfig(key,field,config,path);
			container.appendChild(domField);
			fields.set(key,domField);
			var formAddEvent=new CustomEvent("formAdd",{
				bubbles:true,
				detail:{
					path:path,
					key:key,
					value:value,
					field:field,
					element:domField
				}
			});
			container.dispatchEvent(formAddEvent);
		};
		container.removeField=function(key)
		{
			if(config instanceof SC.Config.Container.Array)
			{
				key=parseInt(key,10);
				var field=fields.get(key);
				if(field)
				{
					config.splice(key);
					container.removeChild(field);
					fields.delete(key);
					var oldIndex=key
					for(var index of fields.keys())
					{
						if(index>key)
						{
							var shiftField=fields.get(index);
							if(shiftField.tagName=="LABEL")
							{//Field
								shiftField.dataset.name=oldIndex;
								shiftField.querySelector("[name]").name=oldIndex;
							}
							else
							{//container
								shiftField.firstElementChild.dataset.translation=shiftField.firstElementChild.textContent=oldIndex;
								var oldPath=shiftField.dataset.path;
								var newPath=oldPath.replace(new RegExp(index+"$"),oldIndex);
								Array.from(shiftField.querySelectorAll('[data-path^="'+oldPath+'"]'))
								.forEach(e=>e.dataset.path=e.dataset.path.replace(oldPath,newPath));
								shiftField.dataset.path=newPath;
							}
							fields.set(oldIndex,shiftField);
							oldIndex=index;
						}
					}
				}
				else return;
			}
			else if(config instanceof SC.Config.Container.Map)
			{
				config.remove(key);
				var field=fields.get(key);
				container.removeChild(field);
				fields.delete(key);
			}
			else return;

			var formAddEvent=new CustomEvent("formRemove",{
				bubbles:true,
				detail:{
					key:key,
					path:path
				}
			});
			container.dispatchEvent(formAddEvent);
		}
		return container;
	};
	var parseConfig=function(name,config,parent,path)
	{
		if(config instanceof SC.Config.Container)
		{
			var subPath=path.concat(name);
			var container=parseContainer(config,name,subPath);
			if(parent instanceof SC.Config.Container.Array || parent instanceof SC.Config.Container.Map)
			{
				var removeButton=document.createElement("button")
				removeButton.classList.add("removeField");
				removeButton.dataset.action="remove";
				removeButton.dataset.translation="form.removeButton"
				removeButton.textContent="-";
				container.appendChild(removeButton);
			}
			return container;
		}
		else
		{
			var label=document.createElement("label");
			label.dataset.name=name;

			var span=document.createElement("span");
			span.classList.add("label");
			span.dataset.translation=span.textContent=name;
			label.appendChild(span);

			var field=FIELD(config,name,path);
			label.appendChild(field);
			label.isValid=()=>field.isValid();

			if(parent instanceof SC.Config.Container.Array || parent instanceof SC.Config.Container.Map)
			{
				var removeButton=document.createElement("button")
				removeButton.classList.add("removeField");
				removeButton.dataset.action="remove";
				removeButton.dataset.translation="form.removeButton"
				removeButton.textContent="-";
				label.appendChild(removeButton);
			}

			return label;
		}
	}
	SMOD("gui.form",FORM);

})(Morgas,Morgas.setModule,Morgas.getModule,Morgas.hasModule,Morgas.shortcut);
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require analytics
//= require jquery
//= require jquery_ujs
//= require turbolinks

// jQuery plugin for drop-in fix binded events problem caused by Turbolinks
//= require jquery.turbolinks

//= require bootstrap
//= require applied_techniques
//= require_tree .
<commit_msg>remove analytics js<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks

// jQuery plugin for drop-in fix binded events problem caused by Turbolinks
//= require jquery.turbolinks

//= require bootstrap
//= require applied_techniques
//= require_tree .
<|endoftext|>