
require(['_AMD'], function(_AMD) {
  _AMD.enableDefine();
  _AMD.enableDeferredLoading();
});
define('spyl/pb/Comment', ['jls/lang/Class'], function (Class) {

var Comment;
Comment = Class.create({
    initialize : function(id, userId, mediaId, content, date) {
        this._id = id;
        this._userId = userId;
        this._mediaId = mediaId;
        this._date = date;
        this._content = content;
    },
    getId : function() {
        return this._id;
    },
    getDate : function() {
        return this._date;
    },
    getUserId : function() {
        return this._userId;
    },
    getMediaId : function() {
        return this._mediaId;
    },
    getContent : function() {
        return this._content;
    }
});

Object.extend(Comment,
{
    createFromXML : function(node) {
        var id = parseInt(node.getAttribute('id'), 10);
        var mediaId = parseInt(node.getAttribute('mediaId'), 10);
        var userId = parseInt(node.getAttribute('userId'), 10);
        var content = node.getAttribute('content');
        var date = new Date(parseInt(node.getAttribute('date'), 10) * 1000);
        return new Comment(id, userId, mediaId, content, date);
    }
});


return Comment;
});
define('spyl/pb/Context', ['jls/lang/Class'], function (Class) {

var Context;
Context = Class.create({
    initialize: function(viewName, group, mediaIndex) {
        this.viewName = viewName;
        this.group = group;
        this.mediaIndex = mediaIndex;
    },
    createView: function(viewName) {
        return new Context(viewName, this.group, this.mediaIndex);
    },
    createMedia: function(delta) {
        var max = this.group.getMediaCount();
        var index = Math.abs(this.mediaIndex + delta + max) % max;
        return new Context('media', this.group, index);
    },
    getMedia: function() {
        return this.group.getMedia(this.mediaIndex);
    }
});


return Context;
});
define('spyl/pb/Group', ['jls/lang/Class', 'jls/lang/Exception'], function (Class, Exception) {

var Group;
Group = Class.create({
    initialize: function(name) {
        this._name = name;
        this._parent = null;
        this._items = [];
    },
    getName: function() {
        return this._name;
    },
    getParent: function() {
        return this._parent;
    },
    setParent: function(parent) {
        this._parent = parent;
    },
    addItem: function(item) {
        this._items.push(item);
    },
    getItem: function(index) {
        if ((typeof index != 'number') || (index < 0) || (index >= this._items.length)) {
            throw new Exception('Index out of bounds (' + index + ')');
        }
        return this._items[index];
    },
    getItemByName: function(name) {
        for (var i = 0; i < this.size(); i++) {
            var item = this.getItem(i);
            if (item.getName() == name) {
                return item;
            }
        }
    },
    size: function() {
        return this._items.length;
    },
    sort: function(fn) {
        return this._items.sort(fn);
    }
});

// A view contains groups that have there proper set with possible duplicates.
Group.View = Class.create(Group, {
    addItem: function($super, item) {
        $super(item);
        item.setParent(this);
    }
});

// Contains medias.
Group.Medias = Class.create(Group, {
    getPreviewMedia: function() {
        return this.getItem(0);
        //return this.getItem(Math.floor(Math.random() * this.size()));
    },
    getMedia: function(index) {
        return this.getItem(index);
    },
    getMediaCount: function() {
        return this.size();
    }
});
Group.RevertedMedias = Class.create(Group.Medias, {
    getPreviewMedia: function() {
        return this.getItem(this.size() - 1);
    },
    getMedia: function(index) {
        return this.getItem(this.size() - 1 - index);
    },
    getMediaCount: function() {
        return this.size();
    }
});

// A list contains groups that are subsets of the set of the list.
Group.List = Class.create(Group.View, {
    getPreviewMedia: function() {
        return this.getItem(0).getPreviewMedia();
        //return this.getMedia(Math.floor(Math.random() * this.getMediaCount()));
    },
    getMedia: function(index) {
        var subIndex = index;
        for (var i = 0; i < this.size(); i++) {
            var group = this.getItem(i);
            var mediaCount = group.getMediaCount();
            if (subIndex < mediaCount) {
                return group.getMedia(subIndex);
            }
            subIndex -= mediaCount;
        }
        return null;
    },
    getMediaCount: function() {
        var count = 0;
        for (var i = 0; i < this.size(); i++) {
            count += this.getItem(i).getMediaCount();
        }
        return count;
    }
});


return Group;
});
define('spyl/pb/GroupPreview', ['jls/lang/Class', 'jls/html/HtmlElement'], function (Class, HtmlElement) {

var GroupPreview;
GroupPreview = Class.create(HtmlElement,
{
    initialize : function($super, parameters, parent) {
        this._content = null;
        this._label = null;
        $super(parameters, parent);
        if ('group' in parameters) {
        	this.setGroup(parameters.group);
        }
    },
    onCreate : function($super) {
        this.setAttribute('htmlTagName', 'table');
        $super();
        var tbody = document.createElement('tbody');
        var contentRow = document.createElement('tr');
        this._content = document.createElement('td');
        this._content.className = 'preview';
        var titleRow = document.createElement('tr');
        var titleCell = document.createElement('td');
        titleCell.className = 'titled_preview';
        this._label = document.createElement('label');
        this.getHtmlElement().appendChild(tbody);
        tbody.appendChild(contentRow);
        contentRow.appendChild(this._content);
        tbody.appendChild(titleRow);
        titleRow.appendChild(titleCell);
        titleCell.appendChild(this._label);
    },
    onAddChild : function(child) {
        this._content.appendChild(child._htmlElement);
    },
    setGroup : function(group) {
        this._group = group;
        var media = this._group.getPreviewMedia();
        media.createMiniature({media: media, attributes: {'class': 'media'}}, this);
        while (this._label.hasChildNodes()) {
            this._label.removeChild(this._label.firstChild);
        }
        this._label.appendChild(document.createTextNode(this._group.getName()));
    },
    getGroup : function() {
        return this._group;
    }
});


return GroupPreview;
});
define('spyl/pb/Icon', ['jls/lang/Class', 'jls/html/HtmlElement'], function (Class, HtmlElement) {

var Icon;
Icon = Class.create(HtmlElement,
{
    initialize : function($super, parameters, parent) {
        this._img = null;
        $super(parameters, parent);
    },
    onCreate : function($super) {
        this.setAttribute('htmlTagName', 'div');
        $super();
        this.setAttribute('class', 'icon');
        this._img = document.createElement('img');
        this.getHtmlElement().appendChild(this._img);
    },
    setIconSrc : function(value) {
        this._img.setAttribute('src', value);
    },
    setIconTitle : function(value) {
        this._img.setAttribute('title', this.getResourceLabel(value));
    }
});


return Icon;
});
define('spyl/pb/Launcher', [
  'jls/lang/Class',
  'jls/lang/Logger',
  'jls/gui/Element',
  'jls/gui/GuiUtilities',
  'jls/html/DomEvent',
  'jls/html/HtmlElement',
  'jls/util/Cookie',
  'jls/util/EventBroker',
  'jls/util/Formatter',
  'jls/util/Location',
  'jls/util/Resource',
  'jls/util/Sxe',
  'jls/util/SxeEnvelope',
  'jls/util/StringCodec',
  'jls/lang/ByteBuffer',
  'jls/util/XMLHttpRequest',
  'jls/security/MessageDigest',
  'spyl/pb/Comment',
  'spyl/pb/Context',
  'spyl/pb/Group',
  'spyl/pb/GroupPreview',
  'spyl/pb/Icon',
  'spyl/pb/Media',
  'spyl/pb/User',
  'spyl/pb/Repository'
], function (
  Class,
  Logger,
  Element,
  GuiUtilities,
  DomEvent,
  HtmlElement,
  Cookie,
  EventBroker,
  Formatter,
  Location,
  Resource,
  Sxe,
  SxeEnvelope,
  StringCodec,
  ByteBuffer,
  XMLHttpRequest,
  MessageDigest,
  Comment,
  Context,
  Group,
  GroupPreview,
  Icon,
  Media,
  User,
  Repository
) {

var Launcher;
/*
 * TODO
 * (Auto-)Refresh the comment board
 * Poll for new comments/medias
 * Start on new medias if any
 * Add tags
 */
Launcher = Class.create({
    initialize: function(theme) {
        this.theme = theme;
        this.broker = new EventBroker();
        this.sxe = new Sxe('script/default.php');
        this.medias = [];
        this.root = null;
        this.parent = GuiUtilities.getRoot();
        this.contextHistory = new Array();
        this.contextHistoryIndex = -1;
        this.resource = null;
        this.adminEMail = null;

        this.users = {};
        this.repositories = {};
        this.user = null;
        this.userId = Cookie.get('userId');
        this.password = Cookie.get('password');
        this.rememberMe = false;
        Logger.getInstance().info('Cookie says: User Id: ' + this.userId);
        
        this.connected = false;
        this.logon = null;
        this.menu = null;
        this.comment = null;

        this.broker.subscribeAll([
              'onOpenMenu',
              'onCloseMenu',
              'onLogoff',
              'onRegister',
              'onDisplayInfo',
              'onDisplayNotification',
              'onAuthenticated',
              'onAccessGranted',
              'onLogon',
              'onMediasLoaded',
              'onConnectOk',
              'onLostPassword',
              'onManageAccount',
              'onManageRepository',
              
              'onOpenGroup',
              'onOpenMedia',
              'onDisplayMedia',
              'onDisplayBoard',
              'onDisplayBoardFull',
              'onDisplayGroup',
              'onGoPrevious',
              'onGoNext',
              'onGoHome',
              'onGoUp',
              'onGoBackward',
              'onGoForward',
              'onComment',
              
              'onDisplayUsers',
              'onFeedUsers',
              'onDebug'
          ], this, this);
    },
    launch: function() {
        Logger.getInstance().trace('launch()');
        this.elements = Resource.getResourceAsJSON('spyl/pb/elements.' + this.theme + '.json');
        this.parent.setAttribute('class', this.theme);
        this.resource = Resource.load('spyl/pb/client');
        this.dateFormat = this.resource.get('date.format');
        this.commentLabel = this.resource.get('comment');
        this.commentsLabel = this.resource.get('comments');
        this.parent.setResource(this.resource);
        this.parent.setEventBroker(this.broker);
        this.createElements();
        HtmlElement.addEventListener(document, 'keydown', (function(e) {
            this.onKey.call(this, new DomEvent(e, this));
        }).bind(this));

        if (this.userId && this.password) {
            this.broker.publish('onLogon');
        }
        // check url
        var hash = Location.getHash();
        if (hash) {
            Location.setHash();
        }
        var query = Location.parseQuery(hash);
        if (query != null) {
            if (query.logLevel) {
                Logger.getInstance().setLogLevel(query.logLevel);
            }
            if (query.operation) {
                this.onQuery(query);
            }
        }
        Logger.getInstance().trace('done');
    },
    onKey: function(event) {
        var keyCode = event.keyCode();
        //Logger.getInstance().debug('onKey(): -->' + keyCode + '<--');
        if ((this.currentContext == null) || this.comment) {
            return;
        }
        switch (keyCode) {
        case 38: // up
            switch (this.currentContext.viewName) {
            case 'media':
                this.broker.publish('onDisplayBoard', event);
                break;
            case 'board':
                this.broker.publish('onDisplayGroup', event);
                break;
            case 'group':
                event.message.preventDefault();
                if (this.currentContext.group != this.root) {
                    this.changeContext(new Context('group', this.root, 0));
                }
                break;
            }
            break;
        case 40: // down
            switch (this.currentContext.viewName) {
            /*case 'media':
                event.preventDefault();
                break;*/
            case 'board':
                this.broker.publish('onDisplayMedia', event);
                break;
            case 'group':
                this.broker.publish('onDisplayBoard', event);
                break;
            }
            break;
        }
        if (this.currentContext.viewName != 'media') {
            return;
        }
        switch (keyCode) {
        case 37: // left
            this.broker.publish('onGoBackward', event);
            break;
        case 39: // right
            this.broker.publish('onGoForward', event);
            break;
        }
    },
    onQuery: function(query) {
        var envelope = new SxeEnvelope();
        envelope.addRequest({name: query.operation, attributes: query});
        this.sxe.send(envelope, (function(response) {
            var envelope = response.getEnvelope();
            //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
            var result = envelope.getResult(0);
            var msg = null;
            if (result.getName() == 'failure') {
                msg = this.resource.getf('operation.failure', query.operation, result.getText());
            } else {
                switch (query.operation) {
                case 'activateUser':
                    if (result.getName() == 'success') {
                        msg = this.resource.get('user.activated');
                    }
                    break;
                }
            }
            this.broker.publish('onDisplayInfo', msg || this.resource.get('error'));
        }).bind(this));
    },
    createElements: function() {
        Logger.getInstance().trace('createElements()');
        this.viewport = Element.create(this.elements.viewport, this.parent);
        this.toolbar = Element.create(this.elements.toolbar, this.parent);
        this.header = Element.create(this.elements.header, this.parent);
        var element = Element.create(this.elements.notification, this.parent);
        this.notification = element.getById('container');
        this.logon = Element.create(this.elements.logon, this.header);
        this.logon.getById('linkMailTo').setAttribute('href', 'mailto:' + this.adminEMail);
        this.options = Element.create(this.elements.options, this.header);
        this.options.getById('linkMailTo').setAttribute('href', 'mailto:' + this.adminEMail);
        this.options.getById('linkHelp').setAttribute('href', 'help.' + _native.core.properties['user.language'] + '.html');
        // fetch elements
        this.groupIcon = this.toolbar.getById('groupIcon');
        this.boardIcon = this.toolbar.getById('boardIcon');
        this.mediaIcon = this.toolbar.getById('mediaIcon');
        this.mediaToolbar = this.toolbar.getById('mediaToolbar');
        this.repositoryToolbar = this.toolbar.getById('repositoryToolbar');
        this.connectLink = this.header.getById('connectLink');
    },
    formatDate: function(date) {
        return Formatter.format(this.dateFormat, date);
    },
    onOpenMenu: function(event) {
        Logger.getInstance().trace('onOpenMenu');
        event.message.preventDefault();
        if (this.menu != null) {
            this.onCloseMenu();
            return;
        }
        if (this.connected) {
            this.menu = this.options;
        } else {
            if (this.userId) {
                this.logon.getById('userId').setText(this.userId);
                this.logon.getById('password').setText('');
            }
            this.menu = this.logon;
        }
        this.menu.getStyle().setProperty('display', 'block');
    },
    onCloseMenu: function(event) {
        if (this.menu != null) {
            this.menu.getStyle().setProperty('display', 'none');
            this.menu = null;
        }
    },
    onLogoff: function(event) {
        event.message.preventDefault();
        Logger.getInstance().trace('logoff');
        this.userId = '';
        this.password = '';
        this.user = null;
        this.rememberMe = false;
        this.connected = false;
        this.connectLink.setTextContent('#logon');
        Cookie.clear('userId');
        Cookie.clear('password');
        this.onCloseMenu();
    },
    onConnectOk: function(event) {
        var userIdEdit = this.logon.getById('userId');
        var passwordEdit = this.logon.getById('password');
        this.userId = userIdEdit.getText();
        this.password = Launcher.sha1(passwordEdit.getText());
        this.rememberMe = true; // TODO use a check box
        this.onCloseMenu();
        this.broker.publish('onLogon');
    },
    onRegister: function(event) {
        Logger.getInstance().trace('onRegister');
        event.message.preventDefault();
        var div = Element.create(this.elements.register, this.parent);
        div.getById('ok').observe('click', (function (e) {
            var userId = div.getById('userId').getText();
            var password = Launcher.sha1(div.getById('password').getText());
            var email = div.getById('email').getText();
            var envelope = new SxeEnvelope();
            envelope.addRequest({name: 'createUser', attributes: {login: userId, password: password, email: email}});
            this.sxe.send(envelope, (function(response) {
                var envelope = response.getEnvelope();
                //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
                var result = envelope.getResult(0);
                if (result.getName() == 'success') {
                    Logger.getInstance().trace('User created');
                    div.destroy();
                    this.broker.publish('onDisplayNotification', this.resource.get('user.create.success'));
                } else {
                    var msg = this.resource.get('user.create.failure');
                    if (result.getName() == 'failure') {
                        msg = this.resource.getf('error.message', result.getText());
                    }
                    this.broker.publish('onDisplayInfo', msg);
                }
            }).bind(this));
        }).bind(this));
        div.getById('cancel').observe('click', function (e) {
            div.destroy();
        });
    },
    onLostPassword: function(event) {
        Logger.getInstance().trace('onLostPassword()');
        event.message.preventDefault();
        var div = Element.create(this.elements.lostPassword, this.parent);
        div.getById('ok').observe('click', (function (e) {
            var password = Launcher.sha1(div.getById('password').getText());
            var email = div.getById('email').getText();
            var envelope = new SxeEnvelope();
            envelope.addRequest({name: 'lostUserPassword', attributes: {email: email, password: password}});
            this.sxe.send(envelope, (function(response) {
                var envelope = response.getEnvelope();
                //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
                var result = envelope.getResult(0);
                if (result.getName() == 'success') {
                    Logger.getInstance().trace('lostUserPassword succeed');
                    div.destroy();
                    this.broker.publish('onDisplayNotification', this.resource.get('user.lostPassword.success'));
                } else {
                    var msg = this.resource.get('error');
                    if (result.getName() == 'failure') {
                        msg = this.resource.getf('error.message', result.getText());
                    }
                    this.broker.publish('onDisplayInfo', msg);
                }
            }).bind(this));
        }).bind(this));
        div.getById('cancel').observe('click', function (e) {
            div.destroy();
        });
    },
    onDebug: function(event) {
        Logger.getInstance().trace('onDebug()');
        if (event.message instanceof DomEvent) {
            event.message.preventDefault();
            this.onCloseMenu();
        }
        if (Logger.getInstance().getLogLevel() == Logger.TRACE) {
            Logger.getInstance().setLogLevel(Logger.INFO);
        } else {
            Logger.getInstance().setLogLevel(Logger.TRACE);
        }
        this.broker.publish('onDisplayNotification', 'Log level set to ' + Logger.getInstance().getLogLevel());
    },
    onManageRepository: function(event) {
        Logger.getInstance().trace('onManageRepository()');
        if (event.message instanceof DomEvent) {
            event.message.preventDefault();
            this.onCloseMenu();
        }
        var broker = new EventBroker();
        var userRepositories = this.repositories;
        var allRepositories, allRepositoryMap;
        var launcher = this;
        var div = Element.create(this.elements.repositoryGrid, this.parent);
        div.setEventBroker(broker);
        var dataGrid = div.getById('dataGrid');
        var dgModel = dataGrid.getModel();
        dgModel.isCellEditable = function(row, column) {
            var rowData = this.getRow(row);
            if (column < 4) {
                return rowData.userId == launcher.userId;
            }
            if (column > 4) {
                return rowData.id in launcher.repositories;
            }
            return true;
        };
        var waitRepository = new Repository(0, '...', -1, '...');
        waitRepository.isSubscribed = false;
        dgModel.addRow(waitRepository);
        
        if ('repository' in this.user.rights) {
            // enable create/delete buttons
        }
        /*
         * Fetch repositories
         */
        var envelope = new SxeEnvelope(launcher.userId, launcher.password);
        envelope.addRequest({name: 'listRepository', attributes: {select: 'all'}});
        launcher.sxe.send(envelope, function(response) {
            var envelope = response.getEnvelope();
            var result = envelope.getResult(0);
            if (result.getName() == 'repositoryListResult') {
                dgModel.removeAll();
                allRepositories = [];
                for (var i = 0; i < result.getChildCount(); i++) {
                    var repository = Repository.createFromXML(result.getChild(i));
                    allRepositories.push(repository);
                    repository = repository.clone();
                    if (repository.id in launcher.repositories) {
                        var ur = launcher.repositories[repository.id];
                        repository.isSubscribed = true;
                        repository.hasCommentRight = Launcher.hasRight(ur.rights, 'comment');
                        repository.hasWriteRight = Launcher.hasRight(ur.rights, 'comment');
                    } else {
                        repository.isSubscribed = false;
                        repository.hasCommentRight = false;
                        repository.hasWriteRight = false;
                    }
                    dgModel.addRow(repository);
                }
                allRepositoryMap = {};
                for (var i = allRepositories.length - 1; i >= 0; i--) {
                    var repository = allRepositories[i];
                    allRepositoryMap[repository.id] = repository;
                }
            } else {
                div.destroy();
                var msg = launcher.resource.get('error');
                if (result.getName() == 'failure') {
                    msg = launcher.resource.getf('error.message', result.getText());
                }
                launcher.broker.publish('onDisplayInfo', msg);
            }
        });
        broker.subscribe('create', function (event) {
            dgModel.addRow(new Repository(-1, 'Add a title', launcher.userId, '', true, false));
        });
        broker.subscribe('delete', function (event) {
            var selectedRow = dataGrid.getSelectedRow();
            Logger.getInstance().trace('delete, selectedRow: ' + selectedRow);
            if ((selectedRow >= 0) && (selectedRow < dgModel.getRowCount())) {
                dgModel.removeRow(selectedRow);
            }
        });
        broker.subscribe('apply', function (event) {
            var envelope = new SxeEnvelope(launcher.userId, launcher.password);
            var repMap = {};
            for (var i = dgModel.getRowCount() - 1; i >= 0; i--) {
                var row = dgModel.getRow(i);
                if (row.id == -1) {
                    envelope.addRequest({name: 'createRepository', attributes: {title: row.title, description: row.description,
                        requiresUserAccess: row.requiresUserAccess, isPublic: row.isPublic}});
                } else if (row.id in allRepositoryMap) {
                    var repository = allRepositoryMap[row.id];
                    if (! row.equals(repository)) {
                        envelope.addRequest({name: 'modifyRepository', attributes: {id: row.id, title: row.title, description: row.description,
                            requiresUserAccess: row.requiresUserAccess, isPublic: row.isPublic}});
                    }
                    if (row.isSubscribed != (repository.id in launcher.repositories)) {
                        if (row.isSubscribed) {
                            envelope.addRequest({name: 'addRepositoryUser', attributes: {repositoryId: row.id, userId: launcher.user.id}});
                        } else {
                            envelope.addRequest({name: 'removeRepositoryUser', attributes: {repositoryId: row.id, userId: launcher.user.id}});
                        }
                    }
                } else {
                    
                }
                repMap[row.id] = row;
            }
            for (var i = allRepositories.length - 1; i >= 0; i--) {
                var repository = allRepositories[i];
                if (! (repository.id in repMap)) {
                    envelope.addRequest({name: 'deleteRepository', attributes: {id: repository.id}});
                }
            }
            if (envelope.getRequestCount() == 0) {
                return;
            }
            Logger.getInstance().warn('envelope [' + envelope.getRequestCount() + ']: ' + envelope.toString());
            launcher.sxe.send(envelope, function(response) {
                div.destroy();
                var resEnv = response.getEnvelope();
                Logger.getInstance().warn('response envelope [' + resEnv.getResultCount() + ']: ' + resEnv.toString());
                for (var i = resEnv.getResultCount() - 1; i >= 0; i--) {
                    var request = envelope.getRequest(i);
                    var result = resEnv.getResult(i);
                    if (result.getName() == 'failure') {
                        var msg = launcher.resource.getf('error.message', result.getText());
                        launcher.broker.publish('onDisplayInfo', msg);
                        break;
                    }
                    var repositoryId;
                    var msg = null;
                    switch (request.getName()) {
                    case 'createRepository':
                    case 'modifyRepository':
                        var newRepository = Repository.createFromXML(result.getChild(0));
                        launcher.repositories[newRepository.id] = newRepository;
                        msg = launcher.resource.getf(request.getName() == 'createRepository' ? 'repository.created' : 'repository.modified', newRepository.title);
                        break;
                    case 'deleteRepository':
                        repositoryId = request.getAttribute('id');
                        msg = launcher.resource.getf('repository.deleted', allRepositoryMap[repositoryId].title);
                        delete launcher.repositories[repositoryId];
                        break;
                    case 'addRepositoryUser':
                        repositoryId = request.getAttribute('repositoryId');
                        launcher.repositories[repositoryId] = allRepositoryMap[repositoryId];
                        msg = launcher.resource.getf('repository.subscribed', allRepositoryMap[repositoryId].title);
                        break;
                    case 'removeRepositoryUser':
                        repositoryId = request.getAttribute('repositoryId');
                        delete launcher.repositories[repositoryId];
                        msg = launcher.resource.getf('repository.unsubscribed', allRepositoryMap[repositoryId].title);
                        break;
                    }
                    if (msg != null) {
                        launcher.broker.publish('onDisplayNotification', msg);
                    }
                }
                //launcher.broker.publish('onDisplayNotification', launcher.resource.get('repository.done'));
            });
        });
        broker.subscribe('close', function () {
            div.destroy();
        });
    },
    onManageAccount: function(event) {
        Logger.getInstance().trace('onManageAccount()');
        event.message.preventDefault();
        this.onCloseMenu();
        var user = this.user;
        var div = Element.create(this.elements.manageAccount, this.parent);
        div.getById('login').setAttribute('text', user.login);
        div.getById('email').setAttribute('text', user.email);
        
        var cbAlert = div.getById('cbAlert');
        var alertMat = [0, 1, 16];
        cbAlert.addString(this.resource.get('user.alert.none'));
        var currentIndex = user.alertThreshold;
        for (var i = 1; i < alertMat.length; i++) {
            cbAlert.addString(this.resource.getf(i > 1 ? 'user.alert.counts' : 'user.alert.count', alertMat[i]));
            if (user.alertThreshold == alertMat[i]) {
                currentIndex = i;
            }
        }
        cbAlert.setCurrentSelectionIndex(currentIndex);
        div.getById('modify').observe('click', (function (e) {
            var login = div.getById('login').getText();
            var oldPassword = div.getById('oldPassword').getText();
            if (oldPassword) {
                oldPassword = Launcher.sha1(oldPassword);
            }
            var newPassword = div.getById('newPassword').getText();
            if (newPassword) {
                newPassword = Launcher.sha1(newPassword);
            }
            var email = div.getById('email').getText();
            var alertThreshold = cbAlert.getCurrentSelectionIndex();
            alertThreshold = alertMat[alertThreshold];
            var modifyUserAttr = {};
            if ((login) && (login != user.login)) {
                modifyUserAttr.login = login;
            }
            if ((oldPassword) && (newPassword) && (newPassword != user.password)) {
                modifyUserAttr.password = newPassword;
            }
            if ((email) && (email != user.email)) {
                modifyUserAttr.email = email;
            }
            if (alertThreshold != user.alertThreshold) {
                modifyUserAttr.alertThreshold = alertThreshold;
            }
            var envelope = new SxeEnvelope(this.userId, oldPassword || this.password);
            envelope.addRequest({name: 'modifyUser', attributes: modifyUserAttr});
            //Logger.getInstance().trace('request: -->' + envelope.toString() + '<--');
            this.sxe.send(envelope, (function(response) {
                var envelope = response.getEnvelope();
                //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
                var result = envelope.getResult(0);
                if (result.getName() == 'ModifyUserResult') {
                    // update user
                    this.user = User.createFromXML(result.getChild(0));
                    Logger.getInstance().trace('User modified');
                    div.destroy();
                    this.broker.publish('onDisplayNotification', this.resource.get('user.modify.success'));
                } else {
                    var msg = this.resource.get('error');
                    if (result.getName() == 'failure') {
                        msg = this.resource.getf('error.message', result.getText());
                    }
                    this.broker.publish('onDisplayInfo', msg);
                }
            }).bind(this));
        }).bind(this));
        div.getById('cancel').observe('click', function (e) {
            div.destroy();
        });
    },
    displayInfo: function(element) {
        Logger.getInstance().trace('displayInfo()');
        var div = Element.create(this.elements.info, this.parent);
        var container = div.getById('container');
        container.addChild(element);
        div.getById('ok').observe('click', function () {
            div.destroy();
        });
    },
    onDisplayInfo: function(event) {
        Logger.getInstance().trace('onDisplayInfo');
        this.displayInfo({classname: 'jls/gui/Label', attributes: {text: event.message}});
    },
    displayNotification: function(element, seconds) {
        Logger.getInstance().trace('displayNotification()');
        var div = new HtmlElement({attributes: {htmlTagName: 'div'}, children: [element]}, this.notification);
        // TODO Manage setTimeout() in jls
        window.setTimeout(function () {
            div.destroy();
        }, seconds || 3000);
        return div;
    },
    onDisplayNotification: function(event) {
        Logger.getInstance().trace('onDisplayNotification');
        this.displayNotification({classname: 'jls/gui/Label', attributes: {text: event.message}});
    },
    onFeedUsers: function(event) {
        Logger.getInstance().trace('onFeedUsers');
        event.message.preventDefault();
        var div = Element.create(this.elements.feedConfirm, this.parent);
        div.getById('ok').observe('click', (function () {
            div.destroy();
            this.onFeedUsersConfirmed();
        }).bind(this));
        div.getById('cancel').observe('click', function () {
            div.destroy();
        });
    },
    onFeedUsersConfirmed: function() {
        Logger.getInstance().trace('onFeedUsersConfirmed');
        var envelope = new SxeEnvelope(this.userId, this.password);
        envelope.addRequest({name: 'feedUser'});
        this.sxe.send(envelope, (function(response) {
            var envelope = response.getEnvelope();
            //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
            var result = envelope.getResult(0);
            if (result.getName() == 'userFeedResult') {
                var emailCount = parseInt(result.getAttribute('emailCount'), 10);
                var failureCount = parseInt(result.getAttribute('failureCount'), 10);
                var msg = this.resource.getf('user.feed.result', emailCount, failureCount);
                this.broker.publish('onDisplayNotification', msg);
            } else {
                var msg = this.resource.get('error');
                if (result.getName() == 'failure') {
                    msg = this.resource.getf('error.message', result.getText());
                }
                this.broker.publish('onDisplayInfo', msg);
            }
        }).bind(this));
    },
    onDisplayUsers: function(event) {
        event.message.preventDefault();
        this.onCloseMenu();
        var users = [];
        for (var id in this.users) {
            users.push(this.users[id]);
        }
        users.sort(function(a, b) {
                if (a.lastAuthenticationDate < b.lastAuthenticationDate)
                    return 1;
                if (a.lastAuthenticationDate > b.lastAuthenticationDate)
                    return -1;
                return 0;
            });
        Logger.getInstance().trace('onDisplayUsers ' + users.length + ' user(s)');
        // TODO elements
        var element = {classname: 'jls/html/Table', attributes: {fixedSize: 5, cellStyle: {padding: '5px', border: '1px dotted black'}}, style: {borderCollapse: 'collapse', display: 'inline-table'}, children: [
            {classname: 'jls/gui/Label', attributes: {text: '#user.id'}},
            {classname: 'jls/gui/Label', attributes: {text: '#user.name'}},
            {classname: 'jls/gui/Label', attributes: {text: '#user.email'}},
            {classname: 'jls/gui/Label', attributes: {text: '#user.creationDate'}},
            {classname: 'jls/gui/Label', attributes: {text: '#user.lastLogonDate'}}
        ]};
        for (var i = 0; i < users.length; i++) {
            var user = users[i];
            element.children.push({classname: 'jls/gui/Label', attributes: {text: user.id}});
            element.children.push({classname: 'jls/gui/Label', attributes: {text: user.login}});
            element.children.push({classname: 'jls/gui/Label', attributes: {text: user.email}});
            element.children.push({classname: 'jls/gui/Label', attributes: {text: this.formatDate(user.creationDate)}});
            element.children.push({classname: 'jls/gui/Label', attributes: {text: this.formatDate(user.lastAuthenticationDate)}});
        }
        this.displayInfo(element);
    },
    onLogon: function() {
        Logger.getInstance().trace('onLogon');
        var envelope = new SxeEnvelope();
        envelope.setAuthentication(this.userId, this.password);
        envelope.addRequest({name: 'authenticateUser'});
        envelope.addRequest({name: 'listUsers'});
        envelope.addRequest({name: 'listRepository', attributes: {select: 'subscribed'}});
        this.sxe.send(envelope, this.broker.callback('onAuthenticated'));
    },
    onAuthenticated: function(event) {
        Logger.getInstance().trace('onAuthenticated');
        var envelope = event.message.getEnvelope();
        //Logger.getInstance().debug('envelope: -->' + envelope.toString() + '<--');
        var result = envelope.getResult(0);
        if (result.getName() != 'AuthenticateUserResult') {
            Logger.getInstance().trace('Authentication fails');
            var msg = this.resource.get('logon.failure');
            if (result.getName() == 'failure') {
                msg = this.resource.getf('error.message', result.getText());
            }
            this.broker.publish('onDisplayInfo', msg);
            return;
        }
        Logger.getInstance().trace('AuthenticateUser succeed');
        if (this.rememberMe) {
            Logger.getInstance().trace('Place credentials in cookie');
            var nextYear = new Date();
            nextYear.setFullYear(nextYear.getFullYear() + 1);
            Cookie.set('userId', this.userId, {expires: nextYear});
            Cookie.set('password', this.password, {expires: nextYear});
        }
        this.connected = true;
        this.connectLink.setTextContent(this.userId);
        Logger.getInstance().trace('connected');

        this.users = {};
        var userListResult = envelope.getResult(1);
        //Logger.getInstance().trace('Users: --> ' + userListResult.toString('UTF-8') + '<--');
        if (userListResult.getName() == 'userListResult') {
            for (var i = 0; i < userListResult.getChildCount(); i++) {
                var user = User.createFromXML(userListResult.getChild(i));
                this.users[user.id] = user;
                Logger.getInstance().trace('User id ' + user.id + ' added');
            }
        }
        this.repositories = {};
        this.user = User.createFromXML(result.getChild(0));
        if (! ('read' in this.user.rights)) {
            this.broker.publish('onDisplayInfo', this.resource.get('logon.badRights'));
        }
    	var container = this.viewport.openGroup();
        this.options.getById('adminOptions').getStyle().setProperty('display', 'admin' in this.user.rights ? '' : 'none');
        var repositoryListResult = envelope.getResult(2);
        if (repositoryListResult.getName() == 'repositoryListResult') {
            if (repositoryListResult.getChildCount() > 0) {
                new HtmlElement({attributes: {htmlTagName: 'img', src: 'style/progress.gif'}}, container);
                for (var i = 0; i < repositoryListResult.getChildCount(); i++) {
                    var repository = Repository.createFromXML(repositoryListResult.getChild(i));
                    this.repositories[repository.id] = repository;
                    Logger.getInstance().trace('Repository id ' + repository.id + ' added');
                }
                var launcher = this;
                var accessCallback = function() {
                    if (this.succeed()) {
                        launcher.broker.publish('onAccessGranted');
                    } else {
                        launcher.broker.publish('onDisplayNotification', 'No access for "' + this._url + '" :-(');
                    }
                };
                Logger.getInstance().info('openning repository access');
                new XMLHttpRequest('repository/access.php').setAuthentication(this.userId, this.password).setMethod('post').send(null, accessCallback);
                
            } else {
            	Element.create({classname: 'jls/gui/Label', attributes: {text: '#repository.none'}}, container);
                this.broker.publish('onAccessGranted');
            }
        }
    },
    onAccessGranted: function() {
        Logger.getInstance().trace('onAccessGranted');
        var envelope = new SxeEnvelope();
        envelope.setAuthentication(this.userId, this.password);
        envelope.addRequest({name: 'listMedias'});
        envelope.addRequest({name: 'listCommentedMedias', attributes: {limit: 40}});
        this.sxe.send(envelope, this.broker.callback('onMediasLoaded'));
    },
    computeRootView : function(medias, commentedMedias, lastAuthenticationDate) {
        Logger.getInstance().info('computeRootView() ' + medias.length + ' media(s)');
        var root = new Group.View(this.resource.get('board.home'));
        if (medias.length == 0) {
            return root;
        }
        medias.sort(Media.compareMedias);
        // new medias
        if (lastAuthenticationDate != 0) {
            var mediasByPubDate = medias.concat([]);
            mediasByPubDate.sort(Media.compareMediasByPubDate);
            var recents = new Group.Medias(this.resource.get('board.recent'));
            var last = Math.max(0, mediasByPubDate.length - Launcher.MAX_MEDIA * 4);
            for (var i = mediasByPubDate.length - 1; (i >= last) && (mediasByPubDate[i]._pubDate > lastAuthenticationDate); i--) {
                recents.addItem(mediasByPubDate[i]);
            }
            recents.sort(Media.compareMedias);
            if (recents.size() > 0) {
                root.addItem(recents);
            }
        }
        // by date
        var rootByDate = new Group.List(this.resource.get('board.byDate'));
        var year = -1;
        var yearGroup = null;
        var monthFullNames = Formatter.getResource(_native.core.properties['user.language']).monthFullNames;
        var month = -1;
        var monthGroup = null;
        for (var i = 0; i < medias.length; i++) {
            var media = medias[i];
            var yearMedia = media.getDate().getFullYear();
            if (year != yearMedia) {
                yearGroup = new Group.List(yearMedia);
                year = yearMedia;
                rootByDate.addItem(yearGroup);
                month = -1;
                monthGroup = null;
            }
            var monthMedia = media.getDate().getMonth();
            if (month != monthMedia) {
                // create group
                monthGroup = new Group.Medias(monthFullNames[monthMedia]);
                month = monthMedia;
                yearGroup.addItem(monthGroup);
            }
            monthGroup.addItem(media);
        }
        root.addItem(rootByDate);
        // Commented medias
        var commentMap = {};
        if (commentedMedias) {
            for (var i = 0; i < commentedMedias.length; i++) {
                var cm = commentedMedias[i];
                commentMap[cm.mediaId] = cm.lastDate;
            }
        }
        // Films
        var movies = new Group.Medias(this.resource.get('board.movies'));
        var comments = new Group.RevertedMedias(this.resource.get('board.commented'));
        for (var i = 0; i < medias.length; i++) {
            var m = medias[i];
            if (m instanceof Media.Movie) {
                movies.addItem(m);
            }
            if (commentMap[m.getId()]) {
                comments.addItem(m);
            }
        }
        if (movies.size() > 0) {
            root.addItem(movies);
        }
        if (comments.size() > 0) {
            Logger.getInstance().info('sort comments, first: ' + comments.getItem(0).getId() + ', date: ' + commentMap[comments.getItem(0).getId()]);
            comments.sort(function(a, b) {
                var aDate = commentMap[a.getId()];
                var bDate = commentMap[b.getId()];
                if (aDate < bDate)
                    return -1;
                if (aDate > bDate)
                    return 1;
                return 0;
            });
            Logger.getInstance().info('sorted comments, first: ' + comments.getItem(0).getId() + ', date: ' + commentMap[comments.getItem(0).getId()]);
            root.addItem(comments);
        }
        // Random
        var rand = new Group.Medias(this.resource.get('board.random'));
        for (var i = 0; i < Launcher.MAX_MEDIA; i++) {
            var m = medias[Math.floor(Math.random() * medias.length)];
            rand.addItem(m);
        }
        root.addItem(rand);
        return root;
    },
    onMediasLoaded: function(event) {
        Logger.getInstance().trace('onMediasLoaded()');
        var envelope = event.message.getEnvelope();
        var result = envelope.getResult(0);
        if (result.getName() != 'mediaListResult') {
            return;
        }
        var mediaNodes = result.getChildren();
        this.medias = [];
        for (var i = 0; i < mediaNodes.length; i++) {
            this.medias.push(Media.createFromXML(mediaNodes[i]));
        }
        result = envelope.getResult(1);
        if (result.getName() != 'commentListResult') {
            return;
        }
        var commentedMedias = [];
        var commentNodes = result.getChildren();
        for (var i = 0; i < commentNodes.length; i++) {
            var node = commentNodes[i];
            var mediaId = parseInt(node.getAttribute('mediaId'), 10);
            var lastDate = new Date(parseInt(node.getAttribute('lastDate'), 10) * 1000);
            commentedMedias.push({
                mediaId: mediaId,
                lastDate: lastDate
            });
        }
        this.root = this.computeRootView(this.medias, commentedMedias, this.user.lastAuthenticationDate);
        this.changeContext(new Context('group', this.root, 0));
        this.toolbar.getStyle().setProperty('display', '');
    },
    onDisplayMedia: function(event) {
        event.message.preventDefault();
        this.changeContext(this.currentContext.createView('media'));
    },
    onDisplayBoard: function(event) {
        event.message.preventDefault();
        this.changeContext(this.currentContext.createView('board'));
    },
    onDisplayBoardFull: function(event) {
        event.message.preventDefault();
        // TODO Add a sticky tag full on the group and just display the board
        this.changeContext(this.currentContext.createView('boardFull'));
    },
    onDisplayGroup: function(event) {
        event.message.preventDefault();
        var group = this.currentContext.group;
        if (group instanceof Group.Medias) {
            group = group.getParent();
        }
        this.changeContext(new Context('group', group, 0));
    },
    displayMedia: function(context) {
        Logger.getInstance().trace('displayMedia()');
        var media = context.getMedia();
        this.mediaToolbar.getStyle().setProperty('display', '');
        this.repositoryToolbar.getStyle().setProperty('display', 'none');
        var mediaContainer = this.viewport.openMedia();
        media.createElement({group: context.group, index: context.mediaIndex, attributes: {'class': 'media'}}, mediaContainer);
        if (media.getCommentCount() == 0) {
            return;
        }
        if (media.getCommentCount() != media.getComments().length) {
        	var commentContainer = this.viewport.openComment();
            new HtmlElement({attributes: {htmlTagName: 'img', src: 'style/progress.gif'}}, commentContainer);
            var envelope = new SxeEnvelope(this.userId, this.password);
            envelope.addRequest({name: 'listComment', attributes: {mediaId: media.getId()}});
            this.sxe.send(envelope, (function(response) {
                var envelope = response.getEnvelope();
                //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
                var result = envelope.getResult(0);
                if (result.getName() == 'commentListResult') {
                	media.clearComments();
                    for (var i = 0; i < result.getChildCount(); i++) {
                        var comment = Comment.createFromXML(result.getChild(i));
                        media.addComment(comment);
                        Logger.getInstance().trace('Comment id ' + comment.getId() + ' added');
                    }
                }
                this.displayMediaComments(context);
            }).bind(this));
        } else {
            this.displayMediaComments(context);
        }
    },
    displayMediaComments: function(context) {
    	var container = this.viewport.openComment();
        var media = context.getMedia();
        for (var i = 0; i < media.getComments().length; i++) {
            var comment = media.getComments()[i];
            var commentElements = [
                {classname: 'jls/html/HtmlElement', attributes: {htmlTagName: 'div', 'class': 'commentHeader',
                    textContent: this.formatDate(comment.getDate()) + ', ' + this.users[comment.getUserId()].login}}
            ];
            var lines = comment.getContent().split('\n');
            for (var j = 0; j < lines.length; j++) {
                commentElements.push({classname: 'jls/html/HtmlElement', attributes: {htmlTagName: 'div', textContent: lines[j]}});
            }
            new HtmlElement({attributes: {htmlTagName: 'div', 'class': 'comment'}, children: commentElements}, container);
        }
    },
    createBreadcrumb: function(group, parent) {
        var groups = [];
        while (group) {
            groups.push(group);
            group = group.getParent();
        }
        if (groups.length == 1) {
            return;
        }
        var p = new HtmlElement({attributes: {htmlTagName: 'p'}, style: {textAlign: 'left'}}, parent);
        for (var i = groups.length - 1; i >= 0; i--) {
            if (i > 0) {
                var e = new HtmlElement({attributes: {htmlTagName: 'a', href: '#', textContent: groups[i].getName()}, events: {click: 'onOpenGroup'}}, p);
                e._group = groups[i];
                e.getGroup = GroupPreview.prototype.getGroup;
                new HtmlElement({attributes: {htmlTagName: 'span', htmlContent: '&nbsp;&raquo;&nbsp;'}}, p);
            } else {
                new HtmlElement({attributes: {htmlTagName: 'span', textContent: groups[i].getName()}}, p);
            }
        }
    },
    displayBoard: function(context, full) {
        Logger.getInstance().trace('displayBoard()');
        this.mediaToolbar.getStyle().setProperty('display', 'none');
        this.repositoryToolbar.getStyle().setProperty('display', 'none');
    	var container = this.viewport.openBoard();
        this.createBreadcrumb(context.group, container);
        var table = Element.create(this.elements.boardTable, container);
        var mediaCount = context.group.getMediaCount();
        if ((! (full || (context.group instanceof Group.Medias))) && (mediaCount > Launcher.MAX_MEDIA)) {
            //new HtmlElement({attributes: {htmlTagName: 'img', src: 'style/list-add.png'}, style: {margin: '30px', cursor: 'pointer'}, events: {click: 'onDisplayBoardFull'}}, container);
            new HtmlElement({attributes: {htmlTagName: 'a', href: '#', textContent: this.resource.getf('seeAllMedias', mediaCount)},
                style: {margin: '30px'}, events: {click: 'onDisplayBoardFull'}}, container);
            mediaCount = Launcher.MAX_MEDIA;
        }
        for (var i = 0; i < mediaCount; i++) {
            var media = context.group.getMedia(i);
            Logger.getInstance().trace('createMiniature(' + i + ')');
            media.createMiniature({group: context.group, index: i, attributes: {'class': 'media'}, events: {click: 'onOpenMedia'}}, table);
        }
    },
    displayGroup: function(context) {
        Logger.getInstance().trace('displayGroup()');
        this.mediaToolbar.getStyle().setProperty('display', 'none');
        this.repositoryToolbar.getStyle().setProperty('display', context.group.getParent() ? 'none' : '');
    	var container = this.viewport.openGroup();
        this.createBreadcrumb(context.group, container);
        var table = Element.create(this.elements.groupTable, container);
        var size = context.group.size();
        for (var i = 0; i < size; i++) {
            var item = context.group.getItem(i);
            var groupCell = Element.create(this.elements.groupCell, table);
            groupCell.setGroup(item);
        }
    },
    displayContext: function(context) {
        Logger.getInstance().trace('displayContext(' + context.viewName + ')');
        this.mediaIcon.setAttribute('class', 'icon');
        this.boardIcon.setAttribute('class', 'icon');
        this.groupIcon.setAttribute('class', 'icon');
        switch (context.viewName) {
        case 'media':
            this.mediaIcon.setAttribute('class', 'iconSelected');
            this.displayMedia(context);
            break;
        case 'board':
        case 'boardFull':
            this.boardIcon.setAttribute('class', 'iconSelected');
            this.displayBoard(context, context.viewName == 'boardFull');
            break;
        case 'group':
            this.groupIcon.setAttribute('class', 'iconSelected');
            this.displayGroup(context);
            break;
        }
    },
    changeContext: function(context) {
        Logger.getInstance().trace('changeContext(' + context.viewName + ')');
        var onlyMedia = false;
        if ((this.currentContext != null) &&
            (this.currentContext.viewName == context.viewName) &&
            (this.currentContext.group == context.group))
        {
            if (this.currentContext.mediaIndex == context.mediaIndex) {
                return;
            } else {
                onlyMedia = true;
            }
        }
        // Reset navigation context
        this.comment = null;
        
        // Save scroll position
        var scrollPos = this.viewport._htmlElement.scrollTop;
        if (this.currentContext != null) {
            switch (this.currentContext.viewName) {
            case 'board':
                this.currentContext.group._scrollBoardHistory = scrollPos;
                break;
            case 'group':
                this.currentContext.group._scrollGroupHistory = scrollPos;
                break;
            }
        }
        Logger.getInstance().trace('Saving scroll position: ' + scrollPos);
        
        this.currentContext = context;
        
        // History
        if (this.contextHistoryIndex != this.contextHistory.length - 1) {
            this.contextHistory.length = this.contextHistoryIndex + 1;
        }
        this.contextHistoryIndex++;
        this.contextHistory[this.contextHistoryIndex] = this.currentContext;
        if (onlyMedia) {
            this.displayMedia(this.currentContext);
        } else {
            this.displayContext(this.currentContext);
        }

        // Restore scroll position
        var scrollPos = -1;
        switch (this.currentContext.viewName) {
        case 'board':
            scrollPos = 0;
            if ('_scrollBoardHistory' in this.currentContext.group) {
                scrollPos = this.currentContext.group._scrollBoardHistory;
            }
            break;
        case 'group':
            scrollPos = 0;
            if ('_scrollGroupHistory' in this.currentContext.group) {
                scrollPos = this.currentContext.group._scrollGroupHistory;
            }
            break;
        }
        if (scrollPos >= 0) {
            Logger.getInstance().trace('Restore scroll position: ' + scrollPos);
            this.viewport._htmlElement.scrollTop = scrollPos;
        }
    },
    onGoNext: function(event) {
        event.message.preventDefault();
        if (this.contextHistoryIndex < this.contextHistory.length - 1) {
            this.contextHistoryIndex++;
            this.currentContext = this.contextHistory[this.contextHistoryIndex];
            this.displayContext(this.currentContext);
        } else {
            alert('no more history ' + this.contextHistoryIndex + '/' + this.contextHistory.length);
        }
    },
    onGoPrevious: function(event) {
        event.message.preventDefault();
        if (this.contextHistoryIndex > 0) {
            this.contextHistoryIndex--;
            this.currentContext = this.contextHistory[this.contextHistoryIndex];
            this.displayContext(this.currentContext);
        } else {
            alert('no more history ' + this.contextHistoryIndex + '/' + this.contextHistory.length);
        }
    },
    onGoHome: function(event) {
        event.message.preventDefault();
        this.changeContext(new Context('group', this.root, 0));
    },
    openGroup: function(grp) {
        this.changeContext(new Context((grp instanceof Group.Medias) ? 'board' : 'group', grp, 0));
    },
    onOpenGroup: function(event) {
        event.message.preventDefault();
        this.openGroup(event.message.element.getGroup());
    },
    onOpenMedia: function(event) {
        event.message.preventDefault();
        var grp = event.message.element.getGroup();
        var idx = event.message.element.getIndex();
        //var media = event.message.element.getMedia();
        this.changeContext(new Context('media', grp, idx));
    },
    onComment: function(event) {
        event.message.preventDefault();
        if (this.comment) {
            return;
        }
    	var container = this.viewport.openComment();
        var div = Element.create(this.elements.addComment, container);
        div.getById('ok').observe('click', (function (e) {
            var media = this.currentContext.getMedia();
            var content = div.getById('newComment').getHtmlElement().value;
            Logger.getInstance().debug('content: -->' + content + '<--');
            var envelope = new SxeEnvelope(this.userId, this.password);
            envelope.addRequest({name: 'addComment', attributes: {mediaId: media.getId(), content: content}});
            this.sxe.send(envelope, (function(response) {
                var envelope = response.getEnvelope();
                //Logger.getInstance().debug('response: -->' + envelope.toString() + '<--');
                var result = envelope.getResult(0);
                if (result.getName() == 'success') {
                    Logger.getInstance().trace('Comment added');
                    media.newComment(this.user.id, content);
                    div.destroy();
                    this.comment = null;
                    this.displayMediaComments(this.currentContext);
                    this.root.getItemByName(this.resource.get('board.commented')).addItem(media);
                } else {
                    var msg = this.resource.get('error');
                    if (result.getName() == 'failure') {
                        msg = this.resource.getf('error.message', result.getText());
                    }
                    this.broker.publish('onDisplayInfo', msg);
                }
            }).bind(this));
        }).bind(this));
        div.getById('cancel').observe('click', (function (e) {
            div.destroy();
            this.comment = null;
        }).bind(this));
        this.comment = true;
        div.getById('newComment').getHtmlElement().focus();
    },
    onGoBackward: function(event) {
        event.message.preventDefault();
        this.changeContext(this.currentContext.createMedia(-1));
    },
    onGoUp: function(event) {
        event.message.preventDefault();
        this.openGroup(this.currentContext.group);
    },
    onGoForward: function(event) {
        event.message.preventDefault();
        this.changeContext(this.currentContext.createMedia(1));
    }
});

Object.extend(Launcher,
{
    _instance: null,
    MAX_MEDIA: 16,
    scrollHistory : {},
    hasRight: function(rights, right) {
        if (rights && right) {
            var ra = rights.split(',');
            for (var i = 0; i < ra.length; i++) {
                if (ra[i] == right) {
                    return true;
                }
            }
        }
        return false;
    },
    sha1: function(value) {
        var md = MessageDigest.getInstance('SHA1');
        md.updateString(value, 'UTF-8');
        var buffer = md.digest();
        return StringCodec.hexEncode(buffer);
    },
    getInstance: function() {
        if (Launcher._instance == null) {
        	Launcher._instance = new Launcher('default');
            //Launcher._instance = new Launcher('mobile');
        }
        return Launcher._instance;
    }
});


return Launcher;
});
define('spyl/pb/Media', ['jls/lang/Class', 'jls/lang/Exception', 'jls/lang/Logger', 'jls/util/Formatter'],
        function (Class, Exception, Logger, Formatter) {

// Late binding
var Launcher, Comment, PreviewElement, PictureElement, MovieElement;
require(['spyl/pb/Launcher',
         'spyl/pb/Comment',
         'spyl/pb/PreviewElement',
         'spyl/pb/PictureElement',
         'spyl/pb/MovieElement'], function(l, c, p, pic, m) {
    Launcher = l;
    Comment = c;
    PreviewElement = p;
    PictureElement = pic;
    MovieElement = m;
});

var Media;
Media = Class.create({
    initialize : function(id, repositoryId, userId, date, pubDate, description, filename, commentCount) {
        this._id = id;
        this._repositoryId = repositoryId;
        this._userId = userId;
        this._date = date;
        this._pubDate = pubDate;
        this._description = description;
        this._filename = filename;
        this._commentCount = commentCount;
        this._comments = [];
    },
    getTitle : function() {
        var launcher = Launcher.getInstance();
        var title = launcher.formatDate(this._date);
        if (this._description) {
            title += ', ' + this._description;
        }
        //title += ', ' + this._filename;
        title += ', ' + launcher.users[this._userId].login;
        if (this._commentCount > 0) {
            title += ', ' + this._commentCount + ' ';
            if (this._commentCount > 1) {
                title += launcher.commentsLabel;
            } else {
                title += launcher.commentLabel;
            }
        }
        return title;
    },
    getId : function() {
        return this._id;
    },
    getRepositoryId : function() {
        return this._repositoryId;
    },
    getDate : function() {
        return this._date;
    },
    addComment : function(comment) {
        this._comments.push(comment);
    },
    newComment : function(userId, content) {
        this._comments.push(new Comment(-1, userId, this._id, content, new Date()));
        this._commentCount++;
    },
    clearComments : function() {
        this._comments = [];
    },
    getCommentCount : function() {
        return this._commentCount;
    },
    getComments : function() {
        return this._comments;
    },
    getBasePathname: function() {
        return Formatter.format('repository/%02d/%d/', this._repositoryId, this._date.getFullYear());
    },
    getExtension: function() {
        return '';
    },
    getPathname: function() {
        return this.getBasePathname() + Formatter.format('%03d', this._id) + this.getExtension();
    },
    getMiniaturePathname: function() {
        return this.getBasePathname() + Formatter.format('mini/%03d.jpg', this._id);
    },
    createMiniature : function(params, parent) {
        Logger.getInstance().trace('createMiniature()');
        return new PreviewElement(params, parent);
    }
});

Object.extend(Media,
{
    compareMedias : function(a, b) {
        if (a._date < b._date)
            return -1;
        if (a._date > b._date)
            return 1;
        return 0;
    },
    compareMediasByPubDate : function(a, b) {
        if (a._pubDate < b._pubDate)
            return -1;
        if (a._pubDate > b._pubDate)
            return 1;
        return 0;
    },
    createFromXML : function(node) {
        var id = parseInt(node.getAttribute('id'), 10);
        var repositoryId = parseInt(node.getAttribute('repositoryId'), 10);
        var type = node.getAttribute('type');
        var userId = parseInt(node.getAttribute('userId'), 10);
        var filename = node.getAttribute('filename');
        var description = node.getAttribute('description');
        var date = new Date(parseInt(node.getAttribute('date'), 10) * 1000);
        var pubDate = new Date(parseInt(node.getAttribute('pubDate'), 10) * 1000);
        var commentCount = parseInt(node.getAttribute('commentCount'), 10);
        switch (type) {
        case 'picture':
            return new Media.Picture(id, repositoryId, userId, date, pubDate, description, filename, commentCount);
        case 'movie':
            return new Media.Movie(id, repositoryId, userId, date, pubDate, description, filename, commentCount);
        }
        throw new Exception('Invalid media type (' + type + ')');
    }
});

Media.Picture = Class.create(Media,
{
    createElement : function(params, parent) {
        return new PictureElement(params, parent);
    },
    getExtension: function() {
        return '.jpg';
    }
});

Media.Movie = Class.create(Media,
{
    createElement : function(params, parent) {
        return new MovieElement(params, parent);
    },
    getExtension: function() {
        return '.flv';
    }
});


return Media;
});
define('spyl/pb/MessageBox', ['jls/lang/Class', 'jls/lang/Logger','jls/html/HtmlElement', 'jls/gui/TemplateContainer'], function(Class, Logger, HtmlElement, TemplateContainer) {

    var MessageBox = Class.create(HtmlElement, {
        initialize : function($super, parameters, parent) {
            TemplateContainer.create(this, MessageBox.TEMPLATE, $super, parameters, parent);
        }
    });

    Object.extend(MessageBox, {
        TEMPLATE : {
            attributes : {
                htmlTagName : 'div',
                'class' : 'glassBg'
            },
            style : {
                position : 'absolute',
                textAlign : 'center',
                width : '100%',
                height : '100%',
                left : '0px',
                top : '0px'
            },
            children : [{
                classname : 'jls/html/HtmlElement',
                attributes : {
                    htmlTagName : 'div',
                    'class' : 'box',
                    id : 'container'
                },
                style : {
                    margin : '30px 60px',
                    padding : '30px'
                }
            }]}
    });
    
    return TemplateContainer.prepareClass(MessageBox);
});
define('spyl/pb/MovieElement', ['jls/lang/Class', 'spyl/pb/PreviewElement'], function (Class, PreviewElement) {

var MovieElement;
MovieElement = Class.create(PreviewElement,
{
    onCreateHtmlElement : function() {
        var embed = document.createElement('embed');
        embed.setAttribute('quality', 'high');
        embed.setAttribute('allowfullscreen', 'true');
        //embed.setAttribute('allowscriptaccess', 'always');
        embed.setAttribute('width', '768'); embed.setAttribute('height', '768');
        var flashvars = 'file=' + this._media.getPathname();
        flashvars += '&autostart=true&controlbar=over&bufferlength=5&stretching=none';
        flashvars += '&screencolor=#FFFFFF';
        flashvars += '&skin=glow.zip';
        embed.setAttribute('flashvars', flashvars);
        embed.setAttribute('type', 'application/x-shockwave-flash');
        embed.setAttribute('src', 'player.swf');
        return embed;
    }
});


return MovieElement;
});
define('spyl/pb/PictureElement', ['jls/lang/Class', 'spyl/pb/PreviewElement'], function (Class, PreviewElement) {
    return Class.create(PreviewElement, {
        onCreateHtmlElement : function() {
            var htmlElement = document.createElement('img');
            htmlElement.setAttribute('src', this._media.getPathname());
            htmlElement.setAttribute('title', this._media.getTitle());
            return htmlElement;
        }
    });
});
define('spyl/pb/PreviewElement', ['jls/lang/Class', 'jls/lang/Logger', 'jls/html/HtmlElement'], function (Class, Logger, HtmlElement) {

var PreviewElement;
PreviewElement = Class.create(HtmlElement,
{
    initialize : function($super, parameters, parent) {
        Logger.getInstance().trace('PreviewElement.constructor()');
        parameters = parameters || {};
        this._group = 'group' in parameters ? parameters.group : null;
        this._index = 'index' in parameters ? parameters.index : -1;
        this._media = 'media' in parameters ? parameters.media : this._group.getMedia(this._index);
        $super(parameters, parent);
    },
    onCreateHtmlElement : function() {
        var htmlElement = document.createElement('img');
        htmlElement.setAttribute('src', this._media.getMiniaturePathname());
        htmlElement.setAttribute('title', this._media.getTitle());
        return htmlElement;
    },
    getMedia : function() {
        return this._media;
    },
    getIndex : function() {
        return this._index;
    },
    getGroup : function() {
        return this._group;
    }
});


return PreviewElement;
});
define('spyl/pb/Repository', ['jls/lang/Class', 'jls/lang/Logger'], function (Class, Logger) {

var Repository;
Repository = Class.create(
{
    initialize : function(id, title, userId, description, requiresUserAccess, isPublic, rights) {
        this.id = id;
        this.title = title;
        this.userId = userId;
        this.description = description;
        this.requiresUserAccess = requiresUserAccess ? true : false;
        this.isPublic = isPublic ? true : false;
        // User repository fields
        this.rights = rights || null;
        //this.isSubscribed = false;
    },
    getRights : function() {
        return this.rights;
    },
    setRights : function(rights) {
        this.rights = rights || null;
        return this;
    },
    clone : function() {
        return new Repository(
                this.id,
                this.title,
                this.userId,
                this.description,
                this.requiresUserAccess,
                this.isPublic,
                this.rights
                );
    },
    equals : function(b) {
        if (b === this) {
            return true;
        }
        return (b.id == this.id) && (b.title == this.title) && (b.userId == this.userId) &&
            (b.description == this.description) && (b.requiresUserAccess == this.requiresUserAccess) &&
            (b.isPublic == this.isPublic);
    }
});

Object.extend(Repository,
{
    createFromXML : function(node) {
        return new Repository(
                parseInt(node.getAttribute('id'), 10),
                node.getAttribute('title'),
                parseInt(node.getAttribute('userId'), 10),
                node.getAttribute('description'),
                node.getAttribute('requiresUserAccess') == 'true',
                node.getAttribute('isPublic') == 'true'
                );
    }
});

return Repository;
});
define('spyl/pb/User', ['jls/lang/Class'], function (Class) {

var User;
User = Class.create(
{
    initialize : function(id, login, password, email, rights, creationDate, lastAuthenticationDate, alertThreshold) {
        this.id = id;
        this.login = login;
        this.password = password;
        this.email = email;
        this.creationDate = creationDate;
        this.lastAuthenticationDate = lastAuthenticationDate;
        this.alertThreshold = alertThreshold;
        rights = typeof rights == 'string' ? rights : '';
        var rightList = rights.split(',');
        this.rights = {};
        for (var i = 0; i < rightList.length; i++) {
            this.rights[rightList[i]] = true;
        }
    },
    toXML : function() {
    }
});

Object.extend(User,
{
    createFromXML : function(xmlUser) {
        return new User(
                parseInt(xmlUser.getAttribute('id'), 10),
                xmlUser.getAttribute('login'),
                xmlUser.getAttribute('password'),
                xmlUser.getAttribute('email'),
                xmlUser.getAttribute('rights'),
                new Date(parseInt(xmlUser.getAttribute('creationDate'), 10) * 1000),
                new Date(parseInt(xmlUser.getAttribute('lastAuthenticationDate'), 10) * 1000),
                xmlUser.getAttribute('alertThreshold')
                );
    }
});


return User;
});
define('spyl/pb/ViewportElement', ['jls/lang/Class', 'jls/lang/Logger', 'jls/html/HtmlElement'], function (Class, Logger, HtmlElement) {

var ViewportElement;
ViewportElement = Class.create(HtmlElement,
{
    initialize : function($super, parameters, parent) {
        $super(parameters, parent);
        this.borderedFix = this.getById('bordered_fix');
        this.borderedAuto = this.getById('bordered_auto');
        this.borderedFixRow = this.getById('bordered_fix_row');
        this.borderedAutoRow = this.getById('bordered_auto_row');
    },
    onCreate : function($super) {
        this.setAttribute('htmlTagName', 'div')
        $super();
    },
    openMedia: function() {
    	this.borderedAutoRow.getStyle().setProperty('display', 'none');
        this.borderedFix.removeChildren();
        this.borderedAuto.removeChildren();
        this.borderedFixRow.getStyle().setProperty('display', '');
        return this.borderedFix;
    },
    openComment: function() {
        this.borderedAuto.removeChildren();
        this.borderedAutoRow.getStyle().setProperty('display', '');
        return this.borderedAuto;
    },
    openBoard: function() {
        this.borderedAuto.removeChildren();
        this.borderedFixRow.getStyle().setProperty('display', 'none');
        this.borderedAutoRow.getStyle().setProperty('display', '');
        return this.borderedAuto;
    },
    openGroup: function() {
        this.borderedAuto.removeChildren();
        this.borderedFixRow.getStyle().setProperty('display', 'none');
        this.borderedAutoRow.getStyle().setProperty('display', '');
        //this.borderedAuto.getStyle().removeProperty('display');
        return this.borderedAuto;
    }
});


return ViewportElement;
});
define('spyl/pb/ViewportMobileElement', ['jls/lang/Class', 'jls/lang/Logger', 'jls/html/HtmlElement'], function (Class, Logger, HtmlElement) {

var ViewportMobileElement;
ViewportMobileElement = Class.create(HtmlElement,
{
    initialize : function($super, parameters, parent) {
        $super(parameters, parent);
        this.container = this.getById('container');
        this.mediaContainer = this.getById('mediaContainer');
        this.media = this.mediaContainer.getById('media');
        this.comments = this.mediaContainer.getById('comments');
    	this.containerDisplay = this.container.getStyle().getPropertyValue('display');
    },
    onCreate : function($super) {
        this.setAttribute('htmlTagName', 'div')
        $super();
    },
    openMedia: function() {
    	this.container.getStyle().setProperty('display', 'none');
    	this.comments.getStyle().setProperty('display', 'none');
    	this.mediaContainer.getStyle().setProperty('display', '');
        this.media.removeChildren();
        return this.media;
    },
    openComment: function() {
        this.comments.removeChildren();
    	this.comments.getStyle().setProperty('display', '');
        return this.comments;
    },
    openBoard: function() {
    	this.container.removeChildren();
    	this.container.getStyle().setProperty('display', this.containerDisplay);
    	this.mediaContainer.getStyle().setProperty('display', 'none');
        return this.container;
    },
    openGroup: function() {
        return this.openBoard();
    }
});


return ViewportMobileElement;
});

require(['_AMD'], function(_AMD) {
  _AMD.disableDefine();
  _AMD.disableDeferredLoading();
});
