Integrate bpmn-js into xWiki application

Hello! I’m trying to integrate bpmn-js into my xWiki application.

I istalled bpmn-js via bower into xWiki resource folder and then i created a page with this content:

{{velocity}}
$xwiki.jsfx.use('js/bower_components/jquery/dist/jquery.js')
$xwiki.jsfx.use('js/bower_components/jquery-mousewheel/jquery.mousewheel.js')
$xwiki.jsfx.use('js/bower_components/lodash/dist/lodash.js')
$xwiki.jsfx.use('js/bower_components/sax/lib/sax.js')
$xwiki.jsfx.use('js/bower_components/Snap.svg/dist/snap.svg.js')
$xwiki.jsfx.use('js/bower_components/bpmn-js/dist/bpmn-viewer.js')
$xwiki.jsfx.use('js/b2w/test.js')
{{/velocity}}

{{html}}
<h1>Pizza Collaboration Viewer</h1>
<button onclick="test()">Try it</button>
<div id="canvas" style="height: 500px; width: 800px"></div>
{{/html}}

test,js contains the script present in the bundle that embeds the viewer in a page:

function test() {
/**
 * bpmn-js-seed
 *
 * This is an example script that loads an embedded diagram file <diagramXML>
 * and opens it using the bpmn-js viewer.
 */
(function(BpmnViewer, $) {

  // create viewer
  var bpmnViewer = new BpmnViewer({
    container: '#canvas'
  });


  // import function
  function importXML(xml) {

    // import diagram
    bpmnViewer.importXML(xml, function(err) {

      if (err) {
        return console.error('could not import BPMN 2.0 diagram', err);
      }

      var canvas = bpmnViewer.get('canvas'),
          overlays = bpmnViewer.get('overlays');


      // zoom to fit full viewport
      canvas.zoom('fit-viewport');

      // attach an overlay to a node
      overlays.add('SCAN_OK', 'note', {
        position: {
          bottom: 0,
          right: 0
        },
        html: '<div class="diagram-note">Mixed up the labels?</div>'
      });

      // add marker
      canvas.addMarker('SCAN_OK', 'needs-discussion');
    });
  }


  // a diagram to display
  //
  // see index-async.js on how to load the diagram asynchronously from a url.
  // (requires a running webserver)
  var diagramXML = "<?xml version=\"1.0\" ..........";

  // import xml
  importXML(diagramXML);

})(window.BpmnJS, window.jQuery);
}

when I click the button i get this error in javascript console:

 Uncaught TypeError: undefined is not a function

at this line of the test.js script:

  var bpmnViewer = new BpmnViewer({
    container: '#canvas'
  });

How can I fix this problem??

Where does this problem occur? Try to track it down using the Chrome / Firefox / IE developer tools (F12) in your browser. They usually show nicely formatted stack traces on what is happening.

When the page loads (and the javascript libs are imported) I can see these errors:

