Nuno Maltez avatar Nuno Maltez committed 3e6d620

Shipments app added

Comments (0)

Files changed (66)

         jQuery Mobile version: lots of problems with scrolling
         Sencha Touch 2 version: works ok
 
+    Shipments
 
 
 See main.py for entry point URLs
+#coding: utf-8
+from basehandler import BaseHandler
+from models import Shipment
+import datetime
+import logging
+
+class Index(BaseHandler):
+    def get(self):
+        self.render('index.html')
+
+class Heuristic(BaseHandler):
+    def get(self):
+        self.render('heuristic.html')
+                
+class GoogleMap(BaseHandler):
+    def get(self):
+        self.render('map.html')
+
+class SapMobileDemo(BaseHandler):
+    def get(self):
+        self.render('sapmobile/sapmobiledemo.html')
+        
+class SapMobile(BaseHandler):
+    def get(self):
+        self.render('sapmobile/sapmobile.html')
+
+class SapMobileList(BaseHandler):
+    def get(self):
+        loaddate = self.get_argument('date','')
+        ldate = datetime.date(*map(int, loaddate.split('-')))
+        loadstatus = self.get_argument('status','')
+        shipment_query = Shipment.all().filter('dplbg =', ldate)
+        if loadstatus:
+            shipment_query = shipment_query.filter('sttrg =', loadstatus)
+        self.set_header('Content-Type', 'text/xml')
+        self.render('sapmobile/list.xml', shipments=shipment_query.fetch(1000))
+
+class SapMobileAction(BaseHandler):
+    def get(self):
+        mode = self.get_argument('mode','')
+        sid = self.get_argument('sid','')
+        logging.info('mode %s sid %s' % (mode, sid))
+        if mode and sid:
+            shipment = Shipment.all().filter('tknum =', sid).get()
+            if mode == 'streg':
+                return self.set_shipment_status(shipment, 'streg', 'dareg', 'uareg')
+            elif mode == 'stlbg':
+                return self.set_shipment_status(shipment, 'stlbg', 'dalbg', 'ualbg')
+            elif mode == 'stlad':
+                return self.set_shipment_status(shipment, 'stlad', 'dalen', 'ualen')
+            elif mode == 'stabf':
+                return self.set_shipment_status(shipment, 'stabf', 'dtabf', 'uzabf')
+            elif mode == 'sttbg':
+                return self.set_shipment_status(shipment, 'sttbg', 'datbg', 'uatbg')
+            elif mode == 'stten':
+                return self.set_shipment_status(shipment, 'stten', 'daten', 'uaten')
+
+    def set_shipment_status(self, shipment, status_field, date_field, time_field):
+        if getattr(shipment, status_field) == 'X':
+            setattr(shipment, status_field, '')
+            setattr(shipment, date_field, None)
+            setattr(shipment, time_field, None)
+            logging.info('shipment %s %s' % (date_field, getattr(shipment, date_field)))
+            shipment.put()
+        else:
+            now = datetime.datetime.now()
+            setattr(shipment, status_field, 'X')
+            setattr(shipment, date_field, now.date())
+            setattr(shipment, time_field, now.time())
+            logging.info('shipment %s %s' % (date_field, getattr(shipment, date_field)))
+            shipment.put()
+        self.set_header('Content-Type', 'text/xml')
+        self.render('sapmobile/detail.xml', shipment=shipment)
+
+
+class SapMobileLog(BaseHandler):
+    def post(self):
+        sid = self.get_argument('sid','')
+        shipment = Shipment.all().filter('tknum =', sid).get()
+        logtext = self.get_argument('logtext','')
+        if logtext:
+            shipment.textlog += '\n' + logtext
+            shipment.put()
+        self.set_header('Content-Type', 'text/xml')
+        self.render('sapmobile/detail.xml', shipment=shipment)
+
+
+class SapMobileLoad(BaseHandler):
+    def get(self):
+        import data
+        data.load_data()
+        self.write("load ok")
+
+# ---- F4 widget ----
+
+class SearchHelpFields(BaseHandler):
+    def get(self):
+        shname = self.get_argument('sh','')
+        if shname == 'sh_tknum':
+            fields = (
+                ('route', 'text', 'Route'),
+                ('tplst', 'text', 'Planning Point'),
+            )
+        elif shname == 'sh_vsbed':
+            fields = (
+                ('vkorg', 'text', 'Sales Org.'),
+                ('vtweg', 'text', 'Dist. Channel'),
+            )            
+        self.set_header('Content-Type', 'text/xml')
+        self.render('sapmobile/searchhelpfields.xml', fields=fields)
+
+
+class SearchHelpList(BaseHandler):
+    def get(self):
+        logging.info(self.get_argument('route','____'))
+        logging.info(self.get_argument('sh','____'))
+        if len(self.request.arguments) > 1: #sh
+            fields = (
+                    ('tknum', 'int', 'TKNUM'),
+                    ('route', 'text', 'Route'),
+                    ('tplst', 'text', 'Planning Point'),
+                )
+            results = (
+                      { 'tknum': 1, 'route': 'Route 1', 'tplst': 'Point 1' },
+                      { 'tknum': 2, 'route': 'Route 2', 'tplst': 'Point 2' },
+                      { 'tknum': 3, 'route': 'Route 3', 'tplst': 'Point 3' },
+                      { 'tknum': 4, 'route': 'Route 4', 'tplst': 'Point 4' },
+                      )
+        else:
+            fields = results = ()
+        self.set_header('Content-Type', 'text/xml')
+        self.render('sapmobile/searchhelplist.xml', results=results, fields=fields)
+
+# ---- testes ----
+class CalculatorUI(BaseHandler):
+    def get(self):
+        self.render('tests/calculator.html')
+
+
+class YouTubeURL(BaseHandler):
+    def get(self):
+        res = None
+        if len(self.request.arguments) > 0:
+            yturl = self.get_argument('yturl','')
+            from urlparse import urlparse
+            from cgi import parse_qs
+            parts = urlparse(yturl)
+            if parts[2].startswith('/embed/'):
+                vid = parts[2][7:]
+            else:
+                qs = parts[4]
+                vid = parse_qs(qs).get('v', '???')[0]
+            from google.appengine.api import urlfetch
+            url = "http://gdata.youtube.com/feeds/api/videos/%s?alt=json" % vid
+            result = urlfetch.fetch(url=url)
+            if result.status_code == 200:
+                from django.utils import simplejson as json
+                obj = json.loads(result.content)
+                res = obj['entry']['media$group']
+                content = res['media$content']
+                ##1   URL de transmissão RTSP para reprodução de vídeos em celulares. Vídeo H.263 (até 176 x 144) e áudio AMR.
+                ##5   URL HTTP para o player incorporável (SWF) do vídeo. Esse formato não está disponível para um vídeo que não pode ser incorporado. Os desenvolvedores geralmente adicionam &format=5 às suas consultas para restringir os resultados aos vídeos que podem ser incorporados aos seus respectivos sites.
+                ##6   URL de transmissão RTSP para reprodução de vídeos em celulares. Vídeo MPEG-4 SP (até 176 x 144) e áudio AAC.
+                FORMATS = {
+                        1: u'3gpp - H.263/AMR',
+                        5: u'SWF',
+                        6: u'3gpp - MPEG-4 SP/AAC'
+                    }
+                video_formats = [{'url': v['url'], 'format': FORMATS[v['yt$format']]} for v in content]
+                thumbnails = res["media$thumbnail"]
+        self.render('tests/youtubeurl.html', vid=video_formats, thumbs=thumbnails)
 # manually, move them above the marker line.  The index.yaml file is
 # automatically uploaded to the admin console when you next deploy
 # your application using appcfg.py.
-
 import tornado.web
 import tornado.wsgi
 import wsgiref.handlers
+import handlers
 from apps.mobibatchinfo import handlers as batchinfohandlers
 from apps.mobilauncher import handlers as launcherhandlers
 from apps.mobibase import handlers as basehandlers
   #mobile base
   (r'/sap/bc/bsp/sap/ymwbase/barcodescanner.js', basehandlers.StaticHandler, dict(filename='mobibase/barcodescanner.js')),
   (r'/sap/bc/bsp/sap/ymwbase/cordova.js', basehandlers.StaticHandler, dict(filename='mobibase/cordova.js')),
+  #shipments
+  (r'/sapmobiledemo', handlers.SapMobileDemo),  
+  (r'/sapmobile', handlers.SapMobile),
+  (r'/sapmobile/list', handlers.SapMobileList),
+  (r'/sapmobile/action', handlers.SapMobileAction),
+  (r'/sapmobile/log', handlers.SapMobileLog),
+  (r'/sapmobile/load', handlers.SapMobileLoad),
+  (r'/sapmobile/sh/fields', handlers.SearchHelpFields),
+  (r'/sapmobile/sh/search', handlers.SearchHelpList),
   #mobile launcher
   (r'/sap/bc/bsp/sap/ymwlauncher/index.do', launcherhandlers.Index),
   #mobile batchinfo
   # DEV:
   # /static/stweaver/index.html   <- launcher
   # /static/stw_batchinfo/index.html  <- batch info
