http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/instance.service.js
----------------------------------------------------------------------
diff --git a/src/app/instance/instance.service.js 
b/src/app/instance/instance.service.js
new file mode 100644
index 0000000..6e809f4
--- /dev/null
+++ b/src/app/instance/instance.service.js
@@ -0,0 +1,485 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+/*global $:false */
+/*global vkbeautify:false */
+
+/**
+ * @ngdoc service
+ * @name angApp.InstanceService
+ * @description
+ * # InstanceService
+ * Service in the angApp.
+ */
+angular.module('odeConsole')
+  .factory('InstanceService', function ($log, $q, xmlFilter, SoapService, 
IMAPI_ENDPOINT) {
+
+  var PMAPI_NS = 'http://www.apache.org/ode/pmapi';
+
+  /** private functions **/
+  var nsResolver = function (prefix) {
+    var ns = {
+      'ns'     : 'http://www.apache.org/ode/pmapi/types/2006/08/02/',
+      'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/',
+      'pmapi'  : 'http://www.apache.org/ode/pmapi'
+    };
+
+    return ns[prefix] || null;
+  };
+
+  var mapInstanceInfo = function (instanceInfoElement) {
+    var processNameEl = instanceInfoElement.xpath('ns:process-name', 
nsResolver),
+        rootScopeEl   = instanceInfoElement.xpath('ns:root-scope', nsResolver),
+        iObj = {
+      iid: instanceInfoElement.xpath('ns:iid', nsResolver).text(),
+      pid: instanceInfoElement.xpath('ns:pid', nsResolver).text(),
+      processNameShort: processNameEl.text(),
+      processNameFull: 
SoapService.qnameToString(SoapService.createQNameObj(processNameEl.text(), 
processNameEl[0])),
+      processQName: SoapService.createQNameObj(processNameEl.text(), 
processNameEl[0]),
+      status: instanceInfoElement.xpath('ns:status', nsResolver).text(),
+      started: instanceInfoElement.xpath('ns:dt-started', nsResolver).text(),
+      lastActive: instanceInfoElement.xpath('ns:dt-last-active', 
nsResolver).text(),
+      rootScope: {id: rootScopeEl.attr('siid'),
+                  name: rootScopeEl.attr('name'),
+                  status: rootScopeEl.attr('status'),
+                  modelId: rootScopeEl.attr('modelId')}
+    };
+
+    if (instanceInfoElement.xpath('ns:fault-info', nsResolver).length !== 0) {
+      var faultNameEl = instanceInfoElement.xpath('ns:fault-info/ns:name', 
nsResolver);
+      iObj.fault = {
+        name: SoapService.createQNameObj(faultNameEl.text(), faultNameEl[0]),
+        explanation: instanceInfoElement.xpath('ns:fault-info/ns:explanation', 
nsResolver).text(),
+        lineNumber: instanceInfoElement.xpath('ns:fault-info/ns:line-number', 
nsResolver).text(),
+        aiid: instanceInfoElement.xpath('ns:fault-info/ns:aiid', 
nsResolver).text()
+      };
+    }
+
+    if (instanceInfoElement.xpath('ns:failures', nsResolver).length !== 0) {
+      iObj.failures = {
+        failure: instanceInfoElement.xpath('ns:failures/ns:dt-failure', 
nsResolver).text(),
+        count: instanceInfoElement.xpath('ns:failures/ns:count', 
nsResolver).text()
+      };
+    }
+
+    if 
(instanceInfoElement.xpath('ns:correlation-properties/ns:correlation-property', 
nsResolver).length !== 0) {
+      var cProps = 
instanceInfoElement.xpath('ns:correlation-properties/ns:correlation-property', 
nsResolver);
+      iObj.correlationProperties = [];
+      for (var j = 0; j < cProps.length; j++) {
+        var propEl = angular.element(cProps[j]);
+        iObj.correlationProperties.push(
+          { name: SoapService.createQNameObj(propEl.attr('propertyName'), 
propEl[0]), 
+            value: propEl.text()
+          });
+      }
+    }
+
+    return iObj;
+  };
+
+  var mapScopeInfo = function (scopeInfoElement) {
+    var si = {};
+    si.siid = scopeInfoElement.xpath('ns:siid', nsResolver).text();
+    si.name = scopeInfoElement.xpath('ns:name', nsResolver).text();
+    si.status = scopeInfoElement.xpath('ns:status', nsResolver).text();
+
+    // variables
+    si.variables = [];
+    scopeInfoElement.xpath('ns:variables/ns:variable-ref', 
nsResolver).each(function() {
+      var vr = {};
+      vr.iid = Number($(this).attr('iid'));
+      vr.siid = Number($(this).attr('siid'));
+      vr.name = $(this).attr('name');
+      vr.scope = { name: si.name, status: si.status };
+
+      si.variables.push(vr);
+    });
+
+    // endpoints
+    si.endpoints = [];
+    scopeInfoElement.xpath('ns:endpoints/ns:endpoint-ref', 
nsResolver).each(function() {
+      var er = {};
+      er.siid = si.siid;
+      er.partnerLink = $(this).attr('partner-link');
+      er.partnerRole = $(this).attr('partner-role');
+      er.scope = { name: si.name, status: si.status };
+      er.value = vkbeautify.xml($(this).first().html().trim(), 2);
+      si.endpoints.push(er);
+    });
+
+    // activities
+    si.activities = [];
+    scopeInfoElement.xpath('ns:activities/ns:activity-info', 
nsResolver).each(function() {
+      var act = {};
+      act.name = $(this).xpath('ns:name', nsResolver).text();
+      act.type = $(this).xpath('ns:type', nsResolver).text();
+      act.status = $(this).xpath('ns:status', nsResolver).text();
+      act.enabled = $(this).xpath('ns:dt-enabled', nsResolver).text();
+      act.started = $(this).xpath('ns:dt-started', nsResolver).text();
+      act.completed = $(this).xpath('ns:dt-completed', nsResolver).text();
+      act.aiid = Number($(this).xpath('ns:aiid', nsResolver).text());
+      var scope = $(this).xpath('ns:scope', nsResolver);
+      act.scope = {siid : Number(scope.attr('siid')), name: 
scope.attr('name'), modelId: Number(scope.attr('modelId')), status: si.status };
+      si.activities.push(act);
+      
+      var failure = $(this).xpath('ns:failure', nsResolver);
+      if (failure.length > 0) {
+        act.failure = {};
+        act.failure.failure = failure.xpath('ns:dt-failure', 
nsResolver).text();
+        act.failure.retries = failure.xpath('ns:retries', nsResolver).text();
+        act.failure.reason = failure.xpath('ns:reason', nsResolver).text();
+        act.failure.actions = failure.xpath('ns:actions', 
nsResolver).text().split(' ');
+      }
+    });
+
+    // children
+    si.children = [];
+    // add child references
+    scopeInfoElement.xpath('ns:children/ns:child-ref', 
nsResolver).each(function() {
+      var cr = {};
+      cr.siid = Number($(this).attr('siid'));
+      cr.name = $(this).attr('name');
+      cr.status = $(this).attr('status');
+      cr.modelId = Number($(this).attr('modelId'));
+      cr.isRef = true;
+
+      si.children.push(cr);
+    });
+
+    return si;
+  };
+
+  /** public functions **/
+  var is = {};
+
+  /**
+   * List instances and only return summary information about the instance,
+   * combined with all correlation properties.
+   * 
+   * The request identifies the process instances using a filter that can
+   * select instances with a given name, status, property values, etc.
+   * Without a filter, the operation returns all process instances up to a
+   * specified <code>limit<.code>. The request also indicates which key fields
+   * to use for ordering the results.
+   * </p>
+   *
+   * <p>
+   * The filter element can be used to narrow down the list of process 
definitions
+   * by applying selection criteria. There are six filters that can be applied:
+   * <ul>
+   * <li><p>name -- Only process instances with this local name.</p></li>
+   * <li><p>namespace -- Only process instances with this namespace 
URI.</p></li>
+   * <li><p>status -- Only process instances with these status 
code(s).</p></li>
+   * <li><p>started -- Only process instances started relative to this 
date/time.</p></li>
+   * <li><p> last-active -- Only process instances last active relative to 
this date/time.</p></li>
+   * <li><p>$property -- Only process instances with a correlation property 
equal to the
+   * specified value.</p></li>
+   * </ul>
+   *
+   * </p>
+   * <p>
+   * The name and namespace filters can do full or partial name matching. 
Partial matching
+   * occurs if either filter ends with an asterisk (*). These filters are not 
case sensitive,
+   * for example name=my* will match MyProcess and my-process. If unspecified, 
the default
+   * filter is name=* namespace=*.
+   * </p>
+   *
+   * <p>
+   * The status filter can be used to filter all process definitions based on 
six status codes:
+   * <ul>
+   * <li><p>active -- All currently active process instances (excludes 
instances in any other
+   * state).</p></li>
+   * <li><p>suspended -- All process instances that have not completed, but 
are currently
+   * suspended.</p></li>
+   * <li><p>error -- All process instances that have not completed, but are 
currently indicate an
+   * error condition.</p></li>
+   * <li><p>completed -- All successfully completed process instances 
(excludes instances in any
+   * other state). </p></li>
+   * <li><p>terminated -- All process instances that were terminated.
+   * <li><p>faulted -- All process instances that encountered a fault (in the 
global scope).
+   * </ul>
+   * <p>
+   * The started filter can be used to filter all process instances started on 
or after a
+   * particular date or date/time instant. The value of this filter is either 
an ISO-8601
+   * date or ISO-8601 date/time. For example, to find all process instances 
started on or
+   * after September 1, 2005, use started>=20050901. Similarly, the 
last-active filter can
+   * be used to filter all process instances based on their last active time. 
The last
+   * active time records when the process last completed performing work, and 
either
+   * completed or is now waiting to receive a message, a timeout or some other 
event.
+   * </p>
+   *
+   * <p>
+   * Each process instance has one or more properties that are set its 
instantiation, that
+   * can be used to distinguish it from other process instances. In this 
version of the
+   * specification, we only support properties instantiated as part of 
correlation sets
+   * defined in the global scope of the process. For example, if a process 
instantiates a
+   * correlation set that uses the property order-id, it is possible to filter 
that process
+   * instance based on the value of that property.
+   * </p>
+   *
+   * <p>
+   * The property name is identified by the prefix $. If the property name is 
an NCName,
+   * the filter will match all properties with that local name. If the 
property name is
+   * {namespace}local, the filter will match all properties with the specified 
namespace URI
+   * and local name. For example, to retrieve a list of all active process 
instances with a
+   * property order-id that has the value 456, use status=active $order-id=456.
+   * </p>
+   *
+   * <p>
+   * By default the response returns process instances in no particular order. 
The order
+   * element can be used to order the results by specifying a space-separated 
list of keys.
+   * Each key can be prefixed with a plus sign '+' to specify ascending order, 
or a '-'
+   * minus sign to specify descending order. Without a sign the default 
behavior is to
+   * return process instances in ascending order. The currently supported 
odering keys are:
+   * <ul>
+   * <li><p>pid</p></li> -- Order based on the process identifier.
+   * <li><p>name</p></li> -- Order based on the local name of the process 
instance.
+   * <li><p>namespace</p></li> -- Order based on the namespace URI of the 
process instance.
+   * <li><p>version</p></li> -- Order based on the version number.
+   * <li><p>status</p></li> -- Order based on the status of the process 
instance.
+   * <li><p>started</p></li> -- Order based on the process instance start 
date/time.
+   * <li><p>last-active</p></li> -- Order based on the process instance last 
active date/time.
+   * </ul>
+   * 
+   * @param {string} filter See listInstances' filter argument
+   * @param {string} order  See listInstances' order argument
+   * @param {number} limit maximum number of instances to return
+   * @returns list of matching instances
+   */
+  is.listInstancesSummary = function (filter, order, limit) {
+    filter = filter || '';
+    order = order || '';
+    limit = limit || 0;
+    
+    var deferred = $q.defer();
+
+    SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'listInstancesSummary', namespaceURI: PMAPI_NS},
+      {filter: filter, order: order, limit: limit})
+      .then(function(response) {
+        var instances = [],
+          els = response.xpath('//ns:instance-info', nsResolver), 
+          instance,
+          i;
+
+        for (i = 0; i < els.length; i += 1) {
+          instance = angular.element(els[i]);
+          instances.push(mapInstanceInfo(instance));
+        }
+
+        deferred.resolve(instances);
+
+      }, function(fault) {
+        deferred.reject(fault);
+      });
+      
+    return deferred.promise;
+  };
+
+  /**
+   * Get an instance by id.
+   * @param {number} iid
+   * @returns information about a specific instance
+   */
+  is.getInstanceInfo = function (iid) {
+    var deferred = $q.defer();
+
+    SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'getInstanceInfo', namespaceURI: PMAPI_NS},
+      {iid: iid})
+    .then(function(response) {
+      deferred.resolve(mapInstanceInfo(response.xpath('//instance-info', 
nsResolver)));
+    }, function(fault) {
+      deferred.reject(fault);
+    });
+
+    return deferred.promise;
+  };
+
+  /** Get info about a scope instance by id, optionally including activity 
info.
+   * @param {number} siid scope instance identifier
+   * @param {boolean} activityInfo if <code>true</code>, include activity info
+   * @returns information about a specific scope instance
+   */
+  is.getScopeInfoWithActivity = function (siid, activityInfo) {
+    var deferred = $q.defer();
+
+    SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'getScopeInfoWithActivity', namespaceURI: PMAPI_NS},
+      {sid: siid, activityInfo: activityInfo})
+    .then(function(response) {
+      deferred.resolve(mapScopeInfo(response.xpath('//scope-info')));
+    }, function(fault) {
+      deferred.reject(fault);
+    });
+
+    return deferred.promise;
+  };
+
+  /**
+   * Delete a process instances with the given IID.
+   * @param iid InstanceID.
+   * @returns collection of instances identfiers, corresponding to deleted
+   *         instances
+   */
+  is.delete = function (iid) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'delete', namespaceURI: PMAPI_NS},
+      {filter: 'iid = ' + iid});
+  };
+
+  /**
+   * Delete the process instances matching the given filter.
+   * @param filter instance filter (see listInstancesSummary ).
+   * @returns collection of instances identfiers, corresponding to deleted
+   *         instances
+   */
+  is.deleteFilter = function (filter) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'delete', namespaceURI: PMAPI_NS},
+      {filter: filter});
+  };
+
+  /**
+   * Causes the process instance to terminate immediately, without a chance to
+   * perform any fault handling or compensation. The process transitions to the
+   * terminated state. It only affects process instances that are in the 
active,
+   * suspended or error states.
+   * @param iid instance id
+   * @returns post-change instance information
+   */
+  is.terminate = function (iid) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'terminate', namespaceURI: PMAPI_NS},
+      {iid: iid});
+  };
+
+  /**
+   * Changes the process state from active to suspended. this affects process 
instances that
+   * are in the active or error states.
+   * @param iid instance id
+   * @returns post-change instance information
+   */
+  is.suspend = function (iid) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'suspend', namespaceURI: PMAPI_NS},
+      {iid: iid});
+  };
+
+  /**
+   * Resume the (previously suspended) instance. This operation only affects 
process instances
+   * that are in the suspended state.
+   * @param iid instance id
+   * @returns post-change instance information
+   */
+  is.resume = function (iid) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'resume', namespaceURI: PMAPI_NS},
+      {iid: iid});
+  };
+
+  /**
+   * Performs an activity recovery action.
+   * @param iid instance id (process)
+   * @param aid instance id (activity)
+   * @param action recovery action (e.g. retry, fault)
+   * @returns post-change instance information
+   */
+  is.recoverActivity = function (iid, aid, action) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'recoverActivity', namespaceURI: PMAPI_NS},
+      {iid: iid, aid: aid, action: action});
+  };
+
+  /**
+   * Get info about a variable.
+   * @param siid scope identifier
+   * @param varName variable name
+   * @returns information about variable (basically the value)
+   */
+  is.getVariableInfo = function (siid, varName) {
+    var deferred = $q.defer();
+
+    SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'getVariableInfo', namespaceURI: PMAPI_NS},
+      {sid: siid, varName: varName})
+    .then(function(response) {
+      var res = {};
+      var selfEl = response.xpath('//variable-info/ns:self', nsResolver);
+      res.name = selfEl.attr('name');
+      res.iid = selfEl.attr('iid');
+      res.siid = selfEl.attr('siid');
+      res.value = response.xpath('//variable-info/ns:value', 
nsResolver).html().trim();
+      deferred.resolve(res);
+    }, function(fault) {
+      deferred.reject(fault);
+    });
+
+    return deferred.promise;
+  };
+
+  is.setVariable = function (siid, varName, value) {
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'setVariable', namespaceURI: PMAPI_NS},
+      {sid: siid, varName: varName, value: value});
+  };
+
+  /**
+   * Retrieve a timeline of BPEL events.
+   *
+   * @param instanceFilter instance filter (if set,return only events for 
matching instances)
+   * @param eventFilter event filter (event type and data range)
+   * @returns list of stringified dates (in ISO format)
+   */
+  is.getEventTimeline = function (instanceFilter, eventFilter) {
+    instanceFilter = instanceFilter || '';
+    eventFilter = eventFilter || '';
+
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'getEventTimeline', namespaceURI: PMAPI_NS},
+      {instanceFilter: instanceFilter, eventFilter: eventFilter});
+    //TODO
+  };
+
+  /**
+   * Retrieve BPEL events. One may specify an "instance filter" and an "event 
filter" to
+   * limit the number of events returned. The instance filter takes the exact 
same syntax
+   * as for the {@link #listInstances(String, String, int)} method. The "event 
filter" employs
+   * a similar syntax; the following properties may be filtered: <ol>
+   * <li><em>type</em> -  the event type</li>
+   * <li><em>tstamp</em> - the event timestamp</li>
+   * </ol>
+   * @param instanceFilter instance filter (if set,return only events for 
matching instances)
+   * @param eventFilter event filter (event type and data range)
+   * @returns list of events
+   */
+  is.listEvents = function (instanceFilter, eventFilter, maxCount) {
+    instanceFilter = instanceFilter || '';
+    eventFilter = eventFilter || '';
+
+    return SoapService.callSOAP(IMAPI_ENDPOINT,
+      {localName: 'getEventTimeline', namespaceURI: PMAPI_NS},
+      {instanceFilter: instanceFilter, eventFilter: eventFilter, maxCount: 
maxCount});
+    //TODO
+  };
+
+  return is;
+
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/instanceactionbuttons.html
----------------------------------------------------------------------
diff --git a/src/app/instance/instanceactionbuttons.html 
b/src/app/instance/instanceactionbuttons.html
new file mode 100644
index 0000000..37468a4
--- /dev/null
+++ b/src/app/instance/instanceactionbuttons.html
@@ -0,0 +1,14 @@
+<div ng-controller="InstanceActionsController">
+<button type="button" class="btn btn-default btn-xs" ng-if="instance.status == 
'SUSPENDED'" ng-click="resume(instance.iid)">
+  <span class="glyphicon glyphicon-play"></span> Resume
+</button>
+<button type="button" class="btn btn-default btn-xs" ng-if="instance.status == 
'ACTIVE'" ng-click="suspend(instance.iid)">
+  <span class="glyphicon glyphicon-pause"></span> Suspend
+</button>
+<button type="button" class="btn btn-xs btn-warning" ng-if="instance.status == 
'ACTIVE' || instance.status == 'SUSPENDED'" ng-really-message="Are you sure?" 
ng-really-click="terminate(instance.iid)">
+  <span class="glyphicon glyphicon-stop"></span> Terminate
+</button>
+<button type="button" class="btn btn-xs btn-danger" ng-really-message="Are you 
sure?" ng-really-click="delete(instance.iid)">
+  <span class="glyphicon glyphicon-remove"></span> Delete
+</button>
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/instanceactions.controller.js
----------------------------------------------------------------------
diff --git a/src/app/instance/instanceactions.controller.js 
b/src/app/instance/instanceactions.controller.js
new file mode 100644
index 0000000..16625d6
--- /dev/null
+++ b/src/app/instance/instanceactions.controller.js
@@ -0,0 +1,97 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('odeConsole')
+  .controller('InstanceActionsController', function ($scope, $location, 
xmlParser, $http, InstanceService, ngToast, $modal, $q) {
+
+  var fireChangeEvent = function (action, iids) {
+    $scope.$emit('instance-modified', {action: action, iids: iids});
+  };
+
+  $scope.delete = function(iids) {
+    iids = iids.pop ? iids : [iids];
+    if (iids.length === 0) {return;}
+    console.log('delete ' + iids);
+
+    $q.all(iids.map(function (id) {
+      return InstanceService.delete(id);
+    })).then(function() {
+      fireChangeEvent('delete', iids);
+    });
+  };
+
+  $scope.terminate = function(iids) {
+    iids = iids.pop ? iids : [iids];
+    if (iids.length === 0) {return;}
+    console.log('terminate ' + iids);
+
+    $q.all(iids.map(function (id) {
+      return InstanceService.terminate(id);
+    })).then(function() {
+      fireChangeEvent('terminate', iids);
+    });
+  };
+
+  $scope.suspend = function(iids) {
+    iids = iids.pop ? iids : [iids];
+    if (iids.length === 0) {return;}
+    console.log('suspend ' + iids);
+
+    $q.all(iids.map(function (id) {
+      return InstanceService.suspend(id);
+    })).then(function() {
+      fireChangeEvent('suspend', iids);
+    });
+  };
+
+  $scope.resume = function(iids) {
+    iids = iids.pop ? iids : [iids];
+    if (iids.length === 0) {return;}
+    console.log('resume ' + iids);
+
+    $q.all(iids.map(function (id) {
+      return InstanceService.resume(id);
+    })).then(function() {
+      fireChangeEvent('resume', iids);
+    });
+  };
+
+  $scope.deleteAll = function() {
+    console.log('deleteAll ' + $scope.filter);
+    InstanceService.deleteFilter($scope.filter).then(function() {
+      fireChangeEvent('deleteAll', '*');
+    });
+  };
+
+  $scope.openFaultModal = function () {
+    $modal.open({
+      templateUrl: 'app/instance/faultinfodialog.html',
+      scope: $scope,
+    });
+  };
+
+  $scope.openCorrelationPropertiesModal = function () {
+    $modal.open({
+      templateUrl: 'app/instance/corrpropinfodialog.html',
+      scope: $scope,
+    });
+  };
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/instancelist.controller.js
----------------------------------------------------------------------
diff --git a/src/app/instance/instancelist.controller.js 
b/src/app/instance/instancelist.controller.js
new file mode 100644
index 0000000..b27ecc6
--- /dev/null
+++ b/src/app/instance/instancelist.controller.js
@@ -0,0 +1,70 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('odeConsole')
+  .controller('InstanceListController', function ($scope, $location, 
xmlParser, $http, InstanceService, ngToast) {
+
+  var updateTable = function () {
+    InstanceService.listInstancesSummary($scope.filter, '-last-active', 
200).then(function(instances) {
+      $scope.instances = instances;
+    }, function(fault) {
+        $scope.instances = [];
+        var re = /java\.lang\.RuntimeException: Invocation of method \S+ in 
management interface failed: (.+)/,
+            m;
+        if ((m = re.exec(fault.faultstring)) !== null) {
+          ngToast.create({content: m[0], class: 'warning'});
+        } else {
+          ngToast.create({content: 'Could not load process instance list.', 
class: 'danger'});
+        }
+    });
+  };
+
+  $scope.getSelectedIIDs = function () {
+    return $scope.instances.filter(function (i) {
+      return i.isSelected;
+    }).map(function (i) {
+      return i.iid;
+    });
+  };
+
+  // set filter to query or empty string
+  $scope.filter = ($location.search()).q || '';
+  // watch query to update filter and table
+  $scope.$watch(function () { return $location.search(); }, function() {
+    $scope.filter = ($location.search()).q || '';
+    updateTable();
+  });
+
+
+  $scope.update = function () {
+    $scope.loaded = false;
+    $location.search({q: $scope.filter});
+  };
+
+  $scope.instances = [];
+  $scope.itemsByPage = 25;
+
+  // listen for changes issued by actions to reload table
+  $scope.$on('instance-modified', function () { 
+    updateTable();
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/instancelist.html
----------------------------------------------------------------------
diff --git a/src/app/instance/instancelist.html 
b/src/app/instance/instancelist.html
new file mode 100644
index 0000000..981102c
--- /dev/null
+++ b/src/app/instance/instancelist.html
@@ -0,0 +1,78 @@
+<div class="container">
+
+  <div class="panel panel-default" st-table="instancesViewModel" 
st-safe-src="instances">
+    <!-- Default panel contents -->
+    <div class="panel-heading">
+      <form class="" role="search" ng-submit="update()">
+        <div class="input-group">
+          <input type="text" class="form-control" ng-model="filter" 
autofocus="">
+          <span class="input-group-btn">
+            <button class="btn btn-default" type="submit"><span 
class="glyphicon glyphicon-search"></span></button>
+          </span>
+        </div>
+      </form>
+    </div>
+    <table class="table table-condensed">
+      <thead>
+        <tr>
+          <th><select-all-checkbox items="instancesViewModel" 
prop="isSelected"></ui-select-all></th>
+          <th st-sort="iid">IID</th>
+          <th st-sort="processNameShort">Process name</th>
+          <th st-sort="status">Status</th>
+          <th st-sort="started">Started</th>
+          <th st-sort="lastActive" st-sort-default="reverse">Last Active</th>
+          <th>Action</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr ng-if="!instances.length">
+          <td colspan="7" ng-if="!instances.length">
+              <h4 class="text-center">No instances found.</h4>
+          </td>
+        </tr>
+        <tr ng-class="instance.status | lowercase" ng-repeat="instance in 
instancesViewModel">
+          <td><input type="checkbox" ng-model="instance.isSelected"></td>
+          <td><a href="#/instances/{{instance.iid}}">{{instance.iid}}</a></td>
+          <td data-toggle="tooltip" title="{{instance.processNameFull}}">
+            <a href="#/processes/{{instance.pid}}">{{instance.pid}}</a>
+          </td>
+          <td ng-controller="InstanceActionsController">
+            <span class="label ng-class: 'label-instance-' + (instance.status 
| lowercase);">{{instance.status}}</span>
+            <i class="fa fa-exclamation-circle text-info" 
style="vertical-align:middle" aria-hidden="true" ng-if="instance.fault" 
ng-click="openFaultModal()" tooltip="Show fault details"></i>
+            <i class="fa fa-bullseye" style="vertical-align:middle" 
aria-hidden="true" ng-if="instance.correlationProperties" 
ng-click="openCorrelationPropertiesModal()" tooltip="Show correlation 
properties"></i>
+            <i class="fa fa-flash" style="vertical-align:middle" 
aria-hidden="true" ng-if="instance.failures" 
tooltip="{{instance.failures.count}} failure(s) @ {{instance.failures.failure | 
date:'medium'}}"></i>
+          </td>
+          <td>
+            <time datetime="{{instance.started}}" tooltip="{{instance.started 
| date:'medium'}}" am-time-ago="instance.started"></time>
+          </td>
+          <td>
+            <time datetime="{{instance.lastActive}}" 
tooltip="{{instance.lastActive | date:'medium'}}" 
am-time-ago="instance.lastActive"></time>
+          </td>
+          <td><div 
ng-include="'app/instance/instanceactionbuttons.html'"></div></td>
+        </tr>
+      </tbody>
+      <tfoot>
+        <tr>
+          <td colspan="7">
+            <div class="pull-left" ng-controller="InstanceActionsController">
+              <button type="button" class="btn btn-default btn-sm" 
ng-click="suspend(getSelectedIIDs())"><span class="glyphicon 
glyphicon-pause"></span> Suspend</button>
+              <button type="button" class="btn btn-default btn-sm" 
ng-click="resume(getSelectedIIDs())"><span class="glyphicon 
glyphicon-play"></span> Resume</button>
+              <button type="button" class="btn btn-warning btn-sm" 
ng-really-message="Are you sure?" 
ng-really-click="terminate(getSelectedIIDs())"><span class="glyphicon 
glyphicon-stop"></span> Terminate</button>
+              <button type="button" class="btn btn-danger btn-sm" 
ng-really-message="Are you sure?" 
ng-really-click="delete(getSelectedIIDs())"><span class="glyphicon 
glyphicon-remove"></span> Delete</button>
+              <button type="button" class="btn btn-danger btn-sm" 
ng-really-message="Are you sure?" ng-really-click="deleteAll()"><span 
class="glyphicon glyphicon-remove"></span> Delete All</button>
+            </div>
+            <div class="btn-group pull-right">
+              <label class="btn btn-default btn-xs" ng-model="itemsByPage" 
btn-radio="25" uncheckable>25</label>
+              <label class="btn btn-default btn-xs" ng-model="itemsByPage" 
btn-radio="50" uncheckable>50</label>
+              <label class="btn btn-default btn-xs" ng-model="itemsByPage" 
btn-radio="100" uncheckable>100</label>
+            </div>
+          </td>
+        </tr>        
+      </tfoot>
+    </table>
+    <div class="panel-footer text-center">
+      <div st-pagination="" st-items-by-page="itemsByPage" 
st-displayed-pages="7"></div>
+    </div>
+  </div>
+
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/recoverydialog.html
----------------------------------------------------------------------
diff --git a/src/app/instance/recoverydialog.html 
b/src/app/instance/recoverydialog.html
new file mode 100644
index 0000000..1399827
--- /dev/null
+++ b/src/app/instance/recoverydialog.html
@@ -0,0 +1,55 @@
+<div class="modal-header">
+  <h3 class="modal-title">Activity Recovery</h3>
+</div>
+<div class="modal-body">
+  <p>The following activity has entered a failure state. It can be manually 
recovered.</p>
+  <table class="table table-condensed">
+    <tbody>
+      <tr>
+        <th>Name</th>
+        <td>{{activity.name}}</td>
+      </tr>
+      <tr>
+        <th>Type</th>
+        <td>{{activity.type}}</td>
+      </tr>
+      <tr>
+        <th>Status</th>
+        <td>{{activity.status}}</td>
+      </tr>
+      <tr>
+        <th>Enabled</th>
+        <td>{{activity.enabled}}</td>
+      </tr>
+      <tr>
+        <th>Started</th>
+        <td>{{activity.started}}</td>
+      </tr>
+      <tr>
+        <th>Failure</th>
+        <td>{{activity.failure.failure}}</td>
+      </tr>
+      <tr>
+        <th>Retries</th>
+        <td>{{activity.failure.retries}}</td>
+      </tr>
+      <tr>
+        <th>Reason</th>
+        <td>{{activity.failure.reason}}</td>
+      </tr>
+    </tbody>
+  </table>
+
+  <h4>Recovery Options</h4>
+  <div class="btn-group">
+    <label style="text-transform: capitalize" ng-repeat="action in 
activity.failure.actions" class="btn btn-default" ng-model="radio.model" 
btn-radio="action">{{action}}</label>
+  </div>
+  <p ng-if="radio.model == 'retry'">The execution of the activity will be 
tried again.</p>
+  <p ng-if="radio.model == 'cancel'">The execution of the activity will be 
cancelled.</p>
+  <p ng-if="radio.model == 'fault'">The execution of the activity will be 
faulted. The process instance will either handle this fault in a fault handler 
or will enter a fault state itself.</p>
+
+</div>
+<div class="modal-footer">
+  <button class="btn btn-primary" ng-disabled="!radio.model" 
ng-click="$close()">OK</button>
+  <button class="btn btn-default" ng-click="$dismiss()">Cancel</button>
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/instance/varinfodialog.html
----------------------------------------------------------------------
diff --git a/src/app/instance/varinfodialog.html 
b/src/app/instance/varinfodialog.html
new file mode 100644
index 0000000..e9e65c2
--- /dev/null
+++ b/src/app/instance/varinfodialog.html
@@ -0,0 +1,15 @@
+<div class="modal-header">
+  <h3 class="modal-title">Edit Variable Details</h3>
+</div>
+<div class="modal-body">
+  <h4>Variable Name</h4>
+  <p>{{var.name}}</p>
+  <h4>Content</h4>
+  <div ui-ace="{mode: 'xml'}" ng-model="var.value"></div>
+  <h4>Scope Instance ID</h4>
+  <p>{{var.siid}}</p>
+</div>
+<div class="modal-footer">
+  <button class="btn btn-primary" ng-click="$close(var)">OK</button>
+  <button class="btn btn-default" ng-click="$dismiss()">Cancel</button>
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/process.controller.js
----------------------------------------------------------------------
diff --git a/src/app/process/process.controller.js 
b/src/app/process/process.controller.js
new file mode 100644
index 0000000..1ed3af5
--- /dev/null
+++ b/src/app/process/process.controller.js
@@ -0,0 +1,61 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('odeConsole')
+  .controller('ProcessController', function ($scope, $routeParams, xmlParser, 
$http, ProcessService, InstanceService, ngToast) {
+
+  var updateInstanceTable = function () {
+    InstanceService.listInstancesSummary('pid = ' + $routeParams.pid, 
'-last-active', $scope.maxInstances).then(function(instances) {
+      $scope.instances = instances;
+    }, function(fault) {
+        $scope.instances = [];
+        var re = /java\.lang\.RuntimeException: Invocation of method \S+ in 
management interface failed: (.+)/,
+            m;
+        if ((m = re.exec(fault.faultstring)) !== null) {
+          ngToast.create({content: m[0], class: 'warning'});
+        } else {
+          ngToast.create({content: 'Could not load process instance list.', 
class: 'danger'});
+        }
+    });
+  };
+
+  var updateProcessInfo = function () {
+    ProcessService.getProcessInfo($routeParams.pid).then(function(process) {
+      $scope.process = process;
+    }, function() {
+        ngToast.create({content: 'Could not load process ' + $routeParams.pid 
+ '.', class: 'danger'});
+    });
+  };
+
+  // listen for changes issued by actions to reload table
+  $scope.$on('instance-modified', function () { 
+    updateInstanceTable();
+  });
+  $scope.$on('process-modified', function () { 
+    updateProcessInfo();
+  });
+
+  $scope.maxInstances = 15;
+
+
+  updateInstanceTable();
+  updateProcessInfo();
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/process.html
----------------------------------------------------------------------
diff --git a/src/app/process/process.html b/src/app/process/process.html
new file mode 100644
index 0000000..b596bba
--- /dev/null
+++ b/src/app/process/process.html
@@ -0,0 +1,99 @@
+<div class="container">
+
+  <div class="panel panel-default">
+    <div class="panel-heading">
+      <h3 class="panel-title">Process Details</h3>
+    </div>
+    <table class="table">
+      <tr>
+        <th>Process Name</th>
+        <td><a href="#/instances?q=name%20%3D%20{{process.nameFull | 
escape}}">{{process.nameFull}}</a></td>
+      </tr>
+      <tr>
+        <th>Process ID</th>
+        <td>{{process.pid}}</td>
+      </tr>
+      <tr>
+        <th>Package</th>
+        <td>{{process.package}}</td>
+      </tr>
+      <tr>
+        <th>Deploy Date</th>
+        <td><span tooltip="{{process.deployDate | 
amCalendar}}">{{process.deployDate | date:'medium'}}</span></td>
+      </tr>
+      <tr>
+        <th>Status</th>
+        <td><span class="label ng-class: 'label-process-' + (process.status | 
lowercase);">{{process.status}}</span></td>
+      </tr>
+      <tr>
+        <th>WSDL URL</th>
+        <td></td>
+      </tr>
+      <tr>
+        <th>Actions</th>
+        <td><div 
ng-include="'app/process/processactionbuttons.html'"></div></td>
+      </tr>
+    </table>
+  </div>
+
+  <div class="panel panel-default" ng-if="process.endpoints.length">
+    <div class="panel-heading">
+      <h3 class="panel-title">Endpoints</h3>
+    </div>
+    <ul class="list-group">
+      <li class="list-group-item" ng-repeat="endpoint in process.endpoints">
+        <div ui-ace="{mode: 'xml'}" readonly ng-model="endpoint"></div>
+      </li>
+    </ul>
+  </div>
+
+  <div class="panel panel-default" ng-cloak>
+    <div class="panel-heading">
+      <h3 class="panel-title"><span tooltip="Top {{maxInstances}} instances, 
ordered by last activity">Most Recent Process Instances</span></h3>
+    </div>
+    <table class="table table-condensed">
+      <thead>
+        <tr>
+          <th>IID</th>
+          <th>Process name</th>
+          <th>Status</th>
+          <th>Started</th>
+          <th>Last Active</th>
+          <th>Action</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr ng-if="!instances.length">
+          <td colspan="6" ng-if="!instances.length">
+              <h4 class="text-center">No instances found.</h4>
+          </td>
+        </tr>
+        <tr ng-class="instance.status | lowercase" ng-repeat="instance in 
instances">
+          <td><a href="#/instances/{{instance.iid}}">{{instance.iid}}</a></td>
+          <td data-toggle="tooltip" title="{{instance.processNameFull}}">
+            <a href="#/processes/{{instance.pid}}">{{instance.pid}}</a>
+          </td>
+          <td ng-controller="InstanceActionsController">
+            <span class="label ng-class: 'label-instance-' + (instance.status 
| lowercase);">{{instance.status}}</span>
+            <i class="fa fa-exclamation-circle text-info" 
style="vertical-align:middle" aria-hidden="true" ng-if="instance.fault" 
ng-click="openFaultModal()" tooltip="Show fault details"></i>
+            <i class="fa fa-bullseye" style="vertical-align:middle" 
aria-hidden="true" ng-if="instance.correlationProperties" 
ng-click="openCorrelationPropertiesModal()" tooltip="Show correlation 
properties"></i>
+            <i class="fa fa-flash" style="vertical-align:middle" 
aria-hidden="true" ng-if="instance.failures" 
tooltip="{{instance.failures.count}} failure(s) 
({{instance.failures.failure}})"></i>
+          </td>
+          <td>
+            <time datetime="{{instance.started}}" tooltip="{{instance.started 
| date:'medium'}}" am-time-ago="instance.started"></time>
+          </td>
+          <td>
+            <time datetime="{{instance.lastActive}}" 
tooltip="{{instance.lastActive | date:'medium'}}" 
am-time-ago="instance.lastActive"></time>
+          </td>
+          <td><div 
ng-include="'app/instance/instanceactionbuttons.html'"></div></td>
+        </tr>
+      </tbody>
+      <tfoot>
+        <tr>
+          <td colspan="6"><a href="#/instances?q={{'pid = ' + process.pid | 
escape}}">Show all instances</a>
+          </td>
+        </tr>
+      </tfoot>
+    </table>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/process.service.js
----------------------------------------------------------------------
diff --git a/src/app/process/process.service.js 
b/src/app/process/process.service.js
new file mode 100644
index 0000000..938e2c1
--- /dev/null
+++ b/src/app/process/process.service.js
@@ -0,0 +1,270 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+/*global $:false */
+/*global vkbeautify:false */
+
+/**
+ * @ngdoc service
+ * @name angApp.InstanceService
+ * @description
+ * # InstanceService
+ * Service in the angApp.
+ */
+angular.module('odeConsole')
+  .factory('ProcessService', function ($http, $log, $q, $interval, _, 
SoapService, PMAPI_ENDPOINT, DSAPI_ENDPOINT, POLLING_INTERVAL) {
+  
+  var PMAPI_NS = 'http://www.apache.org/ode/pmapi';
+
+  /** private functions **/
+  var nsResolver = function (prefix) {
+    var ns = {
+      'ns'     : 'http://www.apache.org/ode/pmapi/types/2006/08/02/',
+      'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/',
+      'pmapi'  : 'http://www.apache.org/ode/pmapi'
+    };
+
+    return ns[prefix] || null;
+  };
+
+  var splitPackageName = function(packageName) {
+    var split = packageName.split('-');
+    var hasVersion = !isNaN(_.last(split));
+    var result = {
+      packageId: packageName,
+      name: (_.first(split, split.length - hasVersion).join('-')),
+    };
+  
+    result.version = hasVersion ? _.last(split) : null;
+    return result;
+  };
+
+  var mapProcessInfo = function(processElement) {
+    var processNameEl = 
processElement.xpath('ns:definition-info/ns:process-name', nsResolver);
+    var result = {};
+    result.pid = processElement.xpath('ns:pid', nsResolver).text();
+    result.status = processElement.xpath('ns:status', nsResolver).text();
+    result.version = processElement.xpath('ns:version', nsResolver).text();
+    result.package = processElement.xpath('ns:deployment-info/ns:package', 
nsResolver).text();
+    result.deployDate = 
processElement.xpath('ns:deployment-info/ns:deploy-date', nsResolver).text();
+
+    result.nameShort = processNameEl.text();
+    result.nameFull = 
SoapService.qnameToString(SoapService.createQNameObj(processNameEl.text(), 
processNameEl[0]));
+
+    // endpoints
+    result.endpoints = [];
+    processElement.xpath('ns:endpoints/ns:endpoint-ref', 
nsResolver).each(function() {
+      result.endpoints.push(vkbeautify.xml($(this).first().html().trim(), 2));
+    });
+
+    // documents
+    result.documents = [];
+    processElement.xpath('ns:documents/ns:document', 
nsResolver).each(function() {
+      var d = {};
+      d.name = $(this).xpath('ns:name', nsResolver).text();
+      d.type = $(this).xpath('ns:type', nsResolver).text();
+      d.source = $(this).xpath('ns:source', nsResolver).text();
+      result.documents.push(d);
+    });
+
+    // properties
+    // TODO
+
+    result.stats = {
+        active:     
Number(processElement.xpath('ns:instance-summary/ns:instances[@state=\'ACTIVE\']/@count',
 nsResolver).val()),
+        completed:  
Number(processElement.xpath('ns:instance-summary/ns:instances[@state=\'COMPLETED\']/@count',
 nsResolver).val()),
+        error:      
Number(processElement.xpath('ns:instance-summary/ns:instances[@state=\'ERROR\']/@count',
 nsResolver).val()),
+        failed:     
Number(processElement.xpath('ns:instance-summary/ns:instances[@state=\'FAILED\']/@count',
 nsResolver).val()),
+        suspended:  
Number(processElement.xpath('ns:instance-summary/ns:instances[@state=\'SUSPENDED\']/@count',
 nsResolver).val()),
+        terminated: 
Number(processElement.xpath('ns:instance-summary/ns:instances[@state=\'TERMINATED\']/@count',
 nsResolver).val()),
+        inrecovery: 
Number(processElement.xpath('ns:instance-summary/ns:failures/ns:count', 
nsResolver).text() || 0)
+      };
+
+    return result;
+  };
+
+  var updateStats = function(packages) {
+    ps.summary = {};
+    ps.summary.packages = 0;
+    ps.summary.processes = 0;
+    ps.summary.instances = 0;
+
+    ps.summary.active = 0;
+    ps.summary.completed = 0;
+    ps.summary.suspended = 0;
+    ps.summary.failed = 0;
+    ps.summary.error = 0;
+    ps.summary.inrecovery = 0;
+    ps.summary.terminated = 0;
+
+    packages.forEach(function(pa) {
+      ps.summary.packages++;
+      pa.processes.forEach(function(process) {
+        ps.summary.processes++;
+
+        ps.summary.active += process.stats.active;
+        ps.summary.completed += process.stats.completed;
+        ps.summary.suspended += process.stats.suspended;
+        ps.summary.failed += process.stats.failed;
+        ps.summary.error += process.stats.error;
+        ps.summary.inrecovery += process.stats.inrecovery;
+        ps.summary.terminated += process.stats.terminated;
+
+        ps.summary.instances += Object.keys(process.stats).reduce(function 
(a,b) { 
+          return a + process.stats[b];
+        }, 0);
+
+      });
+    });
+  };
+
+  /** public functions **/
+  var ps = {};
+
+  ps.getPackages = function() {
+    var deferred = $q.defer();
+    SoapService.callSOAP(PMAPI_ENDPOINT,
+      {localName: 'listAllProcesses', namespaceURI: PMAPI_NS},
+      {})
+      .then(function(response) {
+        var packages = {},
+            els = response.xpath('//ns:process-info', nsResolver);
+
+        for (var i = 0; i < els.length; i += 1) {
+          var process = angular.element(els[i]);
+          var packageName = process.xpath('ns:deployment-info/ns:package', 
nsResolver).text();
+
+          packages[packageName] = packages[packageName] || 
_.extend(splitPackageName(packageName),
+            { 
+              deployDate: process.xpath('ns:deployment-info/ns:deploy-date', 
nsResolver).text(),
+              processes: []
+            });
+
+          packages[packageName].processes.push(mapProcessInfo(process));
+        }
+        updateStats(_.values(packages));
+        deferred.resolve(_.values(packages));
+      }, function(msg, code) {
+          deferred.reject(msg);
+          $log.error(msg, code);
+      });
+    return deferred.promise;
+  };
+
+  /**
+   * List the processes known to the engine.
+   * @returns list of {@link ProcessInfoDocument}s (including instance 
summaries)
+   */
+  ps.getProcesses = function() {
+    var deferred = $q.defer();
+    SoapService.callSOAP(PMAPI_ENDPOINT,
+      {localName: 'listAllProcesses', namespaceURI: PMAPI_NS},
+      {})
+      .then(function(response) {
+        var processes = [],
+          els = response.xpath('//ns:process-info', nsResolver),
+          process,
+          i;
+
+        for (i = 0; i < els.length; i += 1) {
+          process = angular.element(els[i]);
+          processes.push(mapProcessInfo(process));
+        }
+
+        deferred.resolve(processes);
+      }, function(msg, code) {
+          deferred.reject(msg);
+          $log.error(msg, code);
+      });
+    return deferred.promise;
+  };
+
+  /**
+   * Get the process info for a process (includingthe instance summary).
+   * @param pid name of the process
+   * @returns {@link ProcessInfoDocument} with all details.
+   */
+  ps.getProcessInfo = function (pid) {
+    var deferred = $q.defer();
+    var pidQName = SoapService.parseQNameStr(pid);
+    pidQName.prefix = 'pns';
+
+    SoapService.callSOAP(PMAPI_ENDPOINT,
+      {localName: 'getProcessInfo', namespaceURI: PMAPI_NS},
+      {pid: pidQName})
+    .then(function(response) {
+      deferred.resolve(mapProcessInfo(response.xpath('//process-info')));
+    }, function(fault) {
+      deferred.reject(fault);
+    });
+
+    return deferred.promise;
+  };
+
+  /**
+   * Retire a process.
+   * @param pid identifier of the process to retire
+   * @param {boolean} retired retired or not?
+   * @return {@link ProcessInfoDocument} reflecting the modification
+   */
+  ps.setRetired = function (pid, retired) {
+    var pidQName = SoapService.parseQNameStr(pid);
+    pidQName.prefix = 'pns';
+
+    return SoapService.callSOAP(PMAPI_ENDPOINT,
+      {localName: 'setRetired', namespaceURI: PMAPI_NS},
+      {pid: pidQName, retired: retired});
+  };
+  
+  /**
+   * Deploy a package
+   * @param {string} packageName the package name.
+   * @param {string} zip the package zip in Base64 encoding
+   * @return {Promise}     a promise for this request.
+   */
+  ps.deployPackage = function (packageName, zip) {
+    return SoapService.callSOAP(DSAPI_ENDPOINT,
+      {localName: 'deploy', namespaceURI: PMAPI_NS},
+      {name: packageName,
+       'package': '<dep:zip xmlns:dep="http://www.apache.org/ode/deployapi";>' 
+ zip + '</dep:zip>'});
+  };
+
+  if (POLLING_INTERVAL > 0) {
+    $interval(ps.getPackages, POLLING_INTERVAL);
+  }
+
+  /**
+   * Undeploy a package
+   * @param  {string} paid the package name.
+   * @return {Promise}     a promise for this request.
+   */
+  ps.undeployPackage = function (paid) {
+    return SoapService.callSOAP(DSAPI_ENDPOINT,
+      {localName: 'undeploy', namespaceURI: PMAPI_NS},
+      {packageName: paid});
+  };
+
+  if (POLLING_INTERVAL > 0) {
+    $interval(ps.getPackages, POLLING_INTERVAL);
+  }
+
+  return ps;
+
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/processactionbuttons.html
----------------------------------------------------------------------
diff --git a/src/app/process/processactionbuttons.html 
b/src/app/process/processactionbuttons.html
new file mode 100644
index 0000000..dc892cf
--- /dev/null
+++ b/src/app/process/processactionbuttons.html
@@ -0,0 +1,8 @@
+<div ng-controller="ProcessActionsController">
+  <button type="button" class="btn btn-default btn-xs" ng-if="process.status 
== 'ACTIVE'" ng-click="retire(process.pid)">
+    <span class="glyphicon glyphicon-pause"></span> Retire
+  </button>
+  <button type="button" class="btn btn-default btn-xs" ng-if="process.status 
== 'DISABLED' || process.status == 'RETIRED'" ng-click="activate(process.pid)">
+    <span class="glyphicon glyphicon-fire"></span> Activate
+  </button>
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/processactions.controller.js
----------------------------------------------------------------------
diff --git a/src/app/process/processactions.controller.js 
b/src/app/process/processactions.controller.js
new file mode 100644
index 0000000..934f22a
--- /dev/null
+++ b/src/app/process/processactions.controller.js
@@ -0,0 +1,63 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('odeConsole')
+  .controller('ProcessActionsController', function ($scope, xmlParser, $http, 
ProcessService, ngToast) {
+
+  var fireProcessChangeEvent = function (action, pid) {
+    $scope.$emit('process-modified', {action: action, pid: pid});
+  };
+
+  var firePackageChangeEvent = function (action, paid) {
+    $scope.$emit('package-modified', {action: action, paid: paid});
+  };
+
+  $scope.retire = function(pid) {
+    console.log('retire ' + pid);
+    ProcessService.setRetired(pid, true).then(function() {
+      ngToast.create('Process ' + pid + ' retired.');
+      fireProcessChangeEvent('retire', pid);
+    }, function() {
+      ngToast.create({content: 'Ouups, something went wrong.', class: 
'danger'});
+    });
+  };
+
+  $scope.activate = function(pid) {
+    console.log('activate ' + pid);
+    ProcessService.setRetired(pid, false).then(function() {
+      ngToast.create('Process ' + pid + ' activated.');
+      fireProcessChangeEvent('activate', pid);
+    }, function() {
+      ngToast.create({content: 'Ouups, something went wrong.', class: 
'danger'});
+    });
+  };
+
+  $scope.undeployPackage = function(paid) {
+    console.log('undeployPackage ' + paid);
+    ProcessService.undeployPackage(paid).then(function() {
+      ngToast.create('Package ' + paid + ' undeployed.');
+      firePackageChangeEvent('undeploy', paid);
+    }, function() {
+      ngToast.create({content: 'Ouups, something went wrong.', class: 
'danger'});
+    });
+  };
+
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/processlist.controller.js
----------------------------------------------------------------------
diff --git a/src/app/process/processlist.controller.js 
b/src/app/process/processlist.controller.js
new file mode 100644
index 0000000..db7047e
--- /dev/null
+++ b/src/app/process/processlist.controller.js
@@ -0,0 +1,55 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('odeConsole')
+  .controller('ProcessListController', function ($scope, xmlParser, $http, 
ProcessService, ngToast) {
+
+  var updateTable = function () {
+    ProcessService.getPackages().then(function(packages) {
+      $scope.packages = packages;
+    }, function() {
+        ngToast.create({content: 'Could not load process list.', class: 
'danger'});
+    });
+  };
+
+  $scope.upload = {};
+  $scope.uploadPackage = function () {
+    ProcessService.deployPackage($scope.upload.packageName, 
$scope.upload.file.base64).then(function() {
+      ngToast.create({content: 'Package ' + $scope.upload.name + ' 
successfully deployed', class: 'success'});
+      $scope.upload = {};
+      updateTable();
+    }, function(fault) {
+      ngToast.create({content: 'Could not deploy package ' + 
$scope.upload.file.filename + ': ' + fault.faultstring, class: 'danger'});
+      $scope.upload = {};
+    });
+  };
+
+  // listen for changes issued by actions to reload table
+  $scope.$on('process-modified', function () { 
+    updateTable();
+  });
+  $scope.$on('package-modified', function () { 
+    updateTable();
+  });
+
+  updateTable();
+
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/processlist.controller.spec.js
----------------------------------------------------------------------
diff --git a/src/app/process/processlist.controller.spec.js 
b/src/app/process/processlist.controller.spec.js
new file mode 100644
index 0000000..62be2ea
--- /dev/null
+++ b/src/app/process/processlist.controller.spec.js
@@ -0,0 +1,40 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+describe('controllers', function(){
+  var scope;
+
+  beforeEach(module('odeConsole'));
+
+  beforeEach(inject(function($rootScope) {
+       scope = $rootScope.$new();
+  }));
+
+  it('should define a packages object', inject(function($controller) {
+    expect(scope.packages).toBeUndefined();
+
+    $controller('ProcessListController', {
+      $scope: scope
+       });
+
+    //expect(scope.packages).toBeDefined();
+  }));
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/process/processlist.html
----------------------------------------------------------------------
diff --git a/src/app/process/processlist.html b/src/app/process/processlist.html
new file mode 100644
index 0000000..f76704b
--- /dev/null
+++ b/src/app/process/processlist.html
@@ -0,0 +1,79 @@
+<div class="container">
+
+  <h1>WS-BPEL Process Models</h1>
+
+  <div class="panel panel-default">
+    <!-- Default panel contents -->
+
+    <table class="table table-condensed">
+      <thead>
+        <tr>
+            <th></th>
+            <th>Process name</th>
+            <th>Status</th>
+            <th>Active</th>
+            <th tooltip-append-to-body="true" tooltip="Completed 
OK">Completed</th>
+            <th tooltip-append-to-body="true" tooltip="Completed with 
fault">Failed</th>
+            <th tooltip-append-to-body="true" tooltip="Suspended via a 
breakpoint or user intervention">Suspended</th>
+            <th>Terminated</th>
+            <th>Action</th>
+        </tr>
+      </thead>
+      <tbody ng-repeat="package in packages">
+        <tr ng-if="!packages.length">
+          <td colspan="10" ng-if="!packages.length">
+              <h4 class="text-center">No instances found.</h4>
+          </td>
+        </tr>
+        <tr class="active">
+          <td colspan="8">{{package.name}} <span class="label label-info" 
ng-if="package.version">Version {{package.version}}</span></td>
+          <td>
+            <button type="button" class="btn btn-danger btn-xs" 
ng-really-message="Are you sure?" 
ng-really-click="undeployPackage(package.packageId)">
+              <span class="glyphicon glyphicon-remove"></span> Undeploy
+            </button>
+          </td>
+        </tr>
+        <tr ng-repeat="process in package.processes">
+          <td></td>
+          <td data-toggle="tooltip" title="{{process.nameFull}}"><a 
href="#/processes/{{process.pid}}">{{process.nameShort}}</a></td>
+          <td><span class="label ng-class: 'label-process-' + (process.status 
| lowercase);">{{process.status}}</span></td>
+          <td>{{process.stats.active}} <span ng-if="process.stats.inrecovery > 
0" class="badge" tooltip="Instances waiting for manual recovery"><i class="fa 
fa-exclamation-circle"></i> {{process.stats.inrecovery}}</span></td>
+          <td>{{process.stats.completed}}</td>
+          <td>{{process.stats.failed}}</td>
+          <td>{{process.stats.suspended}}</td>
+          <td>{{process.stats.terminated}}</td>
+          <td><div 
ng-include="'app/process/processactionbuttons.html'"></div></td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+
+  <div class="panel panel-default">
+    <div class="panel-heading">
+      <h3 class="panel-title">Upload a process model</h3>
+    </div>
+    <div class="panel-body">
+      <form name="uploadForm">
+      <div ng-class="{'has-error': uploadForm.$touched && 
!uploadForm.packageName.$valid}" class="form-group">
+        <label for="packageName">Package Name</label>
+        <input type="text" class="form-control" id="packageName" 
name="packageName" ng-model="upload.packageName" placeholder="Package Name" 
required>
+      </div>
+      <div ng-class="{'has-error': uploadForm.touched && 
!uploadForm.file.$valid}"class="form-group">
+        <label for="file">Process Package</label>
+        <div class="input-group">
+          <span class="input-group-btn">
+              <span class="btn btn-default btn-file">
+                  Browse&hellip; <input type="file" name="file" id="file" 
ng-model="upload.file" accept="application/zip" base-sixty-four-input required>
+              </span>
+          </span>
+          <input type="text" class="form-control" 
ng-model="upload.file.filename" readonly>
+        </div>
+        <span class="help-block">
+            Select ODE deployment packages (.zip files containing all process 
artifacts at the root level)
+        </span>
+      </div>
+      <button type="submit" class="btn btn-primary" ng-click="uploadPackage()" 
ng-disabled="!uploadForm.$valid">Upload</button>
+      </div>
+    </form>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/really.directive.js
----------------------------------------------------------------------
diff --git a/src/app/really.directive.js b/src/app/really.directive.js
new file mode 100644
index 0000000..af25bb8
--- /dev/null
+++ b/src/app/really.directive.js
@@ -0,0 +1,48 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * A generic confirmation for risky actions.
+ * Usage: Add attributes: ng-really-message="Are you sure"? 
ng-really-click="takeAction()" function
+ * Inspired from https://gist.github.com/asafge/7430497
+ */
+angular.module('odeConsole').directive('ngReallyClick', ['$modal', '$parse', 
function($modal, $parse) {
+  return {
+    restrict: 'A',
+    link: function(scope, element, attrs) {
+      element.bind('click', function() {
+        // create new child scope to pass data to dialog
+        var mScope = scope.$new();
+        mScope.message = attrs.ngReallyMessage;
+
+        $modal.open({
+          templateUrl: 'app/confirmdialog.html',
+          scope: mScope
+        }).result.then(function() {
+          // destroy temporary child scope
+          mScope.$destroy();
+          // invoke action
+          $parse(attrs.ngReallyClick)(scope);
+        });
+      });
+    }
+  };
+}]);

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/soap.service.js
----------------------------------------------------------------------
diff --git a/src/app/soap.service.js b/src/app/soap.service.js
new file mode 100644
index 0000000..76f4a40
--- /dev/null
+++ b/src/app/soap.service.js
@@ -0,0 +1,129 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc service
+ * @name odeConsole.SoapService
+ * @description
+ * # SoapService
+ * Service in the odeConsole.
+ */
+angular.module('odeConsole')
+  .factory('SoapService', function ($http, $log, $q, xmlFilter, _) {
+
+  var soap = {};
+
+  /** private functions **/
+  var parseSOAPFault = function (response) {
+    var result = {};
+    var fault = xmlFilter(response.data).xpath('/*:Envelope/*:Body/*:Fault');
+    if (fault) {
+      result.faultcode = fault.xpath('faultcode').text();
+      result.faultstring = fault.xpath('faultstring').text();
+      result.details = fault.xpath('details').text();
+    }
+
+    return result;
+  };
+
+  var serializeSOAPParameters = function (parameters) {
+    var param = '';
+    for (var key in parameters) {
+      var value = parameters[key];
+      if (_.isObject(value)) {
+        // then we expect a QName and serialize it accordingly
+        param += '<' + key + ' xmlns:' + value.prefix + '="' + 
value.namespaceURI + '">' + value.prefix + ':' + value.localName + '</' + key + 
'>';
+      } else {
+        param += '<' + key + '>' + value + '</' + key + '>';
+      }
+    }
+    return param;
+  };
+
+  var createSOAPMessage = function (operation, parameters) {
+    operation.prefix = operation.prefix || 'prfx';
+    var message = '<soapenv:Envelope 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/";>' +
+      '   <soapenv:Header/>' +
+      '   <soapenv:Body>' + 
+      '      <' + operation.prefix + ':' + operation.localName + ' xmlns:' + 
operation.prefix + '="' + operation.namespaceURI + '">' +
+      serializeSOAPParameters(parameters) + 
+      '      </' + operation.prefix + ':' + operation.localName + '>' +
+      '   </soapenv:Body>' +
+      '</soapenv:Envelope>';
+    return message;
+  };
+
+  /** public functions **/
+  soap.callSOAP = function(endpoint, operation, parameters) {
+    var deferred = $q.defer(),
+        request = createSOAPMessage(operation, parameters);
+
+    $http.post(endpoint + '/' + operation.localName, request)
+      .then(function(response) {
+        deferred.resolve(response.xml);
+      }, function(response) {
+          if (response.status !== 500) {
+            deferred.reject({faultcode: 'Client', faultstring: 'Network 
problem'});
+          } else {
+            var fault = parseSOAPFault(response);
+            deferred.reject(fault);
+            $log.error(fault);
+          }
+      });
+
+    return deferred.promise;
+  };
+
+  /** Parses string representation into a QName object **/
+  soap.parseQNameStr = function(qnameStr) {
+    if (qnameStr.charAt(0) !== '{') {
+      return {
+        namespaceURI: '',
+        localName:    qnameStr,
+        prefix:       ''
+      };
+    }
+
+    var endOfNamespaceURI = qnameStr.indexOf('}');
+    return {
+      namespaceURI: qnameStr.substr(1, endOfNamespaceURI -1),
+      localName:    qnameStr.substr(endOfNamespaceURI + 1),
+      prefix:       ''
+    };
+  };
+
+  soap.qnameToString = function (qname) {
+    return '{' + qname.namespaceURI + '}' + qname.localName;
+  };
+
+  /** Creates QName object from an XML QName **/
+  soap.createQNameObj = function (qname, contextElement) {
+    var endOfPrefix = qname.indexOf(':');
+    var prefix = qname.substr(0, endOfPrefix);
+    return {
+      namespaceURI: contextElement.lookupNamespaceURI(prefix),
+      localName:    qname.substr(endOfPrefix + 1),
+      prefix:       prefix
+    };
+  };
+
+  return soap;
+});

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/st-select.directive.js
----------------------------------------------------------------------
diff --git a/src/app/st-select.directive.js b/src/app/st-select.directive.js
new file mode 100644
index 0000000..1bef969
--- /dev/null
+++ b/src/app/st-select.directive.js
@@ -0,0 +1,43 @@
+'use strict';
+
+/** License unclear: http://jsfiddle.net/wldaunfr/JSFLa/ **/
+angular.module('odeConsole')
+  .directive('selectAllCheckbox', ['$filter', function($filter) {
+    return {
+        restrict: 'E',
+        template: '<input type="checkbox">',
+        replace: true,
+        link: function(scope, element, iAttrs) {
+            function changeState(checked, indet) {
+                element.prop('checked', checked).prop('indeterminate', indet);
+            }
+            function updateItems() {
+                angular.forEach(scope.$eval(iAttrs.items), function(el) {
+                    el[iAttrs.prop] = element.prop('checked');
+                });
+            }
+            element.bind('change', function() {
+                scope.$apply(function() { updateItems(); });
+            });
+            scope.$watch(iAttrs.items, function(newValue) {
+                var checkedItems = $filter('filter')(newValue, function(el) {
+                    return el[iAttrs.prop];
+                });
+                switch(checkedItems ? checkedItems.length : 0) {
+                    case 0:                // none selected
+                        changeState(false, false);
+                        break;
+                    case newValue.length:  // all selected
+                        changeState(true, false);
+                        break;
+                    default:               // some selected
+                        changeState(false, true);
+                }
+            }, true);
+            scope.$on('$destroy', function() {
+              element.off('change');
+            });
+            updateItems();
+        }
+    };
+}]);

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/app/vendor.scss
----------------------------------------------------------------------
diff --git a/src/app/vendor.scss b/src/app/vendor.scss
new file mode 100644
index 0000000..95675a3
--- /dev/null
+++ b/src/app/vendor.scss
@@ -0,0 +1,71 @@
+$icon-font-path: 
"../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/";
+
+$font-family-base: 'Roboto Slab', serif;
+
+@import 
'../../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap';
+
+// custom instance labels
+.label-instance-active {
+  @include label-variant($label-primary-bg);
+}
+
+.label-instance-suspended {
+  @include label-variant($label-primary-bg);
+}
+
+.label-instance-error {
+  @include label-variant($label-danger-bg);
+}
+
+.label-instance-completed {
+  @include label-variant($label-success-bg);
+}
+
+.label-instance-terminated {
+  @include label-variant($label-default-bg);
+}
+
+.label-instance-faulted {
+  @include label-variant($label-danger-bg);
+}
+
+.label-instance-failed {
+  @include label-variant($label-danger-bg);
+}
+
+// custom instance labels
+.label-process-active {
+  @include label-variant($label-success-bg);
+}
+
+.label-process-retired {
+  @include label-variant($label-default-bg);
+}
+
+// custom activity labels
+.label-activity-started {
+  @include label-variant($label-primary-bg);
+}
+
+.label-activity-enabled {
+  @include label-variant($label-info-bg);
+}
+
+.label-activity-completed {
+  @include label-variant($label-success-bg);
+}
+
+.label-activity-failure {
+  @include label-variant($label-danger-bg);
+}
+
+
+// status contextual table variants
+@include table-row-variant('active', white);
+@include table-row-variant('completed', $state-success-bg);
+@include table-row-variant('suspended', $state-info-bg);
+@include table-row-variant('terminated', $state-warning-bg);
+@include table-row-variant('faulted', $state-danger-bg);
+@include table-row-variant('failed', $state-danger-bg);
+@include table-row-variant('error', $state-danger-bg);
+

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/assets/images/ode-logo.png
----------------------------------------------------------------------
diff --git a/src/assets/images/ode-logo.png b/src/assets/images/ode-logo.png
new file mode 100644
index 0000000..b99ff7b
Binary files /dev/null and b/src/assets/images/ode-logo.png differ

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/components/navbar/navbar.controller.js
----------------------------------------------------------------------
diff --git a/src/components/navbar/navbar.controller.js 
b/src/components/navbar/navbar.controller.js
new file mode 100644
index 0000000..1613e98
--- /dev/null
+++ b/src/components/navbar/navbar.controller.js
@@ -0,0 +1,16 @@
+'use strict';
+
+angular.module('odeConsole')
+  .controller('NavbarController', function ($scope, $location, ProcessService) 
{
+    $scope.isActive = function (viewLocation) {
+        return $location.path().indexOf(viewLocation) === 0;
+    };
+
+    // watch for summary changes and update counter badges
+    $scope.$watch(function() { 
+        return ProcessService.summary;
+      }, function (newVal, oldVal, scope) {
+        scope.summary = newVal;
+    });
+  })
+;

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/components/navbar/navbar.html
----------------------------------------------------------------------
diff --git a/src/components/navbar/navbar.html 
b/src/components/navbar/navbar.html
new file mode 100644
index 0000000..10fa49c
--- /dev/null
+++ b/src/components/navbar/navbar.html
@@ -0,0 +1,26 @@
+<nav class="navbar navbar-static-top navbar-default" 
ng-controller="NavbarController">
+  <div class="container-fluid">
+    <div class="navbar-header">
+      <a class="navbar-brand" href="#/">
+        Apache ODE<sup>™</sup> Console
+      </a>
+    </div>
+
+    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-6">
+      <ul class="nav navbar-nav">
+        <li ng-class="{ active: isActive('/processes')}"><a 
ng-href="#/processes">Process Models <span 
class="badge">{{summary.processes}}</span></a></li>
+        <li class="dropdown" dropdown ng-class="{ active: 
isActive('/instances')}"><a class="dropdown-toggle" dropdown-toggle 
role="button" aria-expanded="false">Process Instances <span 
class="badge">{{summary.instances}}</span><span class="caret"></span></a>
+            <ul class="dropdown-menu" role="menu" >
+              <li><a ng-href="#/instances">All Instances <span 
class="badge">{{summary.instances}}</span></a></li>
+              <li><a ng-href="#/instances?q=status%20%3D%20active" >Running 
Instances <span class="badge">{{summary.active}}</span></a></li>
+              <li><a ng-href="#/instances?q=status%20%3D%20suspended" 
>Suspended Instances <span class="badge">{{summary.suspended}}</span></a></li>
+              <li><a ng-href="#/instances?q=status%20%3D%20failed">Failed 
Instances <span class="badge">{{summary.failed}}</span></a></li>
+              <li><a 
ng-href="#/instances?q=status%20%3D%20completed">Completed Instances <span 
class="badge">{{summary.completed}}</span></a></li>
+            </ul>
+          </li>
+        </li>
+      </ul>
+
+    </div>
+  </div>
+</nav>

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/favicon.ico
----------------------------------------------------------------------
diff --git a/src/favicon.ico b/src/favicon.ico
new file mode 100644
index 0000000..cac0909
Binary files /dev/null and b/src/favicon.ico differ

http://git-wip-us.apache.org/repos/asf/ode-console/blob/4ee2cbad/src/index.html
----------------------------------------------------------------------
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..269130a
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html class="no-js" ng-app="odeConsole">
+  <head>
+    <meta charset="utf-8">
+    <title>Apache ODE Console</title>
+    <meta name="description" content="">
+    <meta name="viewport" content="width=device-width">
+    <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
+    
+    <!-- build:css({.tmp,src}) styles/vendor.css -->
+    <link rel="stylesheet" href="app/vendor.css">
+    <!-- bower:css -->
+    <link rel="stylesheet" href="../bower_components/ngtoast/dist/ngToast.css" 
/>
+    <link rel="stylesheet" 
href="../bower_components/font-awesome/css/font-awesome.css" />
+    <link rel="stylesheet" 
href="../bower_components/angular-loading-bar/build/loading-bar.css" />
+    <link rel="stylesheet" 
href="../bower_components/roboto-slab-fontface/roboto-slab-fontface.css" />
+    <!-- endbower -->
+    <!-- endbuild -->
+    
+
+    <!-- build:css({.tmp,src}) styles/app.css -->
+    <!-- inject:css -->
+    <link rel="stylesheet" href="app/index.css">
+    <!-- endinject -->
+    <!-- endbuild -->
+
+    <!-- build:js scripts/modernizr.js -->
+    <script src="../bower_components/modernizr/modernizr.js"></script>
+    <!-- endbuild -->
+  </head>
+  <body>
+    <!--[if lt IE 10]>
+      <p class="browsehappy">You are using an <strong>outdated</strong> 
browser. Please <a href="http://browsehappy.com/";>upgrade your browser</a> to 
improve your experience.</p>
+    <![endif]-->
+    <toast></toast>
+    <div ng-include="'components/navbar/navbar.html'"></div>
+
+    <div ng-view></div>
+
+    <!-- build:js scripts/vendor.js -->
+    <!-- bower:js -->
+    <script src="../bower_components/jquery/dist/jquery.js"></script>
+    <script src="../bower_components/angular/angular.js"></script>
+    <script 
src="../bower_components/angular-animate/angular-animate.js"></script>
+    <script 
src="../bower_components/angular-sanitize/angular-sanitize.js"></script>
+    <script src="../bower_components/angular-route/angular-route.js"></script>
+    <script 
src="../bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
+    <script 
src="../bower_components/angular-smart-table/dist/smart-table.min.js"></script>
+    <script src="../bower_components/angular-xml/angular-xml.js"></script>
+    <script 
src="../bower_components/angular-poller/angular-poller.min.js"></script>
+    <script src="../bower_components/moment/moment.js"></script>
+    <script 
src="../bower_components/angular-moment/angular-moment.js"></script>
+    <script src="../bower_components/ngtoast/dist/ngToast.js"></script>
+    <script src="../bower_components/underscore/underscore.js"></script>
+    <script 
src="../bower_components/angular-loading-bar/build/loading-bar.js"></script>
+    <script 
src="../bower_components/ace-builds/src-min-noconflict/ace.js"></script>
+    <script 
src="../bower_components/ace-builds/src-min-noconflict/mode-xml.js"></script>
+    <script src="../bower_components/angular-ui-ace/ui-ace.js"></script>
+    <script 
src="../bower_components/angular-base64-upload/src/angular-base64-upload.js"></script>
+    <!-- endbower -->
+    <!-- endbuild -->
+
+    <!-- build:js({.tmp,src}) scripts/app.js -->
+    <!-- inject:js -->
+    <script src="app/index.js"></script>
+    <script src="app/really.directive.js"></script>
+    <script src="app/soap.service.js"></script>
+    <script src="app/spinbutton.directive.js"></script>
+    <script src="app/st-select.directive.js"></script>
+    <script src="vendor/jquery.xpath.js"></script>
+    <script src="vendor/vkbeautify.js"></script>
+    <script src="components/navbar/navbar.controller.js"></script>
+    <script src="app/dashboard/dashboard.controller.js"></script>
+    <script src="app/instance/instance.controller.js"></script>
+    <script src="app/instance/instance.service.js"></script>
+    <script src="app/instance/instanceactions.controller.js"></script>
+    <script src="app/instance/instancelist.controller.js"></script>
+    <script src="app/process/process.controller.js"></script>
+    <script src="app/process/process.service.js"></script>
+    <script src="app/process/processactions.controller.js"></script>
+    <script src="app/process/processlist.controller.js"></script>
+    <!-- endinject -->
+
+    <!-- inject:partials -->
+    <!-- angular templates will be automaticaly converted in js and inserted 
here -->
+    <!-- endinject -->
+    <!-- endbuild -->
+
+  </body>
+</html>

Reply via email to