Gruntification

This commit is contained in:
jendib 2014-02-23 02:28:44 +01:00
parent 12c3c4750f
commit ae566018d6
94 changed files with 488 additions and 271 deletions

View File

@ -264,14 +264,44 @@
<plugins> <plugins>
<!-- Generation of the war --> <!-- Launch NPM & Grunt -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<target name="building">
<!-- npm install -->
<exec executable="cmd" dir="${project.basedir}/src/main/webapp" osfamily="windows" failonerror="true">
<arg line="/c npm install" />
</exec>
<exec executable="npm" dir="${project.basedir}/src/main/webapp" osfamily="unix" failonerror="true">
<arg line="install" />
</exec>
<!-- grunt -->
<exec executable="cmd" dir="${project.basedir}/src/main/webapp" osfamily="windows">
<arg line="/c grunt --apiurl=api" />
</exec>
<exec executable="grunt" dir="${project.basedir}/src/main/webapp" osfamily="unix">
<arg line="--apiurl=api" />
</exec>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- WAR generation -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId> <artifactId>maven-war-plugin</artifactId>
<configuration> <configuration>
<warSourceExcludes> <warSourceDirectory>${basedir}/src/main/webapp/dist</warSourceDirectory>
sismicsdocs/**,
</warSourceExcludes>
<webXml>src\main\webapp\WEB-INF\web.xml</webXml> <webXml>src\main\webapp\WEB-INF\web.xml</webXml>
</configuration> </configuration>
</plugin> </plugin>

View File

@ -1,2 +1,4 @@
/sismicsdocs sismicsdocs
/.idea node_modules
dist
.idea

View File

@ -0,0 +1,122 @@
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
clean: {
dist: {
src: ['dist']
}
},
ngmin: {
dist: {
expand: true,
cwd: 'src',
src: ['app/**/*.js'],
dest: 'dist'
}
},
concat: {
docs: {
options: {
separator: ';'
},
src: ['src/lib/jquery.js','src/lib/jquery.ui.js','src/lib/underscore.js','src/lib/colorpicker.js', 'src/lib/angular.js', 'src/lib/angular.*.js',
'dist/app/docs/app.js', 'dist/app/docs/controller/*.js', 'dist/app/docs/directive/*.js', 'dist/app/docs/filter/*.js', 'dist/app/docs/service/*.js'],
dest: 'dist/docs.js'
},
share: {
options: {
separator: ';'
},
src: ['src/lib/jquery.js','src/lib/jquery.ui.js','src/lib/underscore.js','src/lib/colorpicker.js', 'src/lib/angular.js', 'src/lib/angular.*.js',
'dist/app/share/app.js', 'dist/app/share/controller/*.js', 'dist/app/share/directive/*.js', 'dist/app/share/filter/*.js', 'dist/app/share/service/*.js'],
dest: 'dist/share.js'
},
css: {
src: ['src/style/*.css', 'dist/less.css'],
dest: 'dist/style.css'
}
},
less: {
dist: {
src: ['src/style/*.less'],
dest: 'dist/less.css'
}
},
cssmin: {
dist: {
src: 'dist/style.css',
dest: 'dist/style/style.min.css'
}
},
uglify: {
docs: {
src: 'dist/docs.js',
dest: 'dist/docs.min.js'
},
share: {
src: 'dist/share.js',
dest: 'dist/share.min.js'
}
},
copy: {
dist: {
expand: true,
cwd: 'src/',
src: ['**', '!**/*.js', '!*.html', '!**/*.less', '!**/*.css'],
dest: 'dist/'
}
},
htmlrefs: {
index: {
src: 'src/index.html',
dest: 'dist/index.html'
},
share: {
src: 'src/share.html',
dest: 'dist/share.html'
}
},
remove: {
dist: {
fileList: ['dist/style.css', 'dist/docs.js', 'dist/share.js', 'dist/less.css'],
dirList: ['dist/app']
}
},
cleanempty: {
options: {
files: false,
folders: true
},
src: ['dist/**']
},
replace: {
dist: {
src: ['dist/docs.min.js', 'dist/share.min.js', 'dist/**/*.html'],
overwrite: true,
replacements: [{
from: '../api',
to: grunt.option('apiurl') || '../api'
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-cleanempty');
grunt.loadNpmTasks('grunt-htmlrefs');
grunt.loadNpmTasks('grunt-css');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-remove');
grunt.loadNpmTasks('grunt-ngmin');
grunt.loadNpmTasks('grunt-text-replace');
// Default tasks.
grunt.registerTask('default', ['clean', 'ngmin', 'concat:docs', 'concat:share', 'less', 'concat:css', 'cssmin',
'uglify:docs', 'uglify:share', 'copy', 'remove', 'cleanempty', 'htmlrefs:index', 'htmlrefs:share', 'replace']);
};

View File

@ -1,98 +0,0 @@
'use strict';
/**
* File view controller.
*/
App.controller('FileView', function($modal, $state, $stateParams) {
var modal = $modal.open({
windowClass: 'modal modal-fileview',
templateUrl: 'partial/docs/file.view.html',
controller: function($rootScope, $scope, $state, $stateParams, Restangular) {
// Load files
Restangular.one('file').getList('list', { id: $stateParams.id }).then(function(data) {
$scope.files = data.files;
// Search current file
_.each($scope.files, function(value) {
if (value.id == $stateParams.fileId) {
$scope.file = value;
}
});
});
/**
* Navigate to the next file.
*/
$scope.nextFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var next = $scope.files[key + 1];
if (next) {
$state.transitionTo('document.view.file', { id: $stateParams.id, fileId: next.id });
}
}
});
};
/**
* Navigate to the previous file.
*/
$scope.previousFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var previous = $scope.files[key - 1];
if (previous) {
$state.transitionTo('document.view.file', { id: $stateParams.id, fileId: previous.id });
}
}
});
};
/**
* Open the file in a new window.
*/
$scope.openFile = function() {
window.open('api/file/' + $stateParams.fileId + '/data');
};
/**
* Print the file.
*/
$scope.printFile = function() {
var popup = window.open('api/file/' + $stateParams.fileId + '/data', '_blank');
popup.onload = function () {
popup.print();
popup.close();
}
};
/**
* Close the file preview.
*/
$scope.closeFile = function () {
modal.dismiss();
};
// Close the modal when the user exits this state
var off = $rootScope.$on('$stateChangeStart', function(event, toState) {
if (!modal.closed) {
if (toState.name == 'document.view.file') {
modal.close();
} else {
modal.dismiss();
}
}
off();
});
}
});
// Returns to document view on file close
modal.closed = false;
modal.result.then(function() {
modal.closed = true;
}, function() {
modal.closed = true;
$state.transitionTo('document.view', { id: $stateParams.id });
});
});