+  # /static/stw_shipments/index.html  <- shipments
   # /static/dataview/index.html  <- data view
   # PROD:
   # /static/stweaver/build/production/index.html   <- launcher
+from google.appengine.ext import db
+
+class Shipment(db.Model):
+    tknum = db.StringProperty(verbose_name="Shipment Number")
+    shtyp = db.StringProperty(verbose_name="Shipment type")
+    tplst = db.StringProperty(verbose_name="Transportation planning point")
+    ernam = db.StringProperty(verbose_name="Name of Person who Created the Object")
+    erdat = db.DateProperty(verbose_name="Date on Which Record Was Created")
+    vsart = db.StringProperty(verbose_name="Shipping type")
+    vsbed = db.StringProperty(verbose_name="Shipping Conditions")
+    route = db.StringProperty(verbose_name="Shipping Route")
+    bezei = db.StringProperty(verbose_name="Description of Route")
+    sttrg = db.StringProperty(verbose_name="Overall transportation status")
+    tdlnr = db.StringProperty(verbose_name="Number of forwarding agent")
+    name1 = db.StringProperty(verbose_name="Name of forwarding agent")
+    dplbg = db.DateProperty(verbose_name="Planned date for start of loading")
+    uplbg = db.TimeProperty(verbose_name="Planned loading start time")
+    streg = db.StringProperty(verbose_name="Status of check-in")
+    dareg = db.DateProperty(verbose_name="Actual date of check-in")
+    uareg = db.TimeProperty(verbose_name="Current time of check-in")
+    stlbg = db.StringProperty(verbose_name="Status for start of loading")
+    dalbg = db.DateProperty(verbose_name="Current date for start of loading")
+    ualbg = db.TimeProperty(verbose_name="Actual loading start time")
+    stlad = db.StringProperty(verbose_name="Status for end of loading")
+    dalen = db.DateProperty(verbose_name="Actual date for end of loading")
+    ualen = db.TimeProperty(verbose_name="Actual loading end time")
+    stabf = db.StringProperty(verbose_name="Status of shipment completion")
+    dtabf = db.DateProperty(verbose_name="Current date of shipment completion")
+    uzabf = db.TimeProperty(verbose_name="Time of transportation processing")
+    sttbg = db.StringProperty(verbose_name="Status for start of shipment")
+    datbg = db.DateProperty(verbose_name="Current date for start of shipment")
+    uatbg = db.TimeProperty(verbose_name="Actual transport start time")
+    stten = db.StringProperty(verbose_name="Status for end of shipment")
+    daten = db.DateProperty(verbose_name="Actual Date for End of Shipment")
+    uaten = db.TimeProperty(verbose_name="Actual shipment end time")
+    textlog = db.TextProperty(verbose_name="Shipment Log")
+
+    def get_list_fields(self):
+        list_fields = ['tknum','tplst','sttrg','tdlnr','route','bezei','name1','dplbg','uplbg','textlog',
+                       'streg','dareg','uareg','stlbg','dalbg','ualbg','stlad','dalen','ualen',
+                       'stabf','dtabf','uzabf','sttbg','datbg','uatbg','stten','daten','uaten']
+        return [ (field, getattr(self, field)) for field in list_fields ]
+
+    def get_fields(self):
+        return [ (field.name, getattr(self, field.name)) for field in self._meta.fields if field.get_internal_type() != 'AutoField']
+
+    def __str__(self):
+        return self.tknum

static/sapmobile/helpform.html

+<div id="search_help_form">
+
+
+<a href="" id="help-result-button" data-role="button" data-inline="false">Search</a>
+</div>

static/sapmobile/jquery.mobile.saphelp.css

+.ui-icon-saphelp {
+    background-image: url(sapsearchhelp.png);
+}
+
+table.ui-mobile-saphelp  { width:100%; border-spacing: 0; }
+table.ui-mobile-saphelp thead th { text-align:left; border-bottom-width:1px; border-top-width:1px; }
+table.ui-mobile-saphelp th, td { text-align:left; padding:6px;}
+table.ui-mobile-saphelp tbody tr {margin: 0; display: table-row; }

static/sapmobile/jquery.mobile.saphelp.css.orig

+.ui-icon-saphelp {
+    background-image: url(sapsearchhelp.png);
+}
+
+table.ui-mobile-saphelp  { width:100%; border-spacing: 0; }
+table.ui-mobile-saphelp thead th { text-align:left; border-bottom-width:1px; border-top-width:1px; }
+table.ui-mobile-saphelp th, td { text-align:left; padding:6px;}
+table.ui-mobile-saphelp tbody tr {margin: 0; display: table-row; }
+
+
+/* Base input element Styles */
+
+.ui-input-searchhelp { width: 97%; background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; padding-top: 0px; padding-bottom: 0px; } 
+.ui-input-searchhelp { min-height: 38px; } /* Fix for IE8 */
+.ui-input-searchhelp .ui-btn-icon-notext { margin-top: 5px !important; margin-bottom: 5px !important; }
+.ui-input-searchhelp input { width: 87% !important; padding: 0 !important; margin-top: 5px !important; border: 1px solid transparent !important; vertical-align: middle; display: inline-block !important; background-color: transparent; zoom: 1; *display: inline; }
+.ui-input-searchhelp input:focus { outline: none;}
+.ui-input-searchhelp .ui-btn-text {display: none;}
+
+@media all and (max-width: 650px){ /* Fix internal input element at low size */
+  .ui-input-searchhelp input { width: 80% !important; }
+}
+@media all and (min-width: 450px){
+  .ui-input-searchhelp { width: 60%; display: inline-block; zoom:1; *display: inline; }
+}

static/sapmobile/jquery.mobile.saphelp.js