TypeError: Z.attachEvent is not a function prototype.js:15
Error: Mismatched anonymous define() module: function ($) {

    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
        slice  = Array.prototype.slice,
        nullLowestDeltaTimeout, lowestDelta;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
        }
    }

    var special = $.event.special.mousewheel = {
        version: '3.1.12',

        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }
            // Store the line height and page height for this particular element
            $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
            $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }
            // Clean up the data we added to the element
            $.removeData(this, 'mousewheel-line-height');
            $.removeData(this, 'mousewheel-page-height');
        },

        getLineHeight: function(elem) {
            var $elem = $(elem),
                $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
            if (!$parent.length) {
                $parent = $('body');
            }
            return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
        },

        getPageHeight: function(elem) {
            return $(elem).height();
        },

        settings: {
            adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
            normalizeOffset: true  // calls getBoundingClientRect for each event
        }
    };

    $.fn.extend({
        mousewheel: function(fn) {
            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
        },

        unmousewheel: function(fn) {
            return this.unbind('mousewheel', fn);
        }
    });


    function handler(event) {
        var orgEvent   = event || window.event,
            args       = slice.call(arguments, 1),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0,
            offsetX    = 0,
            offsetY    = 0;
        event = $.event.fix(orgEvent);
        event.type = 'mousewheel';

        // Old school scrollwheel delta
        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaX = deltaY * -1;
            deltaY = 0;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( 'deltaY' in orgEvent ) {
            deltaY = orgEvent.deltaY * -1;
            delta  = deltaY;
        }
        if ( 'deltaX' in orgEvent ) {
            deltaX = orgEvent.deltaX;
            if ( deltaY === 0 ) { delta  = deltaX * -1; }
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) { return; }

        // Need to convert lines and pages to pixels if we aren't already in pixels
        // There are three delta modes:
        //   * deltaMode 0 is by pixels, nothing to do
        //   * deltaMode 1 is by lines
        //   * deltaMode 2 is by pages
        if ( orgEvent.deltaMode === 1 ) {
            var lineHeight = $.data(this, 'mousewheel-line-height');
            delta  *= lineHeight;
            deltaY *= lineHeight;
            deltaX *= lineHeight;
        } else if ( orgEvent.deltaMode === 2 ) {
            var pageHeight = $.data(this, 'mousewheel-page-height');
            delta  *= pageHeight;
            deltaY *= pageHeight;
            deltaX *= pageHeight;
        }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );

        if ( !lowestDelta || absDelta < lowestDelta ) {
            lowestDelta = absDelta;

            // Adjust older deltas if necessary
            if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
                lowestDelta /= 40;
            }
        }

        // Adjust older deltas if necessary
        if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
            // Divide all the things by 40!
            delta  /= 40;
            deltaX /= 40;
            deltaY /= 40;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);

        // Normalise offsetX and offsetY properties
        if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
            var boundingRect = this.getBoundingClientRect();
            offsetX = event.clientX - boundingRect.left;
            offsetY = event.clientY - boundingRect.top;
        }

        // Add information to the event object
        event.deltaX = deltaX;
        event.deltaY = deltaY;
        event.deltaFactor = lowestDelta;
        event.offsetX = offsetX;
        event.offsetY = offsetY;
        // Go ahead and set deltaMode to 0 since we converted to pixels
        // Although this is a little odd since we overwrite the deltaX/Y
        // properties with normalized deltas.
        event.deltaMode = 0;

        // Add event and delta to the front of the arguments
        args.unshift(event, delta, deltaX, deltaY);

        // Clearout lowestDelta after sometime to better
        // handle multiple device types that give different
        // a different lowestDelta
        // Ex: trackpad = 3 and mouse wheel = 120
        if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
        nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);

        return ($.event.dispatch || $.event.handle).apply(this, args);
    }

    function nullLowestDelta() {
        lowestDelta = null;
    }

    function shouldAdjustOldDeltas(orgEvent, absDelta) {
        // If this is an older event and the delta is divisable by 120,
        // then we are assuming that the browser is treating this as an
        // older mouse wheel event and that we should divide the deltas
        // by 40 to try and get a more usable deltaFactor.
        // Side note, this actually impacts the reported scroll distance
        // in older browsers and can cause scrolling to be slower than native.
        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
        return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
    }

}
http://requirejs.org/docs/errors.html#mismatch path:8
Error: Mismatched anonymous define() module: function () {
      return _;
    }
http://requirejs.org/docs/errors.html#mismatch path:8
Error: Mismatched anonymous define() module: function ( eve ) {
            return factory(glob, eve);
        }
http://requirejs.org/docs/errors.html#mismatch path:8
Error: Load timeout for modules: bootstrap
http://requirejs.org/docs/errors.html#timeout path:8

From your stack trace it looks like xWiki is using the ancient prototype.js library. It has known compatibility issues with jQuery. As a reference see this thread on stackoverflow.

The way we currently integrate jQuery makes it impossible for you to workaround this issue.

There is good news though: We got rid of jQuery in the soon to be release next version of our library. You should be fine integrating that one in your application.

Thanks for the reply nikku! Fantastic news! I know asking for eta is bad but when do you think this version will be released? Can I have a beta version to use in my app? I have a deadline next month for this project…

We will ship the next in the next two to three weeks. You can track our progress here. We do not have any beta-builds of our bundled viewer/modeler distribution

Hi carlez,

Did you get it running? I am trying the same thing. The error I get is:

ReferenceError: test is not defined

Do you have any idea what am I missing out? Where should I actually save test.js? As an attachment to the Wiki-Page?

Regards, Helge

Hi,

Tried again and figured out a little more. I do now get an empty canvas, displaying the bpmn.io logo only. Kind of a progress, however the diagram is not loading.

"could not import BPMN 2.0 diagram" TypeError: definitions is undefined

I guess it is about this line in the test.js script:

var diagramXML = "<?xml version=\"1.0\" ..........";

which should somehow reference the diagram - but how? I am kind of new to this, so apologies for weird questions. Any hint would be appreciated.

Thanks, Helge

It was quite a long time since people were unable to do this.

Has anyone successfuly integrated bpmn.js into XWiki by now? I am really interested in doing it for my XWiki.