View File

@ -1,7 +0,0 @@
'use strict';
/**
* Settings default page controller.
*/
App.controller('SettingsDefault', function($scope, Restangular) {
});

View File

@ -1,98 +0,0 @@
'use strict';
/**
* File view controller.
*/
App.controller('FileView', function($modal, $state, $stateParams) {
var modal = $modal.open({
windowClass: 'modal modal-fileview',
templateUrl: 'partial/share/file.view.html',
controller: function($rootScope, $scope, $state, $stateParams, Restangular) {
// Load files
Restangular.one('file').getList('list', { id: $stateParams.documentId, share: $stateParams.shareId }).then(function(data) {
$scope.files = data.files;
// Search current file
_.each($scope.files, function(value) {
if (value.id == $stateParams.fileId) {
$scope.file = value;
}
});
});
/**
* Navigate to the next file.
*/
$scope.nextFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var next = $scope.files[key + 1];
if (next) {
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: next.id });
}
}
});
};
/**
* Navigate to the previous file.
*/
$scope.previousFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var previous = $scope.files[key - 1];
if (previous) {
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: previous.id });
}
}
});
};
/**
* Open the file in a new window.
*/
$scope.openFile = function() {
window.open('api/file/' + $stateParams.fileId + '/data?share=' + $stateParams.shareId);
};
/**
* Print the file.
*/
$scope.printFile = function() {
var popup = window.open('api/file/' + $stateParams.fileId + '/data', '_blank');
popup.onload = function () {
popup.print();
popup.close();
}
};
/**
* Close the file preview.
*/
$scope.closeFile = function () {
modal.dismiss();
};
// Close the modal when the user exits this state
var off = $rootScope.$on('$stateChangeStart', function(event, toState){
if (!modal.closed) {
if (toState.name == 'share.file') {
modal.close();
} else {
modal.dismiss();
}
}
off();
});
}
});
// Returns to share view on file close
modal.closed = false;
modal.result.then(function() {
modal.closed = true;
},function(result) {
modal.closed = true;
$state.transitionTo('share', { documentId: $stateParams.documentId, shareId: $stateParams.shareId });
});
});

View File

@ -1,7 +0,0 @@
'use strict';
/**
* Main controller.
*/
App.controller('Main', function() {
});

View File

@ -0,0 +1,24 @@
{
"name": "sismics-docs",
"description": "Lightweight document management system",
"readme": "See http://github.com/simics/docs for more informations.",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "git://github.com/sismics/docs.git"
},
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-uglify": "~0.3.2",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-cleanempty": "~0.2.0",
"grunt-htmlrefs": "~0.5.0",
"grunt-css": "~0.5.4",
"grunt-contrib-less": "~0.9.0",
"grunt-remove": "~0.1.0",
"grunt-ngmin": "0.0.3",
"grunt-text-replace": "~0.3.11"
}
}

View File

@ -3,7 +3,7 @@
/** /**
* Sismics Docs application. * Sismics Docs application.
*/ */
var App = angular.module('docs', angular.module('docs',
// Dependencies // Dependencies
['ui.router', 'ui.route', 'ui.bootstrap', 'ui.keypress', 'ui.validate', 'dialog', ['ui.router', 'ui.route', 'ui.bootstrap', 'ui.keypress', 'ui.validate', 'dialog',
'ui.sortable', 'restangular', 'ngSanitize', 'ngTouch', 'colorpicker.module'] 'ui.sortable', 'restangular', 'ngSanitize', 'ngTouch', 'colorpicker.module']
@ -171,7 +171,7 @@ var App = angular.module('docs',
}); });
// Configuring Restangular // Configuring Restangular
RestangularProvider.setBaseUrl('api'); RestangularProvider.setBaseUrl('../api');
// Configuring $http to act like jQuery.ajax // Configuring $http to act like jQuery.ajax
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