+/*
+ * Sources:
+ * JQuerymobile.datebox
+ * http://www.hiddentao.com/archives/2011/11/07/how-to-write-a-custom-widget-for-jquery-mobile/
+ * http://wiki.jqueryui.com/w/page/12138135/Widget%20factory
+ * 
+ */
+(function( $, undefined ) {
+    $.widget( "mobile.saphelp", $.mobile.widget /* optional - an existing widget prototype to inherit from */, /* An object literal to become the widget's prototype*/ {
+    closed: false,
+        /** Available options for the widget are specified here, along with default values. */
+    options: {
+      resultField: "name",
+      disabled: false, // needed?
+      baseUrl: "/sapmobile/sh/"
+    },
+    _saphelpHandler: function(event, payload) {
+        // Handle all event triggers that have an internal effect
+        
+        if ( ! event.isPropagationStopped() ) {
+            switch (payload.method) {
+                case 'open':
+                    $(this).jqmData('saphelp').open();
+                    break;
+                case 'close':
+                    $(this).jqmData('saphelp').close(payload.fromCloseButton);
+                    break;
+                case 'set':
+                    $(this).val(payload.value);
+                    $(this).trigger('change');
+                    break;
+                case 'search':
+                    $(this).jqmData('saphelp').search(payload.value);
+                    break;
+//                 case 'doset':
+//                     if ( $(this).data('datebox').options.mode === 'timebox' || $(this).data('datebox').options.mode === 'durationbox' ) {
+//                         $(this).trigger('datebox', {'method':'set', 'value':$(this).data('datebox')._formatTime($(this).data('datebox').theDate)});
+//                     } else {
+//                         $(this).trigger('datebox', {'method':'set', 'value':$(this).data('datebox')._formatDate($(this).data('datebox').theDate)});
+//                     }
+//                 case 'dooffset':
+//                     $(this).data('datebox')._offset(payload.type, payload.amount, true);
+//                     break;
+//                 case 'dorefresh':
+//                     $(this).data('datebox')._update();
+//                     break;
+//                 case 'doreset':
+//                     $(this).data('datebox').hardreset();
+//                     break;
+            }
+        } 
+    },
+    _buildUrl: function(page) {
+      return this.options.baseUrl + page;
+    },
+    // call the server to get the search help metadata and build the search help form
+    // execute the callback after the form initialization
+    _initForm: function() {
+      var self = this;
+      var searchhelpname = this.options.searchHelp;
+      $.mobile.showPageLoadingMsg();
+      $.ajax({
+        url: this._buildUrl('fields'),
+        data: { sh: searchhelpname },
+        context: this.formPage.children(":jqmData(role='content')").first(),
+        dataType: "xml",
+        success: function(data) {
+          var page = '',
+          xmldoc = $(data);
+          page += '<form data-role="sapsearchhelpform">';
+          page += '<input type="hidden" name="sh" value="' + searchhelpname + '">';
+          xmldoc.find('field').each( function(index) {
+            var description = $(this).text(),
+            name = $(this).attr('name'),
+            datatype = $(this).attr('datatype');
+            //FIXME logic for different data types
+            page += '<label for="' + name + '">' + description + '</label>';
+            page += '<input type="text" name="' + name + '">';
+          });
+          page += '</form>';
+          page += '<a href="#searchhelp" id="help-result-button" data-role="button" data-inline="false">Search</a>';
+          $(this).html(page);
+          self.formPage.page();
+          $(this).children(':jqmData(role="sapsearchhelpform")').submit( function(e) {
+            e.preventDefault();
+            var shFormData = $(this).serialize();
+            self.inputElement.trigger('saphelp', {'method':'search', 'value': shFormData});
+            return false;
+          });
+          $(this).find("a").each(function() {
+            $(this).bind('vclick', function(e) {
+               $(this).parent().children(':jqmData(role="sapsearchhelpform")').submit();
+               return false;
+            });
+          });
+          self.formInit = true;
+          $.mobile.hidePageLoadingMsg();
+          $.mobile.changePage(self.formPage, {'transition': self.transition});
+        },
+        error: function() {
+    		  $.mobile.hidePageLoadingMsg();
+    		}
+      });      
+    },
+    open: function() {
+        // prevent the parent page from being removed from the DOM,
+        this.thisPage.unbind( "pagehide.remove" );
+        var self = this;
+        self.closed = false;
+        if ( this.formInit ) {
+          $.mobile.changePage(self.formPage, {'transition': this.transition});          
+        } else { 
+          this._initForm();
+        }
+    },
+    search: function(value) {
+        var transition = 'pop';
+        // Let's use a dialog
+        // TODO prevent the form page from being removed from the DOM?
+        // this.thisPage.unbind( "pagehide.remove" );
+        var self=this;
+        var searchResultsEl = this.listPage.children(":jqmData(role='content')").first();
+        searchResultsEl.empty();
+        $.mobile.showPageLoadingMsg();
+        $.ajax({
+          url: this._buildUrl('search'),
+          data: value,
+          dataType: "xml",
+          context: searchResultsEl,
+          error: function() {
+              $.mobile.hidePageLoadingMsg();
+          },
+          success: function(data){
+            var xmldoc;
+            var resultTable = $('<table class="ui-mobile-saphelp"></table>');
+            var resultsExist = false;
+            xmldoc = $(data);
+            // header row
+                xmldoc.find('searchhelp result').each( function(index) {
+                // primeira linha (index=0) - header
+                if (index == 0) {
+                    resultsExist = true;
+                    var header = $('<thead></thead>');
+                    var headerRow = $('<tr></tr>');
+                    $(this).children().each( function() {
+                        var th = $('<th></th>').text($(this).attr('title'));
+                        headerRow.append(th);
+                    });
+                    header.append(headerRow);
+                    resultTable.append(header);
+                }
+            });
+            if (resultsExist) {
+            // data
+            var tblBody = $('<tbody></tbody>');
+            xmldoc.find('result').each( function(index) {
+                var resultText = $(this).find(self.options.resultField).text();
+                // TODO exception if not found
+                var resRow = $('<tr></tr>',  {class: 'ui-btn ui-btn-up-a', 'data-result': resultText, 'data-theme': 'a'});
+                $(this).children().each( function() {
+                    var resData = $('<td></td>').text($(this).text());
+                    resRow.append(resData);
+                });
+                tblBody.append(resRow);
+            });
+            resultTable.append(tblBody);
+            $(this).append(resultTable);
+            } else {
+                $(this).append("<p>No results found</p>");
+            }
+          $.mobile.hidePageLoadingMsg();
+          $.mobile.changePage(self.listPage, {'transition': transition});
+          $(this).find("td").each(function() {
+            $(this).bind('vclick', function(e) {
+                e.preventDefault();
+                self.inputElement.trigger('saphelp', {'method':'set', 'value': $(this).parent().attr('data-result')});
+                self.inputElement.trigger('saphelp', {'method':'close'});
+            })
+          });
+          }
+        });
+    },
+    close: function(fromCloseButton) {
+        // Close the controls
+        var self = this,
+            callback;
+
+            if (!fromCloseButton) {
+                //dialog('close') só faz um history back
+                if (!self.closed) {
+                    window.history.go(-2);
+                }
+            }
+            if( !self.thisPage.data("page").options.domCache ){
+                self.thisPage.bind( "pagehide.remove", function() {
+                    $(self).remove();
+                });
+            }
+        self.focusedEl.removeClass('ui-focus');
+        // FIXME callBack - precisamos?
+        if ( self.options.closeCallback !== false ) { callback = new Function(self.options.closeCallback); callback(undefined, self); }
+        self.closed = true;
+    },
+    /** Mandatory method - automatically called by jQuery Mobile to initialise the widget. */
+    _create: function() {
+      $(document).trigger("saphelpcreate");
+      var self = this;
+      var inputElement = this.element;
+      var transition = 'pop';
+      // control when the search help form is initialized
+      var formInit = false; 
+      var opts = $.extend(this.options, inputElement.jqmData("options"));
+      var thisPage = inputElement.closest('.ui-page');
+      var thisTheme = 'c';
+      var focusedEl = inputElement.wrap('<div class="ui-input-saphelp ui-input-datebox ui-shadow-inset ui-corner-all ui-body-'+ thisTheme +'"></div>').parent();
+      var helpBtn = $('<a href="#" class="ui-input-clear ui-saphelp" title="search help">search help</a>')
+                .bind('vclick', function (e) {
+                    e.preventDefault();
+                    if ( !opts.disabled ) { self.inputElement.trigger('saphelp', {'method': 'open'}); }
+                    setTimeout( function() { $(e.target).closest("a").removeClass($.mobile.activeBtnClass); }, 300);
+                })
+                .buttonMarkup({icon: 'saphelp', iconpos: 'notext', corners:true, shadow:true})
+                .css({'vertical-align': 'middle', 'float': 'right'});
+      // the formPage is kept unitialized and the page() will be called in the first open call
+      var formPage = $("<div id='formPage' data-role='dialog' class='ui-dialog-datebox' data-theme='a'>" + 
+                            "<div data-role='header' data-backbtn='false' data-theme='a'>" +
+                                "<div class='ui-title'>Search Help</div>" +
+                            "</div>" +
+                        "<div data-role='content'></div>" +
+                        "</div>")
+                    .appendTo( $.mobile.pageContainer );
+                   // .page(); //.css('minHeight', '0px').css('zIndex', o.zindex).addClass(o.transition),
+      var listPage = $("<div data-role='dialog' class='ui-dialog-datebox' data-theme='a'>" +
+                            "<div data-role='header' data-backbtn='false' data-theme='a'>" +
+                                "<div class='ui-title'>Search Help</div>" +
+                            "</div>" + 
+                            "<div data-role='content'></div>" +
+                        "</div>").appendTo( $.mobile.pageContainer )
+                    .page();
+     
+
+      inputElement.after(helpBtn);
+     inputElement.bind('saphelp', this._saphelpHandler);
+     inputElement
+            .removeClass('ui-corner-all ui-shadow-inset')
+            .focus(function(){
+                if ( ! opts.disabled ) {
+                    focusedEl.addClass('ui-focus');
+//                     if ( opts.noButtonFocusMode ) { focusedEl.addClass('ui-focus'); input.trigger('datebox', {'method': 'open'}); }
+                }
+                inputElement.removeClass('ui-focus');
+            })
+            .blur(function(){
+                focusedEl.removeClass('ui-focus');
+                inputElement.removeClass('ui-focus');
+            })
+            .change(function() {
+                self._update();
+            });
+     $.extend(this, {
+            thisPage: thisPage,
+            transition: transition,
+            formInit: formInit,
+            inputElement: inputElement,
+            listPage: listPage,
+            focusedEl: focusedEl,
+            formPage: formPage,
+        });
+    },
+    /** Custom method to handle updates. */
+    _update: function() {
+      var inputElement = this.element;
+      var opts = $.extend(this.options, inputElement.jqmData("options"));
+      $(document).trigger("saphelpupdate");
+
+//       inputElement.siblings("button").text(inputElement.val());
+        // FIXME
+//       
+    },
+    /* Externally callable method to force a refresh of the widget. */
+    refresh: function() {
+      return this._update();
+    }
+    } );
+    
+    /* Handler which initialises all widget instances during page creation. */
+    $(document).bind("pagecreate", function(e) {
+      $(document).trigger("saphelpbeforecreate");
+        return $(":jqmData(role='saphelp')", e.target).saphelp();
+    });
+
+})( jQuery );

static/sapmobile/jquery.mobile.saphelp.js.orig

+/*
+ * Sources:
+ * JQuerymobile.datebox
+ * http://www.hiddentao.com/archives/2011/11/07/how-to-write-a-custom-widget-for-jquery-mobile/
+ * http://wiki.jqueryui.com/w/page/12138135/Widget%20factory
+ *
+ */
+(function( $, undefined ) {
+    $.widget( "mobile.saphelp", $.mobile.widget /* optional - an existing widget prototype to inherit from */, /* An object literal to become the widget's prototype*/ {
+    closed: false,
+        /** Available options for the widget are specified here, along with default values. */
+    options: {
+      resultField: "name",
+      disabled: false, // needed?
+      baseUrl: "/sapmobile/sh/"
+    },
+    _saphelpHandler: function(event, payload) {
+        // Handle all event triggers that have an internal effect
+        
+        if ( ! event.isPropagationStopped() ) {
+            switch (payload.method) {
+                case 'open':
+                    $(this).jqmData('saphelp').open();
+                    break;
+                case 'close':
+                    $(this).jqmData('saphelp').close(payload.fromCloseButton);
+                    break;
+                case 'set':
+                    $(this).val(payload.value);
+                    $(this).trigger('change');
+                    break;
+                case 'search':
+                    $(this).jqmData('saphelp').search(payload.value);
+                    break;
+//                 case 'doset':
+//                     if ( $(this).data('datebox').options.mode === 'timebox' || $(this).data('datebox').options.mode === 'durationbox' ) {
+//                         $(this).trigger('datebox', {'method':'set', 'value':$(this).data('datebox')._formatTime($(this).data('datebox').theDate)});
+//                     } else {
+//                         $(this).trigger('datebox', {'method':'set', 'value':$(this).data('datebox')._formatDate($(this).data('datebox').theDate)});
+//                     }
+//                 case 'dooffset':
+//                     $(this).data('datebox')._offset(payload.type, payload.amount, true);
+//                     break;
+//                 case 'dorefresh':
+//                     $(this).data('datebox')._update();
+//                     break;
+//                 case 'doreset':
+//                     $(this).data('datebox').hardreset();
+//                     break;
+            }
+        } 
+    },
+    _buildUrl: function(page) {
+      return this.options.baseUrl + page;
+    },
+    // call the server to get the search help metadata and build the search help form
+    // execute the callback after the form initialization
+    _initForm: function() {
+      var self = this;
+      var searchhelpname = this.options.searchHelp;
+      $.mobile.showPageLoadingMsg();
+      $.ajax({
+        url: this._buildUrl('fields'),
+        data: { sh: searchhelpname },
+        context: this.formPage.children(":jqmData(role='content')").first(),
+        dataType: "xml",
+        success: function(data) {
+          var page = '',
+          xmldoc = $(data);
+          page += '<form data-role="sapsearchhelpform">';
+          page += '<input type="hidden" name="sh" value="' + searchhelpname + '">';
+          xmldoc.find('field').each( function(index) {
+            var description = $(this).text(),
+            name = $(this).attr('name'),
+            datatype = $(this).attr('datatype');
+            //FIXME logic for different data types
+            page += '<label for="' + name + '">' + description + '</label>';
+            page += '<input type="text" name="' + name + '">';
+          });
+          page += '</form>';
+          page += '<a href="#searchhelp" id="help-result-button" data-role="button" data-inline="false">Search</a>';
+          $(this).html(page);
+          self.formPage.page();
+          self.formPage.find( ".ui-header a").bind('vclick', function(e) {
+            e.preventDefault();
+            e.stopImmediatePropagation();
+            self.inputElement.trigger('saphelp', {'method':'close', 'fromCloseButton':true});
+        });
+          $(this).children(':jqmData(role="sapsearchhelpform")').submit( function(e) {
+            e.preventDefault();
+            var shFormData = $(this).serialize();
+            self.inputElement.trigger('saphelp', {'method':'search', 'value': shFormData});
+            return false;
+          });
+          $(this).find("a").each(function() {
+            $(this).bind('vclick', function(e) {
+               $(this).parent().children(':jqmData(role="sapsearchhelpform")').submit();
+               return false;
+            });
+          });
+          self.formInit = true;
+          $.mobile.hidePageLoadingMsg();
+          $.mobile.changePage(self.formPage, {'transition': self.transition});
+        },
+        error: function() {
+    		  $.mobile.hidePageLoadingMsg();
+    		}
+      });      
+    },
+    open: function() {
+        // prevent the parent page from being removed from the DOM,
+        this.thisPage.unbind( "pagehide.remove" );
+        var self = this;
+        self.closed = false;
+        if ( this.formInit ) {
+          $.mobile.changePage(self.formPage, {'transition': this.transition});          
+        } else { 
+          this._initForm();
+        }
+    },
+    search: function(value) {
+        var transition = 'pop';
+        // Let's use a dialog
+        // TODO prevent the form page from being removed from the DOM?
+        // this.thisPage.unbind( "pagehide.remove" );
+        var self=this;
+        var searchResultsEl = this.listPage.children(":jqmData(role='content')").first();
+        searchResultsEl.empty();
+        $.mobile.showPageLoadingMsg();
+        $.ajax({
+          url: this._buildUrl('search'),
+          data: value,
+          dataType: "xml",
+          context: searchResultsEl,
+          error: function() {
+              $.mobile.hidePageLoadingMsg();
+          },
+          success: function(data){
+            var xmldoc;
+            var resultTable = $('<table class="ui-mobile-saphelp"></table>');
+            var resultsExist = false;
+            xmldoc = $(data);
+            // header row
+                xmldoc.find('searchhelp result').each( function(index) {
+                // primeira linha (index=0) - header
+                if (index == 0) {
+                    resultsExist = true;
+                    var header = $('<thead></thead>');
+                    var headerRow = $('<tr></tr>');
+                    $(this).children().each( function() {
+                        var th = $('<th></th>').text($(this).attr('title'));
+                        headerRow.append(th);
+                    });
+                    header.append(headerRow);
+                    resultTable.append(header);
+                }
+            });
+            if (resultsExist) {
+            // data
+            var tblBody = $('<tbody></tbody>');
+            xmldoc.find('result').each( function(index) {
+                var resultText = $(this).find(self.options.resultField).text();
+                // TODO exception if not found
+                var resRow = $('<tr></tr>',  {class: 'ui-btn ui-btn-up-a', 'data-result': resultText, 'data-theme': 'a'});
+                $(this).children().each( function() {
+                    var resData = $('<td></td>').text($(this).text());
+                    resRow.append(resData);
+                });
+                tblBody.append(resRow);
+            });
+            resultTable.append(tblBody);
+            $(this).append(resultTable);
+            } else {
+                $(this).append("<p>No results found</p>");
+            }
+          $.mobile.hidePageLoadingMsg();
+          $.mobile.changePage(self.listPage, {'transition': transition});
+          $(this).find("td").each(function() {
+            $(this).bind('vclick', function(e) {
+                e.preventDefault();
+                self.inputElement.trigger('saphelp', {'method':'set', 'value': $(this).parent().attr('data-result')});
+                self.inputElement.trigger('saphelp', {'method':'close'});
+            })
+          });
+          }
+        });
+    },
+    close: function(fromCloseButton) {
+        // Close the controls
+        console.log();
+        var self = this,
+            callback;
+
+            if (!fromCloseButton) {
+                //dialog('close') só faz um history back
+                if (!self.closed) {
+                    window.history.go(-2);
+                }
+            }
+            else
+            {
+                if ($.mobile.activePage == self.listPage)
+                    window.history.go(-1);
+            }
+            if( !self.thisPage.data("page").options.domCache ){
+                self.thisPage.bind( "pagehide.remove", function() {
+                    $(self).remove();
+                });
+            }
+        self.focusedEl.removeClass('ui-focus');
+        // FIXME callBack - precisamos?
+        if ( self.options.closeCallback !== false ) { callback = new Function(self.options.closeCallback); callback(undefined, self); }
+        self.closed = true;
+    },
+    /** Mandatory method - automatically called by jQuery Mobile to initialise the widget. */
+    _create: function() {
+      $(document).trigger("saphelpcreate");
+      var self = this;
+      var inputElement = this.element;
+      var transition = 'pop';
+      // control when the search help form is initialized
+      var formInit = false; 
+      var opts = $.extend(this.options, inputElement.jqmData("options"));
+      var thisPage = inputElement.closest('.ui-page');
+      var thisTheme = 'c';
+      var focusedEl = inputElement.wrap('<div class="ui-input-saphelp ui-input-datebox ui-shadow-inset ui-corner-all ui-body-'+ thisTheme +'"></div>').parent();
+      var helpBtn = $('<a href="#" class="ui-input-clear ui-saphelp" title="search help">search help</a>')
+                .bind('vclick', function (e) {
+                    e.preventDefault();
+                    if ( !opts.disabled ) { self.inputElement.trigger('saphelp', {'method': 'open'}); }
+                    setTimeout( function() { $(e.target).closest("a").removeClass($.mobile.activeBtnClass); }, 300);
+                })
+                .buttonMarkup({icon: 'saphelp', iconpos: 'notext', corners:true, shadow:true})
+                .css({'vertical-align': 'middle', 'float': 'right'});
+      // the formPage is kept unitialized and the page() will be called in the first open call
+      var formPage = $("<div id='formPage' data-role='dialog' class='ui-dialog-datebox' data-theme='a'>" + 
+                            "<div data-role='header' data-backbtn='false' data-theme='a'>" +
+                                "<div class='ui-title'>Search Help</div>" +
+                            "</div>" +
+                        "<div data-role='content'></div>" +
+                        "</div>")
+                    .appendTo( $.mobile.pageContainer );
+                   // .page(); //.css('minHeight', '0px').css('zIndex', o.zindex).addClass(o.transition),
+      var listPage = $("<div data-role='dialog' class='ui-dialog-datebox' data-theme='a'>" +
+                            "<div data-role='header' data-backbtn='false' data-theme='a'>" +
+                                "<div class='ui-title'>Search Help</div>" +
+                            "</div>" + 
+                            "<div data-role='content'></div>" +
+                        "</div>").appendTo( $.mobile.pageContainer )
+                    .page();
+     
+
+      inputElement.after(helpBtn);
+     inputElement.bind('saphelp', this._saphelpHandler);
+     inputElement
+            .removeClass('ui-corner-all ui-shadow-inset')
+            .focus(function(){
+                if ( ! opts.disabled ) {
+                    focusedEl.addClass('ui-focus');
+//                     if ( opts.noButtonFocusMode ) { focusedEl.addClass('ui-focus'); input.trigger('datebox', {'method': 'open'}); }
+                }
+                inputElement.removeClass('ui-focus');
+            })
+            .blur(function(){
+                focusedEl.removeClass('ui-focus');
+                inputElement.removeClass('ui-focus');
+            })
+            .change(function() {
+                self._update();
+            });
+     // Bind the close button on the DIALOG mode.
+            
+        listPage.find( ".ui-header a").bind('vclick', function(e) {
+            e.preventDefault();
+            e.stopImmediatePropagation();
+            self.inputElement.trigger('saphelp', {'method':'close', 'fromCloseButton':true});
+        });
+     $.extend(this, {
+            thisPage: thisPage,
+            transition: transition,
+            formInit: formInit,
+            inputElement: inputElement,
+            listPage: listPage,
+            focusedEl: focusedEl,
+            formPage: formPage,
+        });
+    },
+    /** Custom method to handle updates. */
+    _update: function() {
+      var inputElement = this.element;
+      var opts = $.extend(this.options, inputElement.jqmData("options"));
+      $(document).trigger("saphelpupdate");
+
+//       inputElement.siblings("button").text(inputElement.val());
+        // FIXME
+//       
+    },
+    /* Externally callable method to force a refresh of the widget. */
+    refresh: function() {
+      return this._update();
+    }
+    } );
+    
+    /* Handler which initialises all widget instances during page creation. */
+    $(document).bind("pagecreate", function(e) {
+      $(document).trigger("saphelpbeforecreate");
+        return $(":jqmData(role='saphelp')", e.target).saphelp();
+    });
+
+})( jQuery );