View File

@ -3,7 +3,7 @@
/** /**
* Document controller. * Document controller.
*/ */
App.controller('Document', function($scope, $timeout, $state, Restangular) { angular.module('docs').controller('Document', function($scope, $timeout, $state, Restangular) {
/** /**
* Documents table sort status. * Documents table sort status.
*/ */

View File

@ -3,7 +3,7 @@
/** /**
* Document default controller. * Document default controller.
*/ */
App.controller('DocumentDefault', function($scope, $state, Restangular) { angular.module('docs').controller('DocumentDefault', function($scope, $state, Restangular) {
// Load app data // Load app data
Restangular.one('app').get().then(function(data) { Restangular.one('app').get().then(function(data) {
$scope.app = data; $scope.app = data;

View File

@ -3,7 +3,7 @@
/** /**
* Document edition controller. * Document edition controller.
*/ */
App.controller('DocumentEdit', function($rootScope, $scope, $q, $http, $state, $stateParams, Restangular, Tag) { angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $q, $http, $state, $stateParams, Restangular, Tag) {
// Alerts // Alerts
$scope.alerts = []; $scope.alerts = [];
@ -120,7 +120,7 @@ App.controller('DocumentEdit', function($rootScope, $scope, $q, $http, $state, $
// Send the file // Send the file
$.ajax({ $.ajax({
type: 'PUT', type: 'PUT',
url: 'api/file', url: '../api/file',
data: formData, data: formData,
cache: false, cache: false,
contentType: false, contentType: false,

View File

@ -3,7 +3,7 @@
/** /**
* Document view controller. * Document view controller.
*/ */
App.controller('DocumentView', function ($scope, $state, $stateParams, $location, $dialog, $modal, Restangular) { angular.module('docs').controller('DocumentView', function ($scope, $state, $stateParams, $location, $dialog, $modal, Restangular) {
// Load data from server // Load data from server
Restangular.one('document', $stateParams.id).get().then(function(data) { Restangular.one('document', $stateParams.id).get().then(function(data) {
$scope.document = data; $scope.document = data;

View File

@ -0,0 +1,83 @@
'use strict';
/**
* File modal view controller.
*/
angular.module('docs').controller('FileModalView', function($rootScope, $modalInstance, $scope, $state, $stateParams, Restangular) {
// Load files
Restangular.one('file').getList('list', { id: $stateParams.id }).then(function(data) {
$scope.files = data.files;
// Search current file
_.each($scope.files, function(value) {
if (value.id == $stateParams.fileId) {
$scope.file = value;
}
});
});
/**
* Navigate to the next file.
*/
$scope.nextFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var next = $scope.files[key + 1];
if (next) {
$state.transitionTo('document.view.file', { id: $stateParams.id, fileId: next.id });
}
}
});
};
/**
* Navigate to the previous file.
*/
$scope.previousFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var previous = $scope.files[key - 1];
if (previous) {
$state.transitionTo('document.view.file', { id: $stateParams.id, fileId: previous.id });
}
}
});
};
/**
* Open the file in a new window.
*/
$scope.openFile = function() {
window.open('../api/file/' + $stateParams.fileId + '/data');
};
/**
* Print the file.
*/
$scope.printFile = function() {
var popup = window.open('../api/file/' + $stateParams.fileId + '/data', '_blank');
popup.onload = function () {
popup.print();
popup.close();
}
};
/**
* Close the file preview.
*/
$scope.closeFile = function () {
$modalInstance.dismiss();
};
// Close the modal when the user exits this state
var off = $rootScope.$on('$stateChangeStart', function(event, toState) {
if (!$modalInstance.closed) {
if (toState.name == 'document.view.file') {
$modalInstance.close();
} else {
$modalInstance.dismiss();
}
}
off();
});
});

View File

@ -0,0 +1,21 @@
'use strict';
/**
* File view controller.
*/
angular.module('docs').controller('FileView', function($modal, $state, $stateParams) {
var modal = $modal.open({
windowClass: 'modal modal-fileview',
templateUrl: 'partial/docs/file.view.html',
controller: 'FileModalView'
});
// Returns to document view on file close
modal.closed = false;
modal.result.then(function() {
modal.closed = true;
}, function() {
modal.closed = true;
$state.transitionTo('document.view', { id: $stateParams.id });
});
});

View File

@ -3,7 +3,7 @@
/** /**
* Login controller. * Login controller.
*/ */
App.controller('Login', function($scope, $rootScope, $state, $dialog, User) { angular.module('docs').controller('Login', function($scope, $rootScope, $state, $dialog, User) {
$scope.login = function() { $scope.login = function() {
User.login($scope.user).then(function() { User.login($scope.user).then(function() {
$rootScope.userInfo = User.userInfo(true); $rootScope.userInfo = User.userInfo(true);

View File

@ -3,7 +3,7 @@
/** /**
* Main controller. * Main controller.
*/ */
App.controller('Main', function($scope, $rootScope, $state, User) { angular.module('docs').controller('Main', function($scope, $rootScope, $state, User) {
User.userInfo().then(function(data) { User.userInfo().then(function(data) {
if (data.anonymous) { if (data.anonymous) {
$state.transitionTo('login'); $state.transitionTo('login');

View File

@ -3,7 +3,7 @@
/** /**
* Navigation controller. * Navigation controller.
*/ */
App.controller('Navigation', function($scope, $http, $state, $rootScope, User, Restangular) { angular.module('docs').controller('Navigation', function($scope, $http, $state, $rootScope, User, Restangular) {
User.userInfo().then(function(data) { User.userInfo().then(function(data) {
$rootScope.userInfo = data; $rootScope.userInfo = data;
}); });

View File

@ -3,7 +3,7 @@
/** /**
* Settings controller. * Settings controller.
*/ */
App.controller('Settings', function($scope, Restangular) { angular.module('docs').controller('Settings', function($scope, Restangular) {
// Flag if the user is admin // Flag if the user is admin
$scope.isAdmin = $scope.userInfo.base_functions.indexOf('ADMIN') != -1; $scope.isAdmin = $scope.userInfo.base_functions.indexOf('ADMIN') != -1;
}); });

View File

@ -3,7 +3,7 @@
/** /**
* Settings account controller. * Settings account controller.
*/ */
App.controller('SettingsAccount', function($scope, Restangular) { angular.module('docs').controller('SettingsAccount', function($scope, Restangular) {
$scope.editUserAlert = false; $scope.editUserAlert = false;
// Alerts // Alerts

View File

@ -0,0 +1,7 @@
'use strict';
/**
* Settings default page controller.
*/
angular.module('docs').controller('SettingsDefault', function($scope, Restangular) {
});

View File

@ -3,7 +3,7 @@
/** /**
* Settings logs controller. * Settings logs controller.
*/ */
App.controller('SettingsLog', function($scope, Restangular) { angular.module('docs').controller('SettingsLog', function($scope, Restangular) {
Restangular.one('app/log').get({ Restangular.one('app/log').get({
limit: 100 limit: 100
}).then(function(data) { }).then(function(data) {

View File

@ -3,7 +3,7 @@
/** /**
* Settings session controller. * Settings session controller.
*/ */
App.controller('SettingsSession', function($scope, Restangular) { angular.module('docs').controller('SettingsSession', function($scope, Restangular) {
/** /**
* Load sessions. * Load sessions.
*/ */

View File

@ -3,7 +3,7 @@
/** /**
* Settings user page controller. * Settings user page controller.
*/ */
App.controller('SettingsUser', function($scope, $state, Restangular) { angular.module('docs').controller('SettingsUser', function($scope, $state, Restangular) {
/** /**
* Load users from server. * Load users from server.
*/ */

View File

@ -3,7 +3,7 @@
/** /**
* Settings user edition page controller. * Settings user edition page controller.
*/ */
App.controller('SettingsUserEdit', function($scope, $dialog, $state, $stateParams, Restangular) { angular.module('docs').controller('SettingsUserEdit', function($scope, $dialog, $state, $stateParams, Restangular) {
/** /**
* Returns true if in edit mode (false in add mode). * Returns true if in edit mode (false in add mode).
*/ */

View File

@ -3,7 +3,7 @@
/** /**
* Tag controller. * Tag controller.
*/ */
App.controller('Tag', function($scope, $dialog, $state, Tag, Restangular) { angular.module('docs').controller('Tag', function($scope, $dialog, $state, Tag, Restangular) {
$scope.tag = { name: '', color: '#3a87ad' }; $scope.tag = { name: '', color: '#3a87ad' };
// Retrieve tags // Retrieve tags

View File

@ -3,7 +3,7 @@
/** /**
* File upload directive. * File upload directive.
*/ */
App.directive('file', function() { angular.module('docs').directive('file', function() {
return { return {
restrict: 'E', restrict: 'E',
template: '<input type="file" />', template: '<input type="file" />',

View File

@ -4,7 +4,7 @@
* Inline edition directive. * Inline edition directive.
* Thanks to http://jsfiddle.net/joshdmiller/NDFHg/ * Thanks to http://jsfiddle.net/joshdmiller/NDFHg/
*/ */
App.directive('inlineEdit', function() { angular.module('docs').directive('inlineEdit', function() {
return { return {
restrict: 'E', restrict: 'E',
scope: { scope: {

View File

@ -3,7 +3,7 @@
/** /**
* Tag selection directive. * Tag selection directive.
*/ */
App.directive('selectTag', function() { angular.module('docs').directive('selectTag', function() {
return { return {
restrict: 'E', restrict: 'E',
templateUrl: 'partial/docs/directive.selecttag.html', templateUrl: 'partial/docs/directive.selecttag.html',

View File

@ -3,7 +3,7 @@
/** /**
* Filter converting new lines in <br /> * Filter converting new lines in <br />
*/ */
App.filter('newline', function() { angular.module('docs').filter('newline', function() {
return function(text) { return function(text) {
if (!text) { if (!text) {
return ''; return '';

View File

@ -3,7 +3,7 @@
/** /**
* Filter shortening text in one letter uppercase. * Filter shortening text in one letter uppercase.
*/ */
App.filter('shorten', function() { angular.module('docs').filter('shorten', function() {
return function(text) { return function(text) {
if (!text) { if (!text) {
return ''; return '';

View File

@ -3,7 +3,7 @@
/** /**
* Tag service. * Tag service.
*/ */
App.factory('Tag', function(Restangular) { angular.module('docs').factory('Tag', function(Restangular) {
var tags = null; var tags = null;
return { return {

View File

@ -3,7 +3,7 @@
/** /**
* User service. * User service.
*/ */
App.factory('User', function(Restangular) { angular.module('docs').factory('User', function(Restangular) {
var userInfo = null; var userInfo = null;
return { return {

View File

@ -3,7 +3,7 @@
/** /**
* Share application. * Share application.
*/ */
var App = angular.module('share', angular.module('share',
// Dependencies // Dependencies
['ui.router', 'ui.bootstrap', 'restangular', 'ngSanitize', 'ngTouch'] ['ui.router', 'ui.bootstrap', 'restangular', 'ngSanitize', 'ngTouch']
) )
@ -50,7 +50,7 @@ var App = angular.module('share',
}); });
// Configuring Restangular // Configuring Restangular
RestangularProvider.setBaseUrl('api'); RestangularProvider.setBaseUrl('../api');
// Configuring $http to act like jQuery.ajax // Configuring $http to act like jQuery.ajax
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

View File

@ -0,0 +1,83 @@
'use strict';
/**
* File modal view controller.
*/
angular.module('share').controller('FileModalView', function($rootScope, $modalInstance, $scope, $state, $stateParams, Restangular) {
// Load files
Restangular.one('file').getList('list', { id: $stateParams.documentId, share: $stateParams.shareId }).then(function(data) {
$scope.files = data.files;
// Search current file
_.each($scope.files, function(value) {
if (value.id == $stateParams.fileId) {
$scope.file = value;
}
});
});
/**
* Navigate to the next file.
*/
$scope.nextFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var next = $scope.files[key + 1];
if (next) {
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: next.id });
}
}
});
};
/**
* Navigate to the previous file.
*/
$scope.previousFile = function() {
_.each($scope.files, function(value, key) {
if (value.id == $stateParams.fileId) {
var previous = $scope.files[key - 1];
if (previous) {
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: previous.id });
}
}
});
};
/**
* Open the file in a new window.
*/
$scope.openFile = function() {
window.open('../api/file/' + $stateParams.fileId + '/data?share=' + $stateParams.shareId);
};
/**
* Print the file.
*/
$scope.printFile = function() {
var popup = window.open('../api/file/' + $stateParams.fileId + '/data', '_blank');
popup.onload = function () {
popup.print();
popup.close();
}
};
/**
* Close the file preview.
*/
$scope.closeFile = function () {
$modalInstance.dismiss();
};
// Close the modal when the user exits this state
var off = $rootScope.$on('$stateChangeStart', function(event, toState){
if (!$modalInstance.closed) {
if (toState.name == 'share.file') {
$modalInstance.close();
} else {
$modalInstance.dismiss();
}
}
off();
});
});

View File

@ -0,0 +1,21 @@
'use strict';
/**
* File view controller.
*/
angular.module('share').controller('FileView', function($modal, $state, $stateParams) {
var modal = $modal.open({
windowClass: 'modal modal-fileview',
templateUrl: 'partial/share/file.view.html',
controller: 'FileModalView'
});
// Returns to share view on file close
modal.closed = false;
modal.result.then(function() {
modal.closed = true;
},function(result) {
modal.closed = true;
$state.transitionTo('share', { documentId: $stateParams.documentId, shareId: $stateParams.shareId });
});
});

View File

@ -0,0 +1,7 @@
'use strict';
/**
* Main controller.
*/
angular.module('share').controller('Main', function() {
});

View File

@ -3,7 +3,7 @@
/** /**
* Share controller. * Share controller.
*/ */
App.controller('Share', function($scope, $state, $stateParams, Restangular) { angular.module('share').controller('Share', function($scope, $state, $stateParams, Restangular) {
// Load document // Load document
Restangular.one('document', $stateParams.documentId).get({ share: $stateParams.shareId }) Restangular.one('document', $stateParams.documentId).get({ share: $stateParams.shareId })
.then(function (data) { .then(function (data) {

View File

@ -0,0 +1,13 @@
'use strict';
/**
* Filter converting new lines in <br />
*/
angular.module('share').filter('newline', function() {
return function(text) {
if (!text) {
return '';
}
return text.replace(/\n/g, '<br/>');
}
})

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 599 B

View File

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 545 B

View File

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 420 B

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 506 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -5,9 +5,12 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="favicon.png" /> <link rel="shortcut icon" href="favicon.png" />
<!-- ref:css style/style.min.css -->
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" /> <link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
<link rel="stylesheet" href="style/colorpicker.css" type="text/css" /> <link rel="stylesheet" href="style/colorpicker.css" type="text/css" />
<link rel="stylesheet/less" href="style/main.less" type="text/css" /> <link rel="stylesheet/less" href="style/main.less" type="text/css" />
<!-- endref -->
<!-- ref:remove -->
<script> <script>
less = { less = {
env: 'development', // or "production" env: 'development', // or "production"
@ -17,14 +20,16 @@
dumpLineNumbers: 'all' // or "mediaQuery" or "comments" dumpLineNumbers: 'all' // or "mediaQuery" or "comments"
}; };
</script> </script>
<!-- endref -->
<!-- ref:js docs.min.js -->
<script src="lib/jquery.js" type="text/javascript"></script> <script src="lib/jquery.js" type="text/javascript"></script>
<script src="lib/jquery.ui.js" type="text/javascript"></script> <script src="lib/jquery.ui.js" type="text/javascript"></script>
<script src="lib/less.js" type="text/javascript"></script> <script src="lib/less.js" type="text/javascript"></script>
<script src="lib/underscore.js" type="text/javascript"></script> <script src="lib/underscore.js" type="text/javascript"></script>
<script src="lib/colorpicker.js" type="text/javascript"></script> <script src="lib/colorpicker.js" type="text/javascript"></script>
<script src="lib/angular.js" type="text/javascript"></script> <script src="lib/angular.js" type="text/javascript"></script>
<script src="lib/angular-sanitize.js" type="text/javascript"></script> <script src="lib/angular.sanitize.js" type="text/javascript"></script>
<script src="lib/angular-touch.js" type="text/javascript"></script> <script src="lib/angular.touch.js" type="text/javascript"></script>
<script src="lib/angular.ui-router.js" type="text/javascript"></script> <script src="lib/angular.ui-router.js" type="text/javascript"></script>
<script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script> <script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script>
<script src="lib/angular.ui-utils.js" type="text/javascript"></script> <script src="lib/angular.ui-utils.js" type="text/javascript"></script>
@ -38,6 +43,7 @@
<script src="app/docs/controller/DocumentEdit.js" type="text/javascript"></script> <script src="app/docs/controller/DocumentEdit.js" type="text/javascript"></script>
<script src="app/docs/controller/DocumentView.js" type="text/javascript"></script> <script src="app/docs/controller/DocumentView.js" type="text/javascript"></script>
<script src="app/docs/controller/FileView.js" type="text/javascript"></script> <script src="app/docs/controller/FileView.js" type="text/javascript"></script>
<script src="app/docs/controller/FileModalView.js" type="text/javascript"></script>
<script src="app/docs/controller/Login.js" type="text/javascript"></script> <script src="app/docs/controller/Login.js" type="text/javascript"></script>
<script src="app/docs/controller/Tag.js" type="text/javascript"></script> <script src="app/docs/controller/Tag.js" type="text/javascript"></script>
<script src="app/docs/controller/Navigation.js" type="text/javascript"></script> <script src="app/docs/controller/Navigation.js" type="text/javascript"></script>
@ -55,6 +61,7 @@
<script src="app/docs/directive/File.js" type="text/javascript"></script> <script src="app/docs/directive/File.js" type="text/javascript"></script>
<script src="app/docs/directive/SelectTag.js" type="text/javascript"></script> <script src="app/docs/directive/SelectTag.js" type="text/javascript"></script>
<script src="app/docs/directive/InlineEdit.js" type="text/javascript"></script> <script src="app/docs/directive/InlineEdit.js" type="text/javascript"></script>
<!-- endref -->
</head> </head>
<body> <body>
<nav class="navbar navbar-default" role="navigation" ng-controller="Navigation"> <nav class="navbar navbar-default" role="navigation" ng-controller="Navigation">

View File

@ -2046,7 +2046,7 @@
* - `injector()` - retrieves the injector of the current element or its parent. * - `injector()` - retrieves the injector of the current element or its parent.
* - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current
* element or its parent. * element or its parent.
* - `isolateScope()` - retrieves an isolate {@link api/ng.$rootScope.Scope scope} if one is attached directly to the * - `isolateScope()` - retrieves an isolate {@link ../api/ng.$rootScope.Scope scope} if one is attached directly to the
* current element. This getter should be used only on elements that contain a directive which starts a new isolate * current element. This getter should be used only on elements that contain a directive which starts a new isolate
* scope. Calling `scope()` on this element always returns the original non-isolate scope. * scope. Calling `scope()` on this element always returns the original non-isolate scope.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
@ -2136,7 +2136,7 @@
} }
if (!(this instanceof JQLite)) { if (!(this instanceof JQLite)) {
if (isString(element) && element.charAt(0) != '<') { if (isString(element) && element.charAt(0) != '<') {
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/../api/angular.element');
} }
return new JQLite(element); return new JQLite(element);
} }
@ -4799,7 +4799,7 @@
* *
* ### Directive Definition Object * ### Directive Definition Object
* *
* The directive definition object provides instructions to the {@link api/ng.$compile * The directive definition object provides instructions to the {@link ../api/ng.$compile
* compiler}. The attributes are: * compiler}. The attributes are:
* *
* #### `priority` * #### `priority`
@ -4922,7 +4922,7 @@
* You can specify `templateUrl` as a string representing the URL or as a function which takes two * You can specify `templateUrl` as a string representing the URL or as a function which takes two
* arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
* a string value representing the url. In either case, the template URL is passed through {@link * a string value representing the url. In either case, the template URL is passed through {@link
* api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl}. * ../api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl}.
* *
* *
* #### `replace` * #### `replace`
@ -4934,7 +4934,7 @@
* *
* #### `transclude` * #### `transclude`
* compile the content of the element and make it available to the directive. * compile the content of the element and make it available to the directive.
* Typically used with {@link api/ng.directive:ngTransclude * Typically used with {@link ../api/ng.directive:ngTransclude
* ngTransclude}. The advantage of transclusion is that the linking function receives a * ngTransclude}. The advantage of transclusion is that the linking function receives a
* transclusion function which is pre-bound to the correct scope. In a typical setup the widget * transclusion function which is pre-bound to the correct scope. In a typical setup the widget
* creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate` * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
@ -4954,8 +4954,8 @@
* The compile function deals with transforming the template DOM. Since most directives do not do * The compile function deals with transforming the template DOM. Since most directives do not do
* template transformation, it is not used often. Examples that require compile functions are * template transformation, it is not used often. Examples that require compile functions are
* directives that transform template DOM, such as {@link * directives that transform template DOM, such as {@link
* api/ng.directive:ngRepeat ngRepeat}, or load the contents * ../api/ng.directive:ngRepeat ngRepeat}, or load the contents
* asynchronously, such as {@link api/ngRoute.directive:ngView ngView}. The * asynchronously, such as {@link ../api/ngRoute.directive:ngView ngView}. The
* compile function takes the following arguments. * compile function takes the following arguments.
* *
* * `tElement` - template element - The element where the directive has been declared. It is * * `tElement` - template element - The element where the directive has been declared. It is
@ -5000,8 +5000,8 @@
* executed after the template has been cloned. This is where most of the directive logic will be * executed after the template has been cloned. This is where most of the directive logic will be
* put. * put.
* *
* * `scope` - {@link api/ng.$rootScope.Scope Scope} - The scope to be used by the * * `scope` - {@link ../api/ng.$rootScope.Scope Scope} - The scope to be used by the
* directive for registering {@link api/ng.$rootScope.Scope#methods_$watch watches}. * directive for registering {@link ../api/ng.$rootScope.Scope#methods_$watch watches}.
* *
* * `iElement` - instance element - The element where the directive is to be used. It is safe to * * `iElement` - instance element - The element where the directive is to be used. It is safe to
* manipulate the children of the element only in `postLink` function since the children have * manipulate the children of the element only in `postLink` function since the children have
@ -5032,7 +5032,7 @@
* <a name="Attributes"></a> * <a name="Attributes"></a>
* ### Attributes * ### Attributes
* *
* The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the * The {@link ../api/ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
* `link()` or `compile()` functions. It has a variety of uses. * `link()` or `compile()` functions. It has a variety of uses.
* *
* accessing *Normalized attribute names:* * accessing *Normalized attribute names:*
@ -8218,7 +8218,7 @@
throw $interpolateMinErr('noconcat', throw $interpolateMinErr('noconcat',
"Error while interpolating: {0}\nStrict Contextual Escaping disallows " + "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
"interpolations that concatenate multiple expressions when a trusted value is " + "interpolations that concatenate multiple expressions when a trusted value is " +
"required. See http://docs.angularjs.org/api/ng.$sce", text); "required. See http://docs.angularjs.org/../api/ng.$sce", text);
} }
if (!mustHaveExpression || hasInterpolation) { if (!mustHaveExpression || hasInterpolation) {
@ -13104,7 +13104,7 @@
throw $sceMinErr('iequirks', throw $sceMinErr('iequirks',
'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' + 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); 'document. See http://docs.angularjs.org/../api/ng.$sce for more information.');
} }
var sce = copy(SCE_CONTEXTS); var sce = copy(SCE_CONTEXTS);
@ -13642,7 +13642,7 @@
* Parsing means that the anchor node's host, hostname, protocol, port, pathname and related * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
* properties are all populated to reflect the normalized URL. This approach has wide * properties are all populated to reflect the normalized URL. This approach has wide
* compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html * http://www.aptana.com/reference/html/../api/HTMLAnchorElement.html
* *
* Implementation Notes for IE * Implementation Notes for IE
* --------------------------- * ---------------------------
@ -13662,7 +13662,7 @@
* *
* References: * References:
* http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html * http://www.aptana.com/reference/html/../api/HTMLAnchorElement.html
* http://url.spec.whatwg.org/#urlutils * http://url.spec.whatwg.org/#urlutils
* https://github.com/angular/angular.js/pull/2902 * https://github.com/angular/angular.js/pull/2902
* http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
@ -18574,7 +18574,7 @@
* *
* <div class="alert alert-error"> * <div class="alert alert-error">
* The only appropriate use of `ngInit` is for aliasing special properties of * The only appropriate use of `ngInit` is for aliasing special properties of
* {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you * {@link ../api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
* should use {@link guide/controller controllers} rather than `ngInit` * should use {@link guide/controller controllers} rather than `ngInit`
* to initialize values on a scope. * to initialize values on a scope.
* </div> * </div>
@ -18889,7 +18889,7 @@
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
* *
* Creating aliases for these properties is possible with {@link api/ng.directive:ngInit `ngInit`}. * Creating aliases for these properties is possible with {@link ../api/ng.directive:ngInit `ngInit`}.
* This may be useful when, for instance, nesting ngRepeats. * This may be useful when, for instance, nesting ngRepeats.
* *
* # Special repeat start and end points * # Special repeat start and end points
@ -19870,9 +19870,9 @@
* @restrict E * @restrict E
* *
* @description * @description
* Load the content of a `<script>` element into {@link api/ng.$templateCache `$templateCache`}, so that the * Load the content of a `<script>` element into {@link ../api/ng.$templateCache `$templateCache`}, so that the
* template can be used by {@link api/ng.directive:ngInclude `ngInclude`}, * template can be used by {@link ../api/ng.directive:ngInclude `ngInclude`},
* {@link api/ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the * {@link ../api/ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
* `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
* assigned through the element's `id`, which can then be used as a directive's `templateUrl`. * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
* *

View File

@ -31,7 +31,7 @@
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files"> <div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files">
<div class="thumbnail"> <div class="thumbnail">
<a ng-click="openFile(file)"> <a ng-click="openFile(file)">
<img class="thumbnail-file" ng-src="api/file/{{ file.id }}/data?size=thumb" tooltip="{{ file.mimetype }}" tooltip-placement="top" /> <img class="thumbnail-file" ng-src="../api/file/{{ file.id }}/data?size=thumb" tooltip="{{ file.mimetype }}" tooltip-placement="top" />
</a> </a>
<div class="caption"> <div class="caption">
<div class="pull-left"> <div class="pull-left">

View File

@ -22,5 +22,5 @@
<div class="text-center" ng-if="$stateParams.fileId"> <div class="text-center" ng-if="$stateParams.fileId">
<img ng-src="api/file/{{ $stateParams.fileId }}/data?size=web" /> <img ng-src="../api/file/{{ $stateParams.fileId }}/data?size=web" />
</div> </div>

View File

@ -22,5 +22,5 @@
<div class="text-center" ng-if="$stateParams.fileId"> <div class="text-center" ng-if="$stateParams.fileId">
<img ng-src="api/file/{{ $stateParams.fileId }}/data?size=web&share={{ $stateParams.shareId }}" /> <img ng-src="../api/file/{{ $stateParams.fileId }}/data?size=web&share={{ $stateParams.shareId }}" />
</div> </div>

View File

@ -13,7 +13,7 @@
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files"> <div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files">
<div class="thumbnail"> <div class="thumbnail">
<a ng-click="openFile(file)"> <a ng-click="openFile(file)">
<img class="thumbnail-file" ng-src="api/file/{{ file.id }}/data?size=thumb&share={{ $stateParams.shareId }}" tooltip="{{ file.mimetype }}" tooltip-placement="top" /> <img class="thumbnail-file" ng-src="../api/file/{{ file.id }}/data?size=thumb&share={{ $stateParams.shareId }}" tooltip="{{ file.mimetype }}" tooltip-placement="top" />
</a> </a>
</div> </div>
</div> </div>

View File

@ -5,8 +5,11 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="favicon.png" /> <link rel="shortcut icon" href="favicon.png" />
<!-- ref:css style/style.min.css -->
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" /> <link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
<link rel="stylesheet/less" href="style/main.less" type="text/css" /> <link rel="stylesheet/less" href="style/main.less" type="text/css" />
<!-- endref -->
<!-- ref:remove -->
<script> <script>
less = { less = {
env: 'development', // or "production" env: 'development', // or "production"
@ -16,12 +19,14 @@
dumpLineNumbers: 'all' // or "mediaQuery" or "comments" dumpLineNumbers: 'all' // or "mediaQuery" or "comments"
}; };
</script> </script>
<!-- endref -->
<!-- ref:js share.min.js -->
<script src="lib/jquery.js" type="text/javascript"></script> <script src="lib/jquery.js" type="text/javascript"></script>
<script src="lib/less.js" type="text/javascript"></script> <script src="lib/less.js" type="text/javascript"></script>
<script src="lib/underscore.js" type="text/javascript"></script> <script src="lib/underscore.js" type="text/javascript"></script>
<script src="lib/angular.js" type="text/javascript"></script> <script src="lib/angular.js" type="text/javascript"></script>
<script src="lib/angular-sanitize.js" type="text/javascript"></script> <script src="lib/angular.sanitize.js" type="text/javascript"></script>
<script src="lib/angular-touch.js" type="text/javascript"></script> <script src="lib/angular.touch.js" type="text/javascript"></script>
<script src="lib/angular.ui-router.js" type="text/javascript"></script> <script src="lib/angular.ui-router.js" type="text/javascript"></script>
<script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script> <script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script>
<script src="lib/angular.ui-utils.js" type="text/javascript"></script> <script src="lib/angular.ui-utils.js" type="text/javascript"></script>
@ -30,7 +35,9 @@
<script src="app/share/controller/Main.js" type="text/javascript"></script> <script src="app/share/controller/Main.js" type="text/javascript"></script>
<script src="app/share/controller/Share.js" type="text/javascript"></script> <script src="app/share/controller/Share.js" type="text/javascript"></script>
<script src="app/share/controller/FileView.js" type="text/javascript"></script> <script src="app/share/controller/FileView.js" type="text/javascript"></script>
<script src="app/docs/filter/Newline.js" type="text/javascript"></script> <script src="app/share/controller/FileModalView.js" type="text/javascript"></script>
<script src="app/share/filter/Newline.js" type="text/javascript"></script>
<!-- endref -->
</head> </head>
<body> <body>
<div class="navbar navbar-default" role="navigation"> <div class="navbar navbar-default" role="navigation">