static/sapmobile/jquery.mobile.saphelp.orig.js

+/*
+ * Sources:
+ * JQuerymobile.datebox
+ * http://www.hiddentao.com/archives/2011/11/07/how-to-write-a-custom-widget-for-jquery-mobile/
+ * http://wiki.jqueryui.com/w/page/12138135/Widget%20factory
+ * 
+ */
+(function( $, undefined ) {
+    $.widget( "mobile.saphelp", $.mobile.widget /* optional - an existing widget prototype to inherit from */, /* An object literal to become the widget's prototype*/ {
+        /** Available options for the widget are specified here, along with default values. */
+    options: {
+      mode: "default",
+    },
+    _saphelpHandler: function(event, payload) {
+        // Handle all event triggers that have an internal effect
+        
+        if ( ! event.isPropagationStopped() ) {
+            switch (payload.method) {
+                case 'open':
+                    $(this).jqmData('saphelp').open();
+                    break;
+                case 'close':
+                    console.log("close");
+                    $(this).jqmData('saphelp').close(payload.fromCloseButton);
+                    break;
+                case 'set':
+                    console.log("set");
+                    $(this).val(payload.value);
+                    $(this).trigger('change');
+                    break;
+//                 case 'doset':
+//                     if ( $(this).data('datebox').options.mode === 'timebox' || $(this).data('datebox').options.mode === 'durationbox' ) {
+//                         $(this).trigger('datebox', {'method':'set', 'value':$(this).data('datebox')._formatTime($(this).data('datebox').theDate)});
+//                     } else {
+//                         $(this).trigger('datebox', {'method':'set', 'value':$(this).data('datebox')._formatDate($(this).data('datebox').theDate)});
+//                     }
+//                 case 'dooffset':
+//                     $(this).data('datebox')._offset(payload.type, payload.amount, true);
+//                     break;
+//                 case 'dorefresh':
+//                     $(this).data('datebox')._update();
+//                     break;
+//                 case 'doreset':
+//                     $(this).data('datebox').hardreset();
+//                     break;
+            }
+        } 
+    },
+    open: function() {
+        console.log('open');
+        var transition = 'pop';
+        // Let's use a dialog
+        // prevent the parent page from being removed from the DOM,
+        this.thisPage.unbind( "pagehide.remove" );
+//         o.useDialog = true;
+//         self.pickPageContent.append(self.pickerContent);
+//         self.pickerContent.css({'top': 'auto', 'left': 'auto', 'marginLeft': 'auto', 'marginRight': 'auto'}).removeClass('ui-overlay-shadow ui-datebox-hidden');
+        $.mobile.changePage(this.pickPage, {'transition': transition});
+        //talvez usar o loadpage para ele correr as cenas do mobile
+        var self=this;
+        $.ajax({
+          url: "helpform.html",
+          dataType: "html",
+          context: this.pickPage.children(":jqmData(role='content')").first(),
+          success: function(data){
+            $(this).html(data);
+            var helpInput = $(this).children("input").first();
+            $(this).children("a").each(function() {
+                $(this).bind('vclick', function(e) {
+                    e.preventDefault();
+                    self.inputElement.trigger('saphelp', {'method':'set', 'value': $(helpInput).val()});
+                    self.inputElement.trigger('saphelp', {'method':'close'});
+                })
+                });
+          }
+        });
+    },
+    close: function(fromCloseButton) {
+        // Close the controls
+        var self = this,
+            callback;
+
+        
+            if (!fromCloseButton) {
+                $(self.pickPage).dialog('close');
+            }
+            if( !self.thisPage.data("page").options.domCache ){
+                self.thisPage.bind( "pagehide.remove", function() {
+                    $(self).remove();
+                });
+            }
+//             self.pickerContent.addClass('ui-datebox-hidden').removeAttr('style').css('zIndex', self.options.zindex);
+//             self.thisPage.append(self.pickerContent);
+//         self.focusedEl.removeClass('ui-focus');
+        
+        if ( self.options.closeCallback !== false ) { callback = new Function(self.options.closeCallback); callback(self.theDate, self); }
+    },
+    /** Mandatory method - automatically called by jQuery Mobile to initialise the widget. */
+    _create: function() {
+      $(document).trigger("saphelpcreate");
+      var inputElement = this.element;
+      var opts = $.extend(this.options, inputElement.jqmData("options"));
+      var thisPage = inputElement.closest('.ui-page');
+      var helpBtn = $("<button>HELP!!!</button>");
+      inputElement.after(helpBtn);
+      helpBtn.bind('vclick', function (e) {
+              console.log('XXX');
+                    e.preventDefault();
+                    if ( !opts.disabled ) { inputElement.trigger('saphelp', {'method': 'open'}); }
+//                     setTimeout( function() { $(e.target).closest("a").removeClass($.mobile.activeBtnClass); }, 300);
+                })
+     inputElement.bind('saphelp', this._saphelpHandler);
+     
+     $.extend(this, {
+            thisPage: thisPage,
+            inputElement: inputElement,
+            pickPage: $("#xpto"), // TODO generate this dinamically
+/*            
+            pickPage: pickPage,
+            pickPageContent: pickPageContent,
+            pickPageTitle: pickPageTitle,
+            focusedEl: focusedEl,*/
+        });
+    },
+    /** Custom method to handle updates. */
+    _update: function() {
+      var inputElement = this.element;
+      var opts = $.extend(this.options, inputElement.jqmData("options"));
+      $(document).trigger("saphelpupdate");
+
+//       inputElement.siblings("button").text(inputElement.val());
+        // FIXME
+//       
+    },
+    /* Externally callable method to force a refresh of the widget. */
+    refresh: function() {
+      return this._update();
+    }
+    } );
+    /* Handler which initialises all widget instances during page creation. */
+      $(document).bind("pagecreate", function(e) {
+        $(document).trigger("saphelpbeforecreate");
+        return $(":jqmData(role='saphelp')", e.target).saphelp();
+      });
+//   $.fn.myPlugin = function() {
+//   
+//     // Do your awesome plugin stuff here
+// 
+//   };
+})( jQuery );

static/sapmobile/list.xml

+<?xml version="1.0" encoding="utf-8" ?>
+<saphelpresults>
+    <result>
+        <nbr title="Number">4230498290</nbr>
+        <name title="Name">AAAAA</name>
+        <datum title="Date">2010-01-01</datum>
+    </result>
+    <result>
+        <nbr title="Number">8904689058</nbr>
+        <name title="Name">BBBBB</name>
+        <datum title="Date">2011-02-03</datum>
+    </result>
+    <result>
+        <nbr title="Number">5089503480</nbr>
+        <name title="Name">CCCCCC</name>
+        <datum title="Date">2013-04-05</datum>
+    </result>
+    <result>
+        <nbr title="Number">9584680</nbr>
+        <name title="Name">DDDDDDD</name>
+        <datum title="Date">2014-05-06</datum>
+    </result>
+</saphelpresults>
Add a comment to this file

static/sapmobile/sap_mobile_qr_code.png

Added
New image
Add a comment to this file

static/sapmobile/saphelp.png

Added
New image

static/sapmobile/sapmobile.js

+//namespace for sapmobile utilities
+var SMU = {};
+
+SMU.STATUS_FIELDS = [
+    ['streg','dareg','uareg'],
+    ['stlbg','dalbg','ualbg'],
+    ['stlad','dalen','ualen'],
+    ['stabf','dtabf','uzabf'],
+    ['sttbg','datbg','uatbg'],
+    ['stten','daten','uaten']
+];
+
+// get/set a shipment from the global cache and return the shipment xml
+// wrapped in a jQuery object 
+SMU.shipment = function(tknum, upd_shipment) {
+    var shipment;
+    var shipments = $('#listPage').data('shipmentList'); 
+    if (upd_shipment !== undefined) {
+        shipments[tknum] = upd_shipment;
+        $('#listPage').data('shipmentList', shipments);
+        shipment = upd_shipment;
+    } else {      
+        shipment = shipments[tknum];
+    }
+    if (shipment !== undefined) {
+        return $(shipment);
+    }
+};
+
+SMU.display_shipment_detail = function(shipment) {
+    var page = $("#detailPage");
+	page.find('h1').text("Shipment " + shipment.find('tknum').text());
+	page.find('span#shipment').text(shipment.find('tknum').text());
+	page.find('span#carrier').text(shipment.find('tdlnr').text() + ' ' + 
+				shipment.find('name1').text());
+	page.find('span#route').text(shipment.find('route').text() + ' ' + 
+				shipment.find('bezei').text());
+	for (var i=0; i<SMU.STATUS_FIELDS.length; i++ ) {
+	    SMU.updateStatusField(SMU.STATUS_FIELDS[i][0], SMU.STATUS_FIELDS[i][1],
+	            SMU.STATUS_FIELDS[i][2], page, shipment);
+	}
+}
+
+SMU.activate_button = function(btn_id) {
+	$(btn_id).attr('data-icon','check');
+	$(btn_id + ' span.ui-icon').removeClass("ui-icon-arrow-r").addClass("ui-icon-check");
+	$(btn_id).attr('data-theme','b').removeClass("ui-btn-up-c").addClass("ui-btn-up-b");
+    $(btn_id).removeClass("ui-btn-hover-c").addClass("ui-btn-hover-b");	
+};
+
+SMU.deactivate_button = function(btn_id) {
+	$(btn_id).attr('data-icon','arrow-r');
+	$(btn_id + ' span.ui-icon').removeClass("ui-icon-check").addClass("ui-icon-arrow-r");
+	$(btn_id).attr('data-theme','c').removeClass("ui-btn-up-b").addClass("ui-btn-up-c");
+	$(btn_id).removeClass("ui-btn-hover-b").addClass("ui-btn-hover-c");	
+};
+
+SMU.updateList = function(query_date, query_status) {
+	var urlstr = '/sapmobile/list?date=' + query_date + '&status=' + query_status;
+	$.mobile.showPageLoadingMsg();
+	$.ajax({ url: urlstr, dataType: 'xml', 
+	    success: function(data) {
+	        var node, tknum, shipmentList, ul, xmldoc;
+	        shipmentList = {};
+    		ul = $('ul#itemlist');
+    		ul.empty();
+    		xmldoc = $(data);
+    		xmldoc.find('shipment').each( function(index) {
+    			var s = '<li><a data-transition="slide" href="#detailPage">';
+    			s += '<h3>' + $(this).find('tknum').text() + ' ' + $(this).find('name1').text() + '</h3>';
+    			s += '<p>Route ' + $(this).find('route').text() + ' ' + $(this).find('bezei').text(); 
+    			s += '| Carrier ' + $(this).find('tdlnr').text() + '</p></a></li>';
+                node = $(s);
+    			shipment_xml = (new XMLSerializer()).serializeToString(this);
+    			tknum = $(this).find('tknum').text();
+    			shipmentList[tknum] = shipment_xml;
+    			node.find('a:first').data('tknum', tknum);
+    			ul.append(node);
+    			});
+    		$('#listPage').data('shipmentList', shipmentList);
+    		ul.listview('refresh');
+    		ul.find('a').click( function() {
+    			$('#detailPage').data('tknum', $(this).data('tknum'));
+    		});
+    		$.mobile.hidePageLoadingMsg();
+    		},
+		error: function() {
+		    $.mobile.hidePageLoadingMsg();
+		}
+	})
+};
+
+SMU.format_time = function(time_value) {
+    return time_value.substr(0,8);
+}
+
+SMU.updateStatusField = function(status_field, date_field, time_field, page, shipment) {
+    var status = shipment.find(status_field).text();
+	if (status === 'X') { 
+	    SMU.activate_button('#btn_' + status_field);
+	    page.find('#s_' + date_field + ' p').text(shipment.find(date_field).text());
+	    page.find('#s_' + time_field + ' p').text(SMU.format_time(shipment.find(time_field).text()));
+	} else {
+	    SMU.deactivate_button('#btn_' + status_field);
+	    page.find('#s_' + date_field + ' p').text('');
+	    page.find('#s_' + time_field + ' p').text('');
+	}
+};
+
+SMU.addStatusEventManager = function(detailpage, status_field, date_field, time_field) {
+    detailpage.find('#btn_' + status_field).click( function() {
+        var tknum, shipment, status;
+        tknum = $("#detailPage").data('tknum');
+        shipment = SMU.shipment(tknum);
+	    status = shipment.find(status_field).text();
+		$.get('/sapmobile/action', {'mode':status_field, 'sid':tknum}, function(data) {
+		    var xmldoc, shipment_node, shipment_xml, shipment;
+		    xmldoc = $(data);
+		    shipment_node = $(data).find('shipment')[0];
+		    shipment_xml = (new XMLSerializer()).serializeToString(shipment_node);
+		    shipment = SMU.shipment(tknum, shipment_xml);
+		    SMU.display_shipment_detail(shipment);
+		    return false;
+        }, 'xml');    
+	});
+};
+
+(function($) { 
+	var methods = {
+		initSelectionPage : function() { 
+			var selpage = $("#selectionPage");
+			selpage.find("#selectionform").submit( function(event) {
+				var query_date = $(this).find('#date').val();
+				var query_status = $(this).find('#status').val();
+				var listpage = $('#listPage');
+				listpage.data("new_query_flag", true);
+				listpage.data("query_date", query_date);
+				listpage.data("query_status", query_status);
+				$.mobile.changePage("#listPage");
+				return false;
+			});
+		},
+		initListPage : function() { 
+			var listpage = $("#listPage");
+			listpage.bind("pageshow", function(event, ui) {
+				if (listpage.data("new_query_flag")) {
+					SMU.updateList(listpage.data("query_date"), listpage.data("query_status"));
+					listpage.data("new_query_flag", false);
+				}
+			});
+		},
+		initDetailPage : function() { 
+			var detailpage = $("#detailPage");
+			detailpage.bind("pageshow", function(event, ui) {
+			    var page, shipment;
+				page = $("#detailPage");
+				shipment = SMU.shipment(page.data('tknum'));
+                SMU.display_shipment_detail(shipment);
+			});
+			detailpage.find('#logButton').click( function() {
+				$('#logPage').data('tknum', $("#detailPage").data('tknum'));
+			});
+			for (var i=0; i<SMU.STATUS_FIELDS.length; i++ ) {
+			    SMU.addStatusEventManager(detailpage, SMU.STATUS_FIELDS[i][0],
+			        SMU.STATUS_FIELDS[i][1], SMU.STATUS_FIELDS[i][2]);
+			}
+		},
+		initLogPage : function() {
+			$("#logPage").bind("pageshow", function(event, ui) {
+			    var page, tknum, shipment;
+				page = $("#logPage");
+				tknum = page.data('tknum');
+				shipment = SMU.shipment(tknum);
+				page.find('input#tknum').val(shipment.find('tknum').text());
+				page.find('span.var_tknum').text(shipment.find('tknum').text());
+				var logtext = shipment.find('textlog').text();
+				if (logtext) {
+				    logtext = logtext.replace(/\n/g, '<br />');
+					page.find('p#var_log').html(logtext);
+				}
+			});
+			$("#logForm").submit( function() {
+			    var tknum =  $(this).find('#tknum').val();
+			    var logtext = $(this).find('#logtext').val();
+			    var urlstr = '/sapmobile/log';
+			    $.ajax({ type: 'POST', 
+			        url: urlstr, 
+			        data: { logtext: logtext, sid: tknum },
+			        dataType: 'xml', 
+			        success: function(data) {
+			            var xmldoc, shipment_node, shipment_xml, shipment, logtext;
+			            xmldoc = $(data);
+			            shipment_node = $(data).find('shipment')[0];
+			            shipment_xml = (new XMLSerializer()).serializeToString(shipment_node);
+			            shipment = SMU.shipment(tknum, shipment_xml);
+			            logtext = shipment.find('textlog').text();
+		                logtext = logtext.replace(/\n/g, '<br />');
+		                $("#logPage").find('p#var_log').html(logtext);
+		                $("#logPage").find('textarea#logtext').val('');
+		            }
+	            });
+	            return false;
+			});
+		},
+		initAll : function() { 
+			$().initApp("initSelectionPage");
+			$().initApp("initListPage");
+			$().initApp("initDetailPage"); 
+			$().initApp("initLogPage");
+		}
+	};
+	$.fn.initApp = function(method) {
+		if ( methods[method] ) {
+			return methods[ method ].apply( this,
+				Array.prototype.slice.call( arguments, 1 )); 
+		} else if ( typeof method === 'object' || ! method ) {
+			return methods.initAll.apply( this, arguments ); 
+		} else {
+			$.error( 'Method ' + method + ' does not exist' ); 
+		}
+	};
+})(jQuery);
+
+
+$(document).ready( function() {
+	$(document).initApp("initAll");
+ });
Add a comment to this file

static/sapmobile/sapsearchhelp.png

Added
New image

static/stw_shipments/.senchasdk

+sdk

static/stw_shipments/app.js

+//<debug>
+Ext.Loader.setPath({
+    'Ext': '../sdk/src'
+});
+//</debug>
+
+Ext.application({
+    name: 'Shipments',
+
+    requires: [
+        'Ext.MessageBox'
+    ],
+
+    views: ['Main', 'SelectionScreen', 'ShipmentList', 'ShipmentDetails', 'ShipmentLog'],
+    controllers: ['Application'],
+    models: ['ShipmentSummary', 'ShipmentDetail'],
+    stores: ['ShipmentList'],
+
+    icon: {
+        57: 'resources/icons/Icon.png',
+        72: 'resources/icons/Icon~ipad.png',
+        114: 'resources/icons/Icon@2x.png',
+        144: 'resources/icons/Icon~ipad@2x.png'
+    },
+
+    phoneStartupScreen: 'resources/loading/Homescreen.jpg',
+    tabletStartupScreen: 'resources/loading/Homescreen~ipad.jpg',
+
+    launch: function() {
+        // Destroy the #appLoadingIndicator element
+        Ext.fly('appLoadingIndicator').destroy();
+
+        // Initialize the main view
+        Ext.Viewport.add(Ext.create('Shipments.view.Main'));
+    },
+
+    onUpdated: function() {
+        Ext.Msg.confirm(
+            "Application Update",
+            "This application has just successfully been updated to the latest version. Reload now?",
+            function() {
+                window.location.reload();
+            }
+        );
+    }
+});

static/stw_shipments/app.json

+{
+    /**
+     * The application's namespace, used by Sencha Command to generate classes
+     */
+    "name": "Shipments",
+
+    /**
+     * List of all JavaScript assets in the right execution order.
+     * Each item is an object with the following format:
+     *      {
+     *          "path": "path/to/script.js" // Relative path to this app.json file
+     *          "update": "delta"           // (Optional)
+     *                                      //  - If not specified, this file will only be loaded once, and
+     *                                      //    cached inside localStorage until this value is changed.
+     *                                      //  - "delta" to enable over-the-air delta update for this file
+     *                                      //  - "full" means full update will be made when this file changes
+     *
+     *      }
+     */
+    "js": [
+        {
+            "path": "../sdk/sencha-touch.js"
+        },
+        {
+            "path": "app.js",
+            "update": "delta"
+        }
+    ],
+
+    /**
+     * List of all CSS assets in the right inclusion order.
+     * Each item is an object with the following format:
+     *      {
+     *          "path": "path/to/item.css" // Relative path to this app.json file
+     *          "update": "delta"          // (Optional)
+     *                                     //  - If not specified, this file will only be loaded once, and
+     *                                     //    cached inside localStorage until this value is changed to either one below
+     *                                     //  - "delta" to enable over-the-air delta update for this file
+     *                                     //  - "full" means full update will be made when this file changes
+     *
+     *      }
+     */
+    "css": [
+        {
+            "path": "resources/css/app.css",
+            "update": "delta"
+        }
+    ],
+
+    /**
+     * Used to automatically generate cache.manifest (HTML 5 application cache manifest) file when you build
+     */
+    "appCache": {
+        /**
+         * List of items in the CACHE MANIFEST section
+         */
+        "cache": [
+            "index.html"
+        ],
+        /**
+         * List of items in the NETWORK section
+         */
+        "network": [
+            "*"
+        ],
+        /**
+         * List of items in the FALLBACK section
+         */
+        "fallback": []
+    },
+
+    /**
+     * Extra resources to be copied along when build
+     */
+    "extras": [
+        "resources/images",
+        "resources/icons",
+        "resources/loading"
+    ],
+
+    /**
+     * Directory path to store all previous production builds. Note that the content generated inside this directory
+     * must be kept intact for proper generation of delta between updates
+     */
+    "archivePath": "archive",
+
+    /**
+     * Default paths to build this application to for each environment
+     */
+    "buildPaths": {
+        "testing": "build/testing",
+        "production": "build/production",
+        "package": "build/package",
+        "native": "build/native"
+    },
+
+    /**
+     * Build options
+     */
+    "buildOptions": {
+        "product": "touch",
+        "minVersion": 3,
+        "debug": false,
+        "logger": "no"
+    },
+
+    /**
+     * Uniquely generated id for this application, used as prefix for localStorage keys.
+     * Normally you should never change this value.
+     */
+    "id": "2d1ece80-6f86-11e1-9897-bb8eac5807fc"
+}

static/stw_shipments/app/controller/Application.js

+Ext.define('Shipments.controller.Application', {
+    extend: 'Ext.app.Controller',
+
+    config: {
+        refs: {
+            main: 'mainview',
+            selection: 'selectionscreen',
+            shipList: 'shiplist',
+            shipDetail: 'shipdetails',
+            shipLog: 'shipmentlog',
+            logButton: '#logButton'
+        },
+
+        control: {
+            selection: {
+                // how about submit event?
+               selection: 'onSelection'
+            },
+            shiplist: {
+                itemdisclosure: 'onItemDisclosure'
+            },
+            shipdetails: {
+            },
+            main: {
+                push: 'onMainPush',
+                pop: 'onMainPop'
+            },
+            logButton: {
+                tap: 'onLogEdit'
+            }
+        },
+        routes: {
+            'list': 'showShipmentList',
+            'shipment/:id': 'showShipment'
+        }
+    },
+    
+    onSelection: function() {
+        this.redirectTo('list');
+    },
+    
+    showShipmentList: function() {
+        if (!this.shipList) {
+            this.shipList = Ext.create('Shipments.view.ShipmentList');
+        }
+
+        // Push the show contact view into the navigation view
+        this.getMain().push(this.shipList);
+    },
+    
+    showShipment: function(sId) {
+        if (!this.shipDetail) {
+            this.shipDetail = Ext.create('Shipments.view.ShipmentDetails');
+        }
+        this.shipDetail.loadRecord(sId);
+        this.getMain().push(this.shipDetail);
+    },
+    
+    onItemDisclosure: function(rec, item) {
+        this.redirectTo('shipment/' + rec.getId());
+    },
+    
+    onMainPush: function(view, item) {
+        if (item.xtype == "shipdetails") {
+            // dowesn't work - title comes from item-config
+//             var bar = this.getMain().getNavigationBar();
+//             if (bar.titleComponent.element) bar.titleComponent.element.setWidth('auto');
+//             bar.titleComponent.setTitle(item.getTitle());
+//             bar.refreshProxy();
+            this.showLogButton();
+        }
+    },
+    
+    onMainPop: function(view, item) {
+        if (item.xtype == "shipdetails") {
+            this.hideLogButton();
+        }
+    },
+    
+    onLogEdit: function(sId) {
+        if (!this.shipLog) {
+            this.shipLog = Ext.create('Shipments.view.ShipmentLog');
+        }
+        this.shipLog.updateLog(this.shipDetail.getRecord());
+        this.getMain().push(this.shipLog);
+    },
+    
+    showLogButton: function() {
+        var logButton = this.getLogButton();
+
+        if (!logButton.isHidden()) {
+            return;
+        }
+        logButton.show();
+    },
+
+    hideLogButton: function() {
+        var logButton = this.getLogButton();
+
+        if (logButton.isHidden()) {
+            return;
+        }
+        logButton.hide();
+    },
+});

static/stw_shipments/app/model/ShipmentDetail.js

+Ext.define('Shipments.model.ShipmentDetail', {
+    extend: 'Ext.data.Model',
+
+    config: {
+        fields: [
+           'carrier',
+           'route',
+           'checkin',
+           'load_start',
+           'load_end',
+           'complete',
+           'start',
+           'end',
+           'checkin_d',
+           'load_start_d',
+           'load_end_d',
+           'complete_d',
+           'start_d',
+           'end_d',
+           'checkin_t',
+           'load_start_t',
+           'load_end_t',
+           'complete_t',
+           'start_t',
+           'end_t',
+           'log'
+        ],
+        proxy: {
+            type: 'ajax',
+            url: 'd.json',
+            reader: {
+                type: 'json',
+            }
+        }
+    }
+});
+

static/stw_shipments/app/model/ShipmentSummary.js

+Ext.define('Shipments.model.ShipmentSummary', {
+    extend: 'Ext.data.Model',
+
+    config: {
+        fields: [
+            'title',
+            'description',
+        ]
+    }
+});

static/stw_shipments/app/store/ShipmentDetails.js

+Ext.define('Shipments.store.ShipmentDetails', {
+    extend: 'Ext.data.Store',
+
+    config: {
+        model: 'Shipments.model.ShipmentDetail',
+        proxy: {
+            type: 'ajax',
+            url: 'd.json',
+            reader: {
+                type: 'json',
+                rootProperty: 'shipments'
+            }
+        }
+    }
+});

static/stw_shipments/app/store/ShipmentList.js

+Ext.define('Shipments.store.ShipmentList', {
+    extend: 'Ext.data.Store',
+
+    config: {
+        model: 'Shipments.model.ShipmentSummary',
+        autoLoad: true,
+        proxy: {
+            type: 'ajax',
+            url: 's.json',
+            reader: {
+                type: 'json',
+                rootProperty: 'shipments'
+            }
+        }
+    }
+});
Add a comment to this file

static/stw_shipments/app/view/.Shipments2.js.kate-swp

Binary file added.

static/stw_shipments/app/view/Main.js

+Ext.define("Shipments.view.Main", {
+    extend: 'Ext.navigation.View',
+    xtype: 'mainview',
+    requires: ['Ext.TitleBar'],
+    
+    config: {
+        fullscreen: true,
+        autoDestroy: false, // this is REALLY needed! why?
+        navigationBar: {
+            items: [
+                {
+                    xtype: 'button',
+                    id: 'logButton',
+                    text: 'Log',
+                    align: 'right',
+                    hidden: true,
+                    hideAnimation: Ext.os.is.Android ? false : {
+                        type: 'fadeOut',
+                        duration: 200
+                    },
+                    showAnimation: Ext.os.is.Android ? false : {
+                        type: 'fadeIn',
+                        duration: 200
+                    }
+                },
+            ]
+        },
+
+        items: [
+            {
+                xtype: 'selectionscreen'
+            },
+            {
+                docked: 'bottom',
+                xtype: 'titlebar',
+                title: 'cognitiva.com'
+            }
+        ],
+
+        title: 'Shipment Monitor',
+        iconCls: 'home',
+
+        scrollable: true,
+
+    },
+});

static/stw_shipments/app/view/SelectionScreen.js

+function getShipmentListConfig() {
+    return {
+        xtype: 'shiplist',
+        store: Ext.create('Ext.data.Store', {
+            extend: 'Ext.data.Store',
+            autoLoad: true,
+            fields: ['title', 'description'],
+            proxy: {
+                type: 'ajax',
+                url: 's.json',
+                reader: {
+                    type: 'json',
+                    rootProperty: 'shipments'
+                }
+            }
+        })
+    }
+}
+
+Ext.define("Shipments.view.SelectionScreen", {
+    extend: 'Ext.form.Panel',
+    xtype: 'selectionscreen',
+    requires: [
+        'Ext.form.FieldSet',
+    ],
+    
+    config: {
+        title: 'Shipment Selection',
+        items: [{
+                        xtype: 'fieldset',
+                        title: 'Shipment Monitor',
+                        items: [
+                            {
+                                 xtype: 'datepickerfield',
+                                 name: 'date',
+                                 label: 'Date',
+                                 value: new Date()
+                             },
+                                    {
+                                        xtype: 'selectfield',
+                                        label: 'Status',
+                                        name: 'status',
+                                        options: [
+                                            {value: "", text: "Choose status..."},
+                                            {value: "1", text: "Planned"},
+                                            {value: "2", text: "Planning completed"},
+                                            {value: "3", text: "Check-in"},
+                                            {value: "99", text: "Other"}
+                                        ]
+                                    }
+                        ]
+                    },
+                   {
+                       xtype: 'button',
+                       ui: 'confirm',
+                       text: 'List',
+                       handler: function() {
+                           this.up('selectionscreen').fireEvent('selection');
+//                            this.up('selectionscreen').submit();
+//                            this.up('shipments').push(getShipmentListConfig()); // finds the parent xtype 'contactform'
+                       }
+                   }
+       ],
+
+        iconCls: 'home',
+
+        styleHtmlContent: true,
+        scrollable: true,
+
+    },
+});

static/stw_shipments/app/view/ShipmentDetails.js

+Ext.define('Shipments.view.ShipmentDetails', {
+    extend: 'Ext.Panel',
+    xtype: 'shipdetails',
+    
+    requires: [
+        'Shipments.view.StatusItem'
+    ],
+    
+    loadRecord: function(recId) {
+        var sd = Ext.ModelManager.getModel('Shipments.model.ShipmentDetail');
+
+        //Uses the configured RestProxy to make a GET request to /users/123
+        sd.load(recId, {
+            scope: this,
+            success: function(record, operation) {
+                this.setRecord(record);
+                this.displayRecord();
+            }
+        });
+    },
+    
+    displayRecord: function() {
+        var idname,
+            dname,
+            tname,
+            widget,
+            r = this.getRecord();
+        this.setTitle("Shipment " + r.getId().toString());
+        this.child("#shipment-desc").setHtml("Shipment: "+ r.getId().toString() + "<br>" +
+                                             "Carrier: " + r.get('carrier') + "<br>" +
+                                             "Route: " + r.get('route') + "<br>");
+        
+        var btns = ['checkin','load_start','load_end','complete','start','end'];
+        for (var i=0; i<btns.length; i++) {
+            dname = btns[i] + '_d';
+            tname = btns[i] + '_t';
+            idname = '#' + btns[i] + '_st';
+            if (r.get(btns[i])) {
+                widget = this.query(idname)[0];
+                if (widget) {
+                    widget.setButtonChecked();
+                    widget.setDate(r.get(dname));
+                    widget.setTime(r.get(tname));
+                }
+            } 
+        }
+                
+        // maybe on initialize or on controller
+        this.on({
+            // Ext.Buttons have an xtype of 'button', so we use that are a selector for our delegate
+            delegate: 'button',
+
+            tap: function(btn) {
+                    var record,
+                        widgetid,
+                        fieldname,
+                        fieldname_t,
+                        fieldname_d,
+                        status,
+                        date = '',  
+                        time = '',
+                        widget = btn.up('statusitem'),
+                        pf;
+                
+                // pad with zeros 
+                var pf = function(val) {
+                    if (val < 10) { 
+                        return '0' + val;
+                    } else {
+                        return val;
+                    }
+                };
+                // only for buttons in statusitem widgets
+                if (widget) {
+                    record = widget.up('shipdetails').getRecord();
+                    widgetid =  widget.getId()
+                    fieldname = widgetid.substring(0, widgetid.length-3);
+                    fieldname_d = fieldname + '_d';
+                    fieldname_t = fieldname + '_t';
+                    if (record.get(fieldname)) {
+                        status = false;
+                        record.set(fieldname, status);
+                        record.set(fieldname_d, '');
+                        record.set(fieldname_t, '');
+                    } else {
+                        var t = new Date(),
+                            m = t.getMonth() + 1;
+                        
+                        date = t.getFullYear() + '-' + pf(m) + '-' + pf(t.getDate());
+                        time = pf(t.getHours()) + ':' + pf(t.getMinutes()) + ':' + pf(t.getSeconds());
+                        status = true;
+                        record.set(fieldname, status);
+                        record.set(fieldname_d, date);
+                        record.set(fieldname_t, time);                        
+                    }                    
+                    
+                    widget.toggle(status, date, time);
+                }
+                
+            }
+        });
+    },
+    config: {
+        record: undefined,
+        title: 'Shipment Details',
+
+        store: undefined,
+        items: [{
+            id: "shipment-desc",
+            html: "",
+            styleHtmlContent: true,
+        },{
+            xtype: 'toolbar',
+            title: 'Shipment Status'
+        },{
+            id: "shipment-status",
+            styleHtmlContent: true,
+            layout: 'vbox',
+            items: [{
+                    html: "<h4>Realization times</h4>"
+                },{
+                    id: 'checkin_st',
+                    xtype: 'statusitem',
+                    text: 'Check In',
+                },{
+                    id: 'load_start_st',
+                    xtype: 'statusitem',
+                    text: 'Load Start',
+                },{
+                    id: 'load_end_st',
+                    xtype: 'statusitem',
+                    text: 'Load End',
+                },{
+                    id: 'complete_st',
+                    xtype: 'statusitem',
+                    text: 'Complete',
+                },{
+                    id: 'start_st',
+                    xtype: 'statusitem',
+                    text: 'Start',
+                },{
+                    id: 'end_st',
+                    xtype: 'statusitem',
+                    text: 'End',