Merge branch '1.11.x' into question_degre_certitude_remote

pull/2485/head
Julio Montoya 8 years ago committed by GitHub
commit 9e89ca4253
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .codeclimate.yml
  2. 3
      .htaccess
  3. 18
      .travis.yml
  4. 9
      app/Migrations/Schema/V111/Version20161028123400.php
  5. 6
      app/Resources/public/assets/bootstrap/Gemfile
  6. 43
      app/Resources/public/assets/bootstrap/Gemfile.lock
  7. 511
      app/Resources/public/assets/bootstrap/Gruntfile.js
  8. 7
      app/Resources/public/assets/bootstrap/grunt/.jshintrc
  9. 30
      app/Resources/public/assets/bootstrap/grunt/bs-commonjs-generator.js
  10. 42
      app/Resources/public/assets/bootstrap/grunt/bs-glyphicons-data-generator.js
  11. 237
      app/Resources/public/assets/bootstrap/grunt/bs-lessdoc-parser.js
  12. 44
      app/Resources/public/assets/bootstrap/grunt/bs-raw-files-generator.js
  13. 109
      app/Resources/public/assets/bootstrap/grunt/change-version.js
  14. 46
      app/Resources/public/assets/bootstrap/grunt/configBridge.json
  15. 2679
      app/Resources/public/assets/bootstrap/grunt/npm-shrinkwrap.json
  16. 82
      app/Resources/public/assets/bootstrap/grunt/sauce_browsers.yml
  17. 8
      app/Resources/public/assets/bootstrap/nuget/MyGet.ps1
  18. 28
      app/Resources/public/assets/bootstrap/nuget/bootstrap.less.nuspec
  19. 28
      app/Resources/public/assets/bootstrap/nuget/bootstrap.nuspec
  20. 32
      app/Resources/public/assets/bootstrap/package.js
  21. 89
      app/Resources/public/assets/bootstrap/package.json
  22. 19
      app/Resources/public/assets/jqueryui-touch-punch/.bower.json
  23. 41
      app/Resources/public/assets/jqueryui-touch-punch/README.md
  24. 10
      app/Resources/public/assets/jqueryui-touch-punch/bower.json
  25. 180
      app/Resources/public/assets/jqueryui-touch-punch/jquery.ui.touch-punch.js
  26. 11
      app/Resources/public/assets/jqueryui-touch-punch/jquery.ui.touch-punch.min.js
  27. 8
      app/Resources/public/assets/mediaelement/.bower.json
  28. 90
      app/Resources/public/assets/mediaelement/build/lang/ms.js
  29. 32
      app/Resources/public/assets/mediaelement/build/mediaelement-and-player.js
  30. 2
      app/Resources/public/assets/mediaelement/build/mediaelement-and-player.min.js
  31. 4
      app/Resources/public/assets/mediaelement/build/mediaelement.js
  32. 2
      app/Resources/public/assets/mediaelement/build/mediaelement.min.js
  33. 2
      app/Resources/public/assets/mediaelement/build/renderers/dailymotion.min.js
  34. 2
      app/Resources/public/assets/mediaelement/build/renderers/facebook.js
  35. 2
      app/Resources/public/assets/mediaelement/build/renderers/facebook.min.js
  36. 2
      app/Resources/public/assets/mediaelement/build/renderers/soundcloud.min.js
  37. 2
      app/Resources/public/assets/mediaelement/build/renderers/twitch.min.js
  38. 6
      app/Resources/public/assets/mediaelement/build/renderers/vimeo.js
  39. 2
      app/Resources/public/assets/mediaelement/build/renderers/vimeo.min.js
  40. 20
      app/Resources/public/assets/mediaelement/changelog.md
  41. 65
      app/Resources/public/assets/mediaelement/plugins/speed/speed-i18n.js
  42. 94
      app/Resources/public/assets/mediaelement/plugins/speed/speed.css
  43. 174
      app/Resources/public/assets/mediaelement/plugins/speed/speed.js
  44. 1
      app/Resources/public/assets/mediaelement/plugins/speed/speed.min.css
  45. 12
      app/Resources/public/assets/mediaelement/plugins/speed/speed.min.js
  46. 26
      app/Resources/public/assets/multiselect-two-sides/.bower.json
  47. 3
      app/Resources/public/assets/multiselect-two-sides/.gitignore
  48. 35
      app/Resources/public/assets/multiselect-two-sides/CHANGELOG.md
  49. 22
      app/Resources/public/assets/multiselect-two-sides/LICENSE
  50. 66
      app/Resources/public/assets/multiselect-two-sides/README.md
  51. 17
      app/Resources/public/assets/multiselect-two-sides/bower.json
  52. 26
      app/Resources/public/assets/multiselect-two-sides/css/style.css
  53. 813
      app/Resources/public/assets/multiselect-two-sides/dist/js/multiselect.js
  54. 11
      app/Resources/public/assets/multiselect-two-sides/dist/js/multiselect.min.js
  55. 481
      app/Resources/public/assets/multiselect-two-sides/index.html
  56. 4313
      app/Resources/public/assets/multiselect-two-sides/package-lock.json
  57. 34
      app/Resources/public/assets/multiselect-two-sides/package.json
  58. 5382
      app/Resources/public/css/base.css
  59. 251
      app/Resources/public/css/chat.css
  60. 36
      app/Resources/public/css/document.css
  61. 19
      app/Resources/public/css/editor.css
  62. 216
      app/Resources/public/css/editor_content.css
  63. 36
      app/Resources/public/css/print.css
  64. 627
      app/Resources/public/css/scorm.css
  65. 3
      app/Resources/public/css/themes/chamilo/default.css
  66. 1
      app/config/assetic.yml
  67. 33
      app/config/course_info.conf.dist.php
  68. 5
      bower.json
  69. 2
      certificates/index.php
  70. 7
      custompages/index-unlogged-dist.php
  71. 3
      custompages/lostpassword-dist.php
  72. 9
      documentation/changelog.html
  73. 96
      documentation/installation_guide.html
  74. 1504
      documentation/installation_guide_es_ES.html
  75. 101
      documentation/installation_guide_fr_FR.html
  76. 4
      documentation/optimization.html
  77. 37
      documentation/security.html
  78. 39
      index.php
  79. 9
      main/admin/add_drh_to_user.php
  80. 6
      main/admin/configure_homepage.php
  81. 52
      main/admin/course_edit.php
  82. 3
      main/admin/course_list.php
  83. 4
      main/admin/languages.php
  84. 9
      main/admin/settings.lib.php
  85. 6
      main/admin/special_exports.php
  86. 143
      main/admin/statistics/index.php
  87. 2
      main/admin/sub_language.php
  88. 2
      main/admin/teacher_time_report.php
  89. 32
      main/admin/user_add.php
  90. 21
      main/admin/user_edit.php
  91. 4
      main/admin/user_import.php
  92. 20
      main/admin/user_information.php
  93. 98
      main/admin/user_list.php
  94. 6
      main/admin/user_move_stats.php
  95. 2
      main/admin/user_update_import.php
  96. 58
      main/announcements/announcements.php
  97. 2
      main/attendance/attendance_add.php
  98. 2
      main/attendance/attendance_edit.php
  99. 5
      main/auth/courses.php
  100. 26
      main/auth/courses_categories.php
  101. Some files were not shown because too many files have changed in this diff Show More

@ -34,7 +34,7 @@ plugins:
phpcodesniffer:
enabled: true
config:
standard: "Symfony"
standard: "Symfony2"
file_extensions: "php"
checks:
PSR1 Classes ClassDeclaration MissingNamespace:

@ -39,6 +39,9 @@ RewriteRule ^courses/([^/]+)/(.*)$ app/courses/$1/$2 [QSA,L]
# About session
RewriteRule ^session/(\d{1,})/about/?$ main/session/about.php?session_id=$1 [L]
# About course
RewriteRule ^course/(\d{1,})/about/?$ main/course_info/about.php?course_id=$1 [L]
# Issued individual badge friendly URL
RewriteRule ^badge/(\d{1,}) main/badge/issued.php?issue=$1 [L]

@ -1,3 +1,5 @@
dist: trusty
sudo: required
language: php
addons:
@ -12,14 +14,10 @@ addons:
services:
- mysql
sudo: required
dist: trusty
cache:
directories:
- $HOME/.composer/cache/files
php:
#- 5.4 // Removed because of Chash dependencies on PHP 5.5
- 5.5
- 5.6
- 7.0
@ -55,7 +53,6 @@ before_install:
- java -version
- sudo apt-get install oracle-java8-installer
- java -version
#- java -jar selenium-server-standalone-3.7.1.jar -log selenium.log > /dev/null &
- java -jar selenium-server-standalone-3.1.0.jar -log selenium.log > /dev/null &
- nohup bash -c "webdriver-manager start 2>&1 &"
- sleep 10
@ -67,15 +64,14 @@ before_install:
- mysqld --version
- apache2 -v
- php -v
- php -ini | grep memory_limit
- sudo cat /etc/hosts
- phpenv config-add tests/travis/php-config.ini
# Install Chash, a database, and then install Chamilo
- git clone https://github.com/chamilo/chash
- cd chash
- git checkout v0.1.1
- composer update
- composer install
- php -d phar.readonly=0 createPhar.php
- chmod +x chash.phar
- sudo mv chash.phar /usr/local/bin/chash
@ -84,7 +80,7 @@ before_install:
- cd $TRAVIS_BUILD_DIR
- pwd
# Install vendors
- composer update
- travis_wait 30 composer install
# Install chamilo
- php -d date.timezone="Europe/Paris" $TRAVIS_BUILD_DIR/chash/chash.php chash:chamilo_install $CHAMILO_VERSION $TRAVIS_BUILD_DIR --no-interaction --sitename="Chamilo" --site_url="http://$VHOST_URL/" --institution="Chamilo" --institution_url="https://chamilo.org" --encrypt_method="sha1" --firstname="John" --lastname="Doe" --language="english" --driver="pdo_mysql" --host="localhost" --port="3306" --dbname="chamilo" --dbuser="root" --permissions_for_new_directories="0777" --permissions_for_new_files="0666" --linux-user="www-data" --linux-group="www-data" --username="admin" --password="admin" --email="admin@example.com" --phone="555-5555"
- sudo chmod -R 777 app/cache app/logs app/courses app/upload
@ -97,11 +93,13 @@ before_install:
- sudo chown -R 775 $TRAVIS_BUILD_DIR
- sudo chmod +x /home/travis/build
- sudo service apache2 restart
- curl $VHOST_URL
#- curl $VHOST_URL
script:
- whereis google-chrome-stable
- google-chrome-stable --version
- whereis chromedriver
- chromedriver --version
- cd tests/behat
- pwd
- travis_wait 45 ../../vendor/behat/behat/bin/behat -v

@ -33,12 +33,17 @@ class Version20161028123400 extends AbstractMigrationChamilo
$table = $schema->getTable('personal_agenda');
if ($table->hasIndex('id')) {
$this->addSql('ALTER TABLE personal_agenda DROP INDEX id');
$this->addSql('ALTER TABLE personal_agenda DROP INDEX idx_personal_agenda_user');
$this->addSql('ALTER TABLE personal_agenda DROP INDEX idx_personal_agenda_parent');
$this->addSql('ALTER TABLE personal_agenda modify id INT NOT NULL');
$table->dropIndex('id');
if ($table->hasPrimaryKey()) {
$table->dropPrimaryKey();
$this->addSql('ALTER TABLE personal_agenda drop primary key ');
}
$this->addSql('ALTER TABLE personal_agenda CHANGE id id INT AUTO_INCREMENT NOT NULL PRIMARY KEY');
$this->addSql('CREATE INDEX idx_personal_agenda_user ON personal_agenda (user)');
$this->addSql('CREATE INDEX idx_personal_agenda_parent ON personal_agenda (parent_event_id)');
}
}

@ -1,6 +0,0 @@
source 'https://rubygems.org'
group :development, :test do
gem 'jekyll', '~> 3.1.2'
gem 'jekyll-sitemap', '~> 0.11.0'
end

@ -1,43 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.4.0)
colorator (0.1)
ffi (1.9.14-x64-mingw32)
jekyll (3.1.6)
colorator (~> 0.1)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 1.1)
kramdown (~> 1.3)
liquid (~> 3.0)
mercenary (~> 0.3.3)
rouge (~> 1.7)
safe_yaml (~> 1.0)
jekyll-sass-converter (1.4.0)
sass (~> 3.4)
jekyll-sitemap (0.11.0)
addressable (~> 2.4.0)
jekyll-watch (1.4.0)
listen (~> 3.0, < 3.1)
kramdown (1.11.1)
liquid (3.0.6)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
mercenary (0.3.6)
rb-fsevent (0.9.7)
rb-inotify (0.9.7)
ffi (>= 0.5.0)
rouge (1.11.1)
safe_yaml (1.0.4)
sass (3.4.22)
PLATFORMS
x64-mingw32
DEPENDENCIES
jekyll (~> 3.1.2)
jekyll-sitemap (~> 0.11.0)
BUNDLED WITH
1.12.5

@ -1,511 +0,0 @@
/*!
* Bootstrap's Gruntfile
* http://getbootstrap.com
* Copyright 2013-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
module.exports = function (grunt) {
'use strict';
// Force use of Unix newlines
grunt.util.linefeed = '\n';
RegExp.quote = function (string) {
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
};
var fs = require('fs');
var path = require('path');
var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');
var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
var getLessVarsData = function () {
var filePath = path.join(__dirname, 'less/variables.less');
var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
var parser = new BsLessdocParser(fileContent);
return { sections: parser.parseFile() };
};
var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
Object.keys(configBridge.paths).forEach(function (key) {
configBridge.paths[key].forEach(function (val, i, arr) {
arr[i] = path.join('./docs/assets', val);
});
});
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*!\n' +
' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
' * Licensed under the <%= pkg.license %> license\n' +
' */\n',
jqueryCheck: configBridge.config.jqueryCheck.join('\n'),
jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),
// Task configuration.
clean: {
dist: 'dist',
docs: 'docs/dist'
},
jshint: {
options: {
jshintrc: 'js/.jshintrc'
},
grunt: {
options: {
jshintrc: 'grunt/.jshintrc'
},
src: ['Gruntfile.js', 'package.js', 'grunt/*.js']
},
core: {
src: 'js/*.js'
},
test: {
options: {
jshintrc: 'js/tests/unit/.jshintrc'
},
src: 'js/tests/unit/*.js'
},
assets: {
src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
}
},
jscs: {
options: {
config: 'js/.jscsrc'
},
grunt: {
src: '<%= jshint.grunt.src %>'
},
core: {
src: '<%= jshint.core.src %>'
},
test: {
src: '<%= jshint.test.src %>'
},
assets: {
options: {
requireCamelCaseOrUpperCaseIdentifiers: null
},
src: '<%= jshint.assets.src %>'
}
},
concat: {
options: {
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',
stripBanners: false
},
bootstrap: {
src: [
'js/transition.js',
'js/alert.js',
'js/button.js',
'js/carousel.js',
'js/collapse.js',
'js/dropdown.js',
'js/modal.js',
'js/tooltip.js',
'js/popover.js',
'js/scrollspy.js',
'js/tab.js',
'js/affix.js'
],
dest: 'dist/js/<%= pkg.name %>.js'
}
},
uglify: {
options: {
compress: {
warnings: false
},
mangle: true,
preserveComments: /^!|@preserve|@license|@cc_on/i
},
core: {
src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js'
},
customize: {
src: configBridge.paths.customizerJs,
dest: 'docs/assets/js/customize.min.js'
},
docsJs: {
src: configBridge.paths.docsJs,
dest: 'docs/assets/js/docs.min.js'
}
},
qunit: {
options: {
inject: 'js/tests/unit/phantom.js'
},
files: 'js/tests/index.html'
},
less: {
compileCore: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
},
src: 'less/bootstrap.less',
dest: 'dist/css/<%= pkg.name %>.css'
},
compileTheme: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>-theme.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'
},
src: 'less/theme.less',
dest: 'dist/css/<%= pkg.name %>-theme.css'
}
},
autoprefixer: {
options: {
browsers: configBridge.config.autoprefixerBrowsers
},
core: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>.css'
},
theme: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>-theme.css'
},
docs: {
src: ['docs/assets/css/src/docs.css']
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: ['**/*.css'],
dest: 'docs/examples/'
}
},
csslint: {
options: {
csslintrc: 'less/.csslintrc'
},
dist: [
'dist/css/bootstrap.css',
'dist/css/bootstrap-theme.css'
],
examples: [
'docs/examples/**/*.css'
],
docs: {
options: {
ids: false,
'overqualified-elements': false
},
src: 'docs/assets/css/src/docs.css'
}
},
cssmin: {
options: {
// TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
// and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
compatibility: 'ie8',
keepSpecialComments: '*',
sourceMap: true,
sourceMapInlineSources: true,
advanced: false
},
minifyCore: {
src: 'dist/css/<%= pkg.name %>.css',
dest: 'dist/css/<%= pkg.name %>.min.css'
},
minifyTheme: {
src: 'dist/css/<%= pkg.name %>-theme.css',
dest: 'dist/css/<%= pkg.name %>-theme.min.css'
},
docs: {
src: [
'docs/assets/css/ie10-viewport-bug-workaround.css',
'docs/assets/css/src/pygments-manni.css',
'docs/assets/css/src/docs.css'
],
dest: 'docs/assets/css/docs.min.css'
}
},
csscomb: {
options: {
config: 'less/.csscomb.json'
},
dist: {
expand: true,
cwd: 'dist/css/',
src: ['*.css', '!*.min.css'],
dest: 'dist/css/'
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: '**/*.css',
dest: 'docs/examples/'
},
docs: {
src: 'docs/assets/css/src/docs.css',
dest: 'docs/assets/css/src/docs.css'
}
},
copy: {
fonts: {
expand: true,
src: 'fonts/**',
dest: 'dist/'
},
docs: {
expand: true,
cwd: 'dist/',
src: [
'**/*'
],
dest: 'docs/dist/'
}
},
connect: {
server: {
options: {
port: 3000,
base: '.'
}
}
},
jekyll: {
options: {
bundleExec: true,
config: '_config.yml',
incremental: false
},
docs: {},
github: {
options: {
raw: 'github: true'
}
}
},
htmlmin: {
dist: {
options: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
conservativeCollapse: true,
decodeEntities: false,
minifyCSS: {
compatibility: 'ie8',
keepSpecialComments: 0
},
minifyJS: true,
minifyURLs: false,
processConditionalComments: true,
removeAttributeQuotes: true,
removeComments: true,
removeOptionalAttributes: true,
removeOptionalTags: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
removeTagWhitespace: false,
sortAttributes: true,
sortClassName: true
},
expand: true,
cwd: '_gh_pages',
dest: '_gh_pages',
src: [
'**/*.html',
'!examples/**/*.html'
]
}
},
pug: {
options: {
pretty: true,
data: getLessVarsData
},
customizerVars: {
src: 'docs/_pug/customizer-variables.pug',
dest: 'docs/_includes/customizer-variables.html'
},
customizerNav: {
src: 'docs/_pug/customizer-nav.pug',
dest: 'docs/_includes/nav/customize.html'
}
},
htmllint: {
options: {
ignore: [
'Attribute "autocomplete" not allowed on element "button" at this point.',
'Attribute "autocomplete" is only allowed when the input type is "color", "date", "datetime", "datetime-local", "email", "hidden", "month", "number", "password", "range", "search", "tel", "text", "time", "url", or "week".',
'Element "img" is missing required attribute "src".'
]
},
src: '_gh_pages/**/*.html'
},
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:core', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: 'less'
}
},
'saucelabs-qunit': {
all: {
options: {
build: process.env.TRAVIS_JOB_ID,
throttled: 10,
maxRetries: 3,
maxPollRetries: 4,
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
}
}
},
exec: {
npmUpdate: {
command: 'npm update'
}
},
compress: {
main: {
options: {
archive: 'bootstrap-<%= pkg.version %>-dist.zip',
mode: 'zip',
level: 9,
pretty: true
},
files: [
{
expand: true,
cwd: 'dist/',
src: ['**'],
dest: 'bootstrap-<%= pkg.version %>-dist'
}
]
}
}
});
// These plugins provide necessary tasks.
require('load-grunt-tasks')(grunt, { scope: 'devDependencies' });
require('time-grunt')(grunt);
// Docs HTML validation task
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
var runSubset = function (subset) {
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
};
var isUndefOrNonZero = function (val) {
return val === undefined || val !== '0';
};
// Test task.
var testSubtasks = [];
// Skip core tests if running a different subset of the test suite
if (runSubset('core') &&
// Skip core tests if this is a Savage build
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']);
}
// Skip HTML validation if running a different subset of the test suite
if (runSubset('validate-html') &&
// Skip HTML5 validator on Travis when [skip validator] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
testSubtasks.push('validate-html');
}
// Only run Sauce Labs tests if there's a Sauce access key
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
// Skip Sauce if running a different subset of the test suite
runSubset('sauce-js-unit') &&
// Skip Sauce on Travis when [skip sauce] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
testSubtasks.push('connect');
testSubtasks.push('saucelabs-qunit');
}
grunt.registerTask('test', testSubtasks);
grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
// JS distribution task.
grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);
// CSS distribution task.
grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);
grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']);
// Full distribution task.
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);
// Default task.
grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']);
grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); });
// task for building customizer
grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
grunt.registerTask('build-customizer-html', 'pug');
grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
var banner = grunt.template.process('<%= banner %>');
generateRawFiles(grunt, banner);
});
grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
var srcFiles = grunt.config.get('concat.bootstrap.src');
var destFilepath = 'dist/js/npm.js';
generateCommonJSModule(grunt, srcFiles, destFilepath);
});
// Docs task.
grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);
grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);
grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']);
grunt.registerTask('docs-github', ['jekyll:github', 'htmlmin']);
grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']);
};

@ -1,7 +0,0 @@
{
"extends" : "../js/.jshintrc",
"asi" : false,
"browser" : false,
"es3" : false,
"node" : true
}

@ -1,30 +0,0 @@
/*!
* Bootstrap Grunt task for the CommonJS module generation
* http://getbootstrap.com
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
'use strict';
var fs = require('fs');
var path = require('path');
var COMMONJS_BANNER = '// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.\n';
module.exports = function generateCommonJSModule(grunt, srcFiles, destFilepath) {
var destDir = path.dirname(destFilepath);
function srcPathToDestRequire(srcFilepath) {
var requirePath = path.relative(destDir, srcFilepath).replace(/\\/g, '/');
return 'require(\'' + requirePath + '\')';
}
var moduleOutputJs = COMMONJS_BANNER + srcFiles.map(srcPathToDestRequire).join('\n');
try {
fs.writeFileSync(destFilepath, moduleOutputJs);
} catch (err) {
grunt.fail.warn(err);
}
grunt.log.writeln('File ' + destFilepath.cyan + ' created.');
};

@ -1,42 +0,0 @@
/*!
* Bootstrap Grunt task for Glyphicons data generation
* http://getbootstrap.com
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
'use strict';
var fs = require('fs');
module.exports = function generateGlyphiconsData(grunt) {
// Pass encoding, utf8, so `readFileSync` will return a string instead of a
// buffer
var glyphiconsFile = fs.readFileSync('less/glyphicons.less', 'utf8');
var glyphiconsLines = glyphiconsFile.split('\n');
// Use any line that starts with ".glyphicon-" and capture the class name
var iconClassName = /^\.(glyphicon-[a-zA-Z0-9-]+)/;
var glyphiconsData = '# This file is generated via Grunt task. **Do not edit directly.**\n' +
'# See the \'build-glyphicons-data\' task in Gruntfile.js.\n\n';
var glyphiconsYml = 'docs/_data/glyphicons.yml';
for (var i = 0, len = glyphiconsLines.length; i < len; i++) {
var match = glyphiconsLines[i].match(iconClassName);
if (match !== null) {
glyphiconsData += '- ' + match[1] + '\n';
}
}
// Create the `_data` directory if it doesn't already exist
if (!fs.existsSync('docs/_data')) {
fs.mkdirSync('docs/_data');
}
try {
fs.writeFileSync(glyphiconsYml, glyphiconsData);
} catch (err) {
grunt.fail.warn(err);
}
grunt.log.writeln('File ' + glyphiconsYml.cyan + ' created.');
};

@ -1,237 +0,0 @@
/*!
* Bootstrap Grunt task for parsing Less docstrings
* http://getbootstrap.com
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
'use strict';
var Markdown = require('markdown-it');
function markdown2html(markdownString) {
var md = new Markdown();
// the slice removes the <p>...</p> wrapper output by Markdown processor
return md.render(markdownString.trim()).slice(3, -5);
}
/*
Mini-language:
//== This is a normal heading, which starts a section. Sections group variables together.
//## Optional description for the heading
//=== This is a subheading.
//** Optional description for the following variable. You **can** use Markdown in descriptions to discuss `<html>` stuff.
@foo: #fff;
//-- This is a heading for a section whose variables shouldn't be customizable
All other lines are ignored completely.
*/
var CUSTOMIZABLE_HEADING = /^[/]{2}={2}(.*)$/;
var UNCUSTOMIZABLE_HEADING = /^[/]{2}-{2}(.*)$/;
var SUBSECTION_HEADING = /^[/]{2}={3}(.*)$/;
var SECTION_DOCSTRING = /^[/]{2}#{2}(.+)$/;
var VAR_ASSIGNMENT = /^(@[a-zA-Z0-9_-]+):[ ]*([^ ;][^;]*);[ ]*$/;
var VAR_DOCSTRING = /^[/]{2}[*]{2}(.+)$/;
function Section(heading, customizable) {
this.heading = heading.trim();
this.id = this.heading.replace(/\s+/g, '-').toLowerCase();
this.customizable = customizable;
this.docstring = null;
this.subsections = [];
}
Section.prototype.addSubSection = function (subsection) {
this.subsections.push(subsection);
};
function SubSection(heading) {
this.heading = heading.trim();
this.id = this.heading.replace(/\s+/g, '-').toLowerCase();
this.variables = [];
}
SubSection.prototype.addVar = function (variable) {
this.variables.push(variable);
};
function VarDocstring(markdownString) {
this.html = markdown2html(markdownString);
}
function SectionDocstring(markdownString) {
this.html = markdown2html(markdownString);
}
function Variable(name, defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
this.docstring = null;
}
function Tokenizer(fileContent) {
this._lines = fileContent.split('\n');
this._next = undefined;
}
Tokenizer.prototype.unshift = function (token) {
if (this._next !== undefined) {
throw new Error('Attempted to unshift twice!');
}
this._next = token;
};
Tokenizer.prototype._shift = function () {
// returning null signals EOF
// returning undefined means the line was ignored
if (this._next !== undefined) {
var result = this._next;
this._next = undefined;
return result;
}
if (this._lines.length <= 0) {
return null;
}
var line = this._lines.shift();
var match = null;
match = SUBSECTION_HEADING.exec(line);
if (match !== null) {
return new SubSection(match[1]);
}
match = CUSTOMIZABLE_HEADING.exec(line);
if (match !== null) {
return new Section(match[1], true);
}
match = UNCUSTOMIZABLE_HEADING.exec(line);
if (match !== null) {
return new Section(match[1], false);
}
match = SECTION_DOCSTRING.exec(line);
if (match !== null) {
return new SectionDocstring(match[1]);
}
match = VAR_DOCSTRING.exec(line);
if (match !== null) {
return new VarDocstring(match[1]);
}
var commentStart = line.lastIndexOf('//');
var varLine = commentStart === -1 ? line : line.slice(0, commentStart);
match = VAR_ASSIGNMENT.exec(varLine);
if (match !== null) {
return new Variable(match[1], match[2]);
}
return undefined;
};
Tokenizer.prototype.shift = function () {
while (true) {
var result = this._shift();
if (result === undefined) {
continue;
}
return result;
}
};
function Parser(fileContent) {
this._tokenizer = new Tokenizer(fileContent);
}
Parser.prototype.parseFile = function () {
var sections = [];
while (true) {
var section = this.parseSection();
if (section === null) {
if (this._tokenizer.shift() !== null) {
throw new Error('Unexpected unparsed section of file remains!');
}
return sections;
}
sections.push(section);
}
};
Parser.prototype.parseSection = function () {
var section = this._tokenizer.shift();
if (section === null) {
return null;
}
if (!(section instanceof Section)) {
throw new Error('Expected section heading; got: ' + JSON.stringify(section));
}
var docstring = this._tokenizer.shift();
if (docstring instanceof SectionDocstring) {
section.docstring = docstring;
} else {
this._tokenizer.unshift(docstring);
}
this.parseSubSections(section);
return section;
};
Parser.prototype.parseSubSections = function (section) {
while (true) {
var subsection = this.parseSubSection();
if (subsection === null) {
if (section.subsections.length === 0) {
// Presume an implicit initial subsection
subsection = new SubSection('');
this.parseVars(subsection);
} else {
break;
}
}
section.addSubSection(subsection);
}
if (section.subsections.length === 1 && !section.subsections[0].heading && section.subsections[0].variables.length === 0) {
// Ignore lone empty implicit subsection
section.subsections = [];
}
};
Parser.prototype.parseSubSection = function () {
var subsection = this._tokenizer.shift();
if (subsection instanceof SubSection) {
this.parseVars(subsection);
return subsection;
}
this._tokenizer.unshift(subsection);
return null;
};
Parser.prototype.parseVars = function (subsection) {
while (true) {
var variable = this.parseVar();
if (variable === null) {
return;
}
subsection.addVar(variable);
}
};
Parser.prototype.parseVar = function () {
var docstring = this._tokenizer.shift();
if (!(docstring instanceof VarDocstring)) {
this._tokenizer.unshift(docstring);
docstring = null;
}
var variable = this._tokenizer.shift();
if (variable instanceof Variable) {
variable.docstring = docstring;
return variable;
}
this._tokenizer.unshift(variable);
return null;
};
module.exports = Parser;

@ -1,44 +0,0 @@
/*!
* Bootstrap Grunt task for generating raw-files.min.js for the Customizer
* http://getbootstrap.com
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
'use strict';
var fs = require('fs');
var btoa = require('btoa');
var glob = require('glob');
function getFiles(type) {
var files = {};
var recursive = type === 'less';
var globExpr = recursive ? '/**/*' : '/*';
glob.sync(type + globExpr)
.filter(function (path) {
return type === 'fonts' ? true : new RegExp('\\.' + type + '$').test(path);
})
.forEach(function (fullPath) {
var relativePath = fullPath.replace(/^[^/]+\//, '');
files[relativePath] = type === 'fonts' ? btoa(fs.readFileSync(fullPath)) : fs.readFileSync(fullPath, 'utf8');
});
return 'var __' + type + ' = ' + JSON.stringify(files) + '\n';
}
module.exports = function generateRawFilesJs(grunt, banner) {
if (!banner) {
banner = '';
}
var dirs = ['js', 'less', 'fonts'];
var files = banner + dirs.map(getFiles).reduce(function (combined, file) {
return combined + file;
}, '');
var rawFilesJs = 'docs/assets/js/raw-files.min.js';
try {
fs.writeFileSync(rawFilesJs, files);
} catch (err) {
grunt.fail.warn(err);
}
grunt.log.writeln('File ' + rawFilesJs.cyan + ' created.');
};

@ -1,109 +0,0 @@
#!/usr/bin/env node
'use strict';
/* globals Set */
/*!
* Script to update version number references in the project.
* Copyright 2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
var fs = require('fs');
var path = require('path');
var sh = require('shelljs');
sh.config.fatal = true;
var sed = sh.sed;
// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37
RegExp.quote = function (string) {
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
};
RegExp.quoteReplacement = function (string) {
return string.replace(/[$]/g, '$$');
};
var DRY_RUN = false;
function walkAsync(directory, excludedDirectories, fileCallback, errback) {
if (excludedDirectories.has(path.parse(directory).base)) {
return;
}
fs.readdir(directory, function (err, names) {
if (err) {
errback(err);
return;
}
names.forEach(function (name) {
var filepath = path.join(directory, name);
fs.lstat(filepath, function (err, stats) {
if (err) {
process.nextTick(errback, err);
return;
}
if (stats.isSymbolicLink()) {
return;
}
else if (stats.isDirectory()) {
process.nextTick(walkAsync, filepath, excludedDirectories, fileCallback, errback);
}
else if (stats.isFile()) {
process.nextTick(fileCallback, filepath);
}
});
});
});
}
function replaceRecursively(directory, excludedDirectories, allowedExtensions, original, replacement) {
original = new RegExp(RegExp.quote(original), 'g');
replacement = RegExp.quoteReplacement(replacement);
var updateFile = !DRY_RUN ? function (filepath) {
if (allowedExtensions.has(path.parse(filepath).ext)) {
sed('-i', original, replacement, filepath);
}
} : function (filepath) {
if (allowedExtensions.has(path.parse(filepath).ext)) {
console.log('FILE: ' + filepath);
}
else {
console.log('EXCLUDED:' + filepath);
}
};
walkAsync(directory, excludedDirectories, updateFile, function (err) {
console.error('ERROR while traversing directory!:');
console.error(err);
process.exit(1);
});
}
function main(args) {
if (args.length !== 2) {
console.error('USAGE: change-version old_version new_version');
console.error('Got arguments:', args);
process.exit(1);
}
var oldVersion = args[0];
var newVersion = args[1];
var EXCLUDED_DIRS = new Set([
'.git',
'node_modules',
'vendor'
]);
var INCLUDED_EXTENSIONS = new Set([
// This extension whitelist is how we avoid modifying binary files
'',
'.css',
'.html',
'.js',
'.json',
'.less',
'.md',
'.nuspec',
'.ps1',
'.scss',
'.txt',
'.yml'
]);
replaceRecursively('.', EXCLUDED_DIRS, INCLUDED_EXTENSIONS, oldVersion, newVersion);
}
main(process.argv.slice(2));

@ -1,46 +0,0 @@
{
"paths": {
"customizerJs": [
"../assets/js/vendor/autoprefixer.js",
"../assets/js/vendor/less.min.js",
"../assets/js/vendor/jszip.min.js",
"../assets/js/vendor/uglify.min.js",
"../assets/js/vendor/Blob.js",
"../assets/js/vendor/FileSaver.js",
"../assets/js/raw-files.min.js",
"../assets/js/src/customizer.js"
],
"docsJs": [
"../assets/js/vendor/holder.min.js",
"../assets/js/vendor/ZeroClipboard.min.js",
"../assets/js/vendor/anchor.min.js",
"../assets/js/src/application.js"
]
},
"config": {
"autoprefixerBrowsers": [
"Android 2.3",
"Android >= 4",
"Chrome >= 20",
"Firefox >= 24",
"Explorer >= 8",
"iOS >= 6",
"Opera >= 12",
"Safari >= 6"
],
"jqueryCheck": [
"if (typeof jQuery === 'undefined') {",
" throw new Error('Bootstrap\\'s JavaScript requires jQuery')",
"}\n"
],
"jqueryVersionCheck": [
"+function ($) {",
" 'use strict';",
" var version = $.fn.jquery.split(' ')[0].split('.')",
" if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {",
" throw new Error('Bootstrap\\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')",
" }",
"}(jQuery);\n\n"
]
}
}

File diff suppressed because it is too large Load Diff

@ -1,82 +0,0 @@
[
# Docs: https://saucelabs.com/docs/platforms/webdriver
{
browserName: "safari",
platform: "OS X 10.10"
},
{
browserName: "chrome",
platform: "OS X 10.10"
},
{
browserName: "firefox",
platform: "OS X 10.10"
},
# Mac Opera not currently supported by Sauce Labs
{
browserName: "internet explorer",
version: "11",
platform: "Windows 8.1"
},
{
browserName: "internet explorer",
version: "10",
platform: "Windows 8"
},
{
browserName: "internet explorer",
version: "9",
platform: "Windows 7"
},
{
browserName: "internet explorer",
version: "8",
platform: "Windows 7"
},
# { # Unofficial
# browserName: "internet explorer",
# version: "7",
# platform: "Windows XP"
# },
{
browserName: "chrome",
platform: "Windows 8.1"
},
{
browserName: "firefox",
platform: "Windows 8.1"
},
# Win Opera 15+ not currently supported by Sauce Labs
{
browserName: "iphone",
platform: "OS X 10.10",
version: "9.2"
},
# iOS Chrome not currently supported by Sauce Labs
# Linux (unofficial)
{
browserName: "chrome",
platform: "Linux"
},
{
browserName: "firefox",
platform: "Linux"
}
# Android Chrome not currently supported by Sauce Labs
# { # Android Browser (super-unofficial)
# browserName: "android",
# version: "4.0",
# platform: "Linux"
# }
]

@ -1,8 +0,0 @@
$nuget = $env:NuGet
# parse the version number out of package.json
$bsversion = ((Get-Content $env:SourcesPath\package.json) -join "`n" | ConvertFrom-Json).version
# create packages
& $nuget pack "nuget\bootstrap.nuspec" -Verbosity detailed -NonInteractive -NoPackageAnalysis -BasePath $env:SourcesPath -Version $bsversion
& $nuget pack "nuget\bootstrap.less.nuspec" -Verbosity detailed -NonInteractive -NoPackageAnalysis -BasePath $env:SourcesPath -Version $bsversion

@ -1,28 +0,0 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>bootstrap.less</id>
<version>3.3.7</version>
<title>Bootstrap Less</title>
<authors>Twitter, Inc.</authors>
<owners>bootstrap</owners>
<description>The most popular front-end framework for developing responsive, mobile first projects on the web.</description>
<releaseNotes>http://blog.getbootstrap.com</releaseNotes>
<summary>Bootstrap framework in Less. Includes fonts and JavaScript</summary>
<language>en-us</language>
<projectUrl>http://getbootstrap.com</projectUrl>
<iconUrl>http://getbootstrap.com/apple-touch-icon.png</iconUrl>
<licenseUrl>https://github.com/twbs/bootstrap/blob/master/LICENSE</licenseUrl>
<copyright>Copyright 2016</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<dependencies>
<dependency id="jQuery" version="[1.9.1,4)" />
</dependencies>
<tags>css js less mobile-first responsive front-end framework web</tags>
</metadata>
<files>
<file src="less\**\*.less" target="content\Content\bootstrap" />
<file src="fonts\*.*" target="content\Content\fonts" />
<file src="dist\js\bootstrap*.js" target="content\Scripts" />
</files>
</package>

@ -1,28 +0,0 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>bootstrap</id>
<version>3.3.7</version>
<title>Bootstrap CSS</title>
<authors>Twitter, Inc.</authors>
<owners>bootstrap</owners>
<description>The most popular front-end framework for developing responsive, mobile first projects on the web.</description>
<releaseNotes>http://blog.getbootstrap.com</releaseNotes>
<summary>Bootstrap framework in CSS. Includes fonts and JavaScript</summary>
<language>en-us</language>
<projectUrl>http://getbootstrap.com</projectUrl>
<iconUrl>http://getbootstrap.com/apple-touch-icon.png</iconUrl>
<licenseUrl>https://github.com/twbs/bootstrap/blob/master/LICENSE</licenseUrl>
<copyright>Copyright 2016</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<dependencies>
<dependency id="jQuery" version="[1.9.1,4)" />
</dependencies>
<tags>css js less mobile-first responsive front-end framework web</tags>
</metadata>
<files>
<file src="dist\css\*.*" target="content\Content" />
<file src="dist\fonts\*.*" target="content\fonts" />
<file src="dist\js\bootstrap*.js" target="content\Scripts" />
</files>
</package>

@ -1,32 +0,0 @@
// package metadata file for Meteor.js
/* jshint strict:false */
/* global Package:true */
Package.describe({
name: 'twbs:bootstrap', // http://atmospherejs.com/twbs/bootstrap
summary: 'The most popular front-end framework for developing responsive, mobile first projects on the web.',
version: '3.3.7',
git: 'https://github.com/twbs/bootstrap.git'
});
Package.onUse(function (api) {
api.versionsFrom('METEOR@1.0');
api.use('jquery', 'client');
var assets = [
'dist/fonts/glyphicons-halflings-regular.eot',
'dist/fonts/glyphicons-halflings-regular.svg',
'dist/fonts/glyphicons-halflings-regular.ttf',
'dist/fonts/glyphicons-halflings-regular.woff',
'dist/fonts/glyphicons-halflings-regular.woff2'
];
if (api.addAssets) {
api.addAssets(assets, 'client');
} else {
api.addFiles(assets, 'client', { isAsset: true });
}
api.addFiles([
'dist/css/bootstrap.css',
'dist/js/bootstrap.js'
], 'client');
});

@ -1,89 +0,0 @@
{
"name": "bootstrap",
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
"version": "3.3.7",
"keywords": [
"css",
"less",
"mobile-first",
"responsive",
"front-end",
"framework",
"web"
],
"homepage": "http://getbootstrap.com",
"author": "Twitter, Inc.",
"scripts": {
"change-version": "node grunt/change-version.js",
"update-shrinkwrap": "npm shrinkwrap --dev && shx mv ./npm-shrinkwrap.json ./grunt/npm-shrinkwrap.json",
"test": "grunt test"
},
"style": "dist/css/bootstrap.css",
"less": "less/bootstrap.less",
"main": "./dist/js/npm",
"repository": {
"type": "git",
"url": "https://github.com/twbs/bootstrap.git"
},
"bugs": {
"url": "https://github.com/twbs/bootstrap/issues"
},
"license": "MIT",
"devDependencies": {
"btoa": "~1.1.2",
"glob": "~7.0.3",
"grunt": "~1.0.1",
"grunt-autoprefixer": "~3.0.4",
"grunt-contrib-clean": "~1.0.0",
"grunt-contrib-compress": "~1.3.0",
"grunt-contrib-concat": "~1.0.0",
"grunt-contrib-connect": "~1.0.0",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-csslint": "~1.0.0",
"grunt-contrib-cssmin": "~1.0.0",
"grunt-contrib-htmlmin": "~1.5.0",
"grunt-contrib-jshint": "~1.0.0",
"grunt-contrib-less": "~1.3.0",
"grunt-contrib-pug": "~1.0.0",
"grunt-contrib-qunit": "~0.7.0",
"grunt-contrib-uglify": "~1.0.0",
"grunt-contrib-watch": "~1.0.0",
"grunt-csscomb": "~3.1.0",
"grunt-exec": "~1.0.0",
"grunt-html": "~8.0.1",
"grunt-jekyll": "~0.4.4",
"grunt-jscs": "~3.0.1",
"grunt-saucelabs": "~9.0.0",
"load-grunt-tasks": "~3.5.0",
"markdown-it": "^7.0.0",
"shelljs": "^0.7.0",
"shx": "^0.1.2",
"time-grunt": "^1.3.0"
},
"engines": {
"node": ">=0.10.1"
},
"files": [
"dist",
"fonts",
"grunt",
"js/*.js",
"less/**/*.less",
"Gruntfile.js",
"LICENSE"
],
"jspm": {
"main": "js/bootstrap",
"shim": {
"js/bootstrap": {
"deps": "jquery",
"exports": "$"
}
},
"files": [
"css",
"fonts",
"js"
]
}
}

@ -0,0 +1,19 @@
{
"name": "jqueryui-touch-punch",
"main": "jquery.ui.touch-punch.min.js",
"ignore": [],
"dependencies": {
"jquery": ">=1.6",
"jquery-ui": ">=1.8"
},
"homepage": "https://github.com/furf/jquery-ui-touch-punch",
"_release": "4bc0091452",
"_resolution": {
"type": "branch",
"branch": "master",
"commit": "4bc009145202d9c7483ba85f3a236a8f3470354d"
},
"_source": "https://github.com/furf/jquery-ui-touch-punch.git",
"_target": "*",
"_originalSource": "jqueryui-touch-punch"
}

@ -0,0 +1,41 @@
# jQuery UI Touch Punch
## Touch Event Support for jQuery UI
> **jQuery UI Touch Punch is a small hack that enables the use of touch events on sites using the jQuery UI user interface library.**
_[Visit the official Touch Punch website](http://touchpunch.furf.com)._
Currently, [jQuery UI](http://jqueryui.com/) user interface library does not support the use of touch events in their widgets and interactions. This means that the slick UI you designed and tested in your desktop browser will fail on most, if not all, touch-enabled mobile devices, because jQuery UI listens to mouse events—mouseover, mousemove and mouseout—not touch events—touchstart, touchmove and touchend.
That's where jQuery UI Touch Punch comes in. Touch Punch works by using [simulated events](https://developer.mozilla.org/en/DOM/document.createEvent) to map [touch events](http://www.html5rocks.com/en/mobile/touch/) to their mouse event analogs. Simply include the script on your page and your touch events will be turned into their corresponding mouse events to which jQuery UI will respond as expected.
As I said, Touch Punch is a hack. It [duck punches](http://en.wikipedia.org/wiki/Monkey_patch) some of jQuery UI's core functionality to handle the mapping of touch events. Touch Punch works with all basic implementations of jQuery UI's interactions and widgets. However, you may find more complex cases where Touch Punch fails. If so, scroll down to learn how you can file and/or fix issues.
This code is dual licensed under the MIT or GPL Version 2 licenses and is therefore free to use, modify and/or distribute, but if you include Touch Punch in other software packages or plugins, please include an attribution to the original software and a link to [this Touch Punch website](http://touchpunch.furf.com/).
## Using Touch Punch is as easy as 1, 2…
Just follow these simple steps to enable touch events in your jQuery UI app:
1. Include jQuery and jQuery UI on your page.
```html
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.8.17/jquery-ui.min.js"></script>
```
2. Include Touch Punch after jQuery UI and before its first use.
Please note that if you are using jQuery UI's components, Touch Punch must be included after jquery.ui.mouse.js, as Touch Punch modifies its behavior.
```html
<script src="jquery.ui.touch-punch.min.js"></script>
```
3. There is no 3. Just use jQuery UI as expected and watch it work at the touch of a finger.
```html
<script>$('#widget').draggable();</script>
```
_Tested on iPad, iPhone, Android and other touch-enabled mobile devices._

@ -0,0 +1,10 @@
{
"name": "jqueryui-touch-punch",
"version": "0.2.3",
"main": "jquery.ui.touch-punch.min.js",
"ignore": [],
"dependencies": {
"jquery": ">=1.6",
"jquery-ui": ">=1.8"
}
}

@ -0,0 +1,180 @@
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 20112014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
(function ($) {
// Detect touch support
$.support.touch = 'ontouchend' in document;
// Ignore browsers without touch support
if (!$.support.touch) {
return;
}
var mouseProto = $.ui.mouse.prototype,
_mouseInit = mouseProto._mouseInit,
_mouseDestroy = mouseProto._mouseDestroy,
touchHandled;
/**
* Simulate a mouse event based on a corresponding touch event
* @param {Object} event A touch event
* @param {String} simulatedType The corresponding mouse event
*/
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) {
return;
}
event.preventDefault();
var touch = event.originalEvent.changedTouches[0],
simulatedEvent = document.createEvent('MouseEvents');
// Initialize the simulated mouse event using the touch event's coordinates
simulatedEvent.initMouseEvent(
simulatedType, // type
true, // bubbles
true, // cancelable
window, // view
1, // detail
touch.screenX, // screenX
touch.screenY, // screenY
touch.clientX, // clientX
touch.clientY, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button
null // relatedTarget
);
// Dispatch the simulated event to the target element
event.target.dispatchEvent(simulatedEvent);
}
/**
* Handle the jQuery UI widget's touchstart events
* @param {Object} event The widget element's touchstart event
*/
mouseProto._touchStart = function (event) {
var self = this;
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
// Simulate the mouseover event
simulateMouseEvent(event, 'mouseover');
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(event, 'mousedown');
};
/**
* Handle the jQuery UI widget's touchmove events
* @param {Object} event The document's touchmove event
*/
mouseProto._touchMove = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Interaction was not a click
this._touchMoved = true;
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
};
/**
* Handle the jQuery UI widget's touchend events
* @param {Object} event The document's touchend event
*/
mouseProto._touchEnd = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Simulate the mouseup event
simulateMouseEvent(event, 'mouseup');
// Simulate the mouseout event
simulateMouseEvent(event, 'mouseout');
// If the touch interaction did not move, it should trigger a click
if (!this._touchMoved) {
// Simulate the click event
simulateMouseEvent(event, 'click');
}
// Unset the flag to allow other widgets to inherit the touch event
touchHandled = false;
};
/**
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
* This method extends the widget with bound touch event handlers that
* translate touch events to mouse events and pass them to the widget's
* original mouse event handling methods.
*/
mouseProto._mouseInit = function () {
var self = this;
// Delegate the touch handlers to the widget's element
self.element.bind({
touchstart: $.proxy(self, '_touchStart'),
touchmove: $.proxy(self, '_touchMove'),
touchend: $.proxy(self, '_touchEnd')
});
// Call the original $.ui.mouse init method
_mouseInit.call(self);
};
/**
* Remove the touch event handlers
*/
mouseProto._mouseDestroy = function () {
var self = this;
// Delegate the touch handlers to the widget's element
self.element.unbind({
touchstart: $.proxy(self, '_touchStart'),
touchmove: $.proxy(self, '_touchMove'),
touchend: $.proxy(self, '_touchEnd')
});
// Call the original $.ui.mouse destroy method
_mouseDestroy.call(self);
};
})(jQuery);

@ -0,0 +1,11 @@
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 20112014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);

@ -23,12 +23,12 @@
"./build/mediaelement-and-player.js",
"./build/mediaelementplayer.css"
],
"version": "4.2.8",
"_release": "4.2.8",
"version": "4.2.9",
"_release": "4.2.9",
"_resolution": {
"type": "version",
"tag": "4.2.8",
"commit": "0896fce181f4f1450bdc1c70598e66fe0bfa872b"
"tag": "4.2.9",
"commit": "6d665634da067ac075cd49631e646c7b16f25510"
},
"_source": "https://github.com/johndyer/mediaelement.git",
"_target": "4.*",

@ -0,0 +1,90 @@
'use strict';/*!
* This is a `i18n` language object.
*
* Malay (for inter-country use - see the countries mentioned in infobox`s `native to` at https://en.wikipedia.org/wiki/Malay_language)
*
* @author
* muhdnurhidayat (Twitter: @mnh48com)
* Sascha Greuel (Twitter: @SoftCreatR)
*
* @see core/i18n.js
*/
(function (exports) {
if (exports.ms === undefined) {
exports.ms = {
'mejs.plural-form': 0,
'mejs.download-file': 'Muat Turun Fail',
'mejs.install-flash': 'Anda sedang menggunakan pelayar internet yang tidak mempunyai pemain Flash. Sila aktifkan pemalam pemain Flash anda atau muat turun versi terbaru dari https://get.adobe.com/flashplayer/',
'mejs.fullscreen': 'Skrin penuh',
'mejs.play': 'Main',
'mejs.pause': 'Jeda',
'mejs.time-slider': 'Lungsur Masa',
'mejs.time-help-text': 'Gunakan kekunci Anak Panah Kiri/Kanan untuk bergerak satu saat, Anak Panah Atas/Bawah untuk bergerak sepuluh saat.',
'mejs.live-broadcast' : 'Siaran Langsung',
'mejs.volume-help-text': 'Gunakan kekunci Anak Panah Atas/Bawah untuk menguatkan atau memperlahankan bunyi.',
'mejs.unmute': 'Nyahsenyap',
'mejs.mute': 'Senyap',
'mejs.volume-slider': 'Lungsur Bunyi',
'mejs.video-player': 'Pemain Video',
'mejs.audio-player': 'Pemain Audio',
'mejs.captions-subtitles': 'Sarikata',
'mejs.captions-chapters': 'Bab',
'mejs.none': 'Tiada',
'mejs.afrikaans': 'Bahasa Afrikaans',
'mejs.albanian': 'Bahasa Albania',
'mejs.arabic': 'Bahasa Arab',
'mejs.belarusian': 'Bahasa Belarus',
'mejs.bulgarian': 'Bahasa Bulgaria',
'mejs.catalan': 'Bahasa Catalonia',
'mejs.chinese': 'Bahasa Cina',
'mejs.chinese-simplified': 'Bahasa Cina (Ringkas)',
'mejs.chinese-traditional': 'Bahasa Cina (Tradisional)',
'mejs.croatian': 'Bahasa Croatia',
'mejs.czech': 'Bahasa Czech',
'mejs.danish': 'Bahasa Denmark',
'mejs.dutch': 'Bahasa Belanda',
'mejs.english': 'Bahasa Inggeris',
'mejs.estonian': 'Bahasa Estonia',
'mejs.filipino': 'Bahasa Filipino',
'mejs.finnish': 'Bahasa Finland',
'mejs.french': 'Bahasa Perancis',
'mejs.galician': 'Bahasa Galicia',
'mejs.german': 'Bahasa Jerman',
'mejs.greek': 'Bahasa Greek',
'mejs.haitian-creole': 'Bahasa Kreol Haiti',
'mejs.hebrew': 'Bahasa Ibrani',
'mejs.hindi': 'Bahasa Hindi',
'mejs.hungarian': 'Bahasa Hungary',
'mejs.icelandic': 'Bahasa Iceland',
'mejs.indonesian': 'Bahasa Indonesia',
'mejs.irish': 'Bahasa Ireland',
'mejs.italian': 'Bahasa Itali',
'mejs.japanese': 'Bahasa Jepun',
'mejs.korean': 'Bahasa Korea',
'mejs.latvian': 'Bahasa Latvia',
'mejs.lithuanian': 'Bahasa Lithuania',
'mejs.macedonian': 'Bahasa Macedonia',
'mejs.malay': 'Bahasa Melayu',
'mejs.maltese': 'Bahasa Malta',
'mejs.norwegian': 'Bahasa Norway',
'mejs.persian': 'Bahasa Parsi',
'mejs.polish': 'Bahasa Poland',
'mejs.portuguese': 'Bahasa Portugis',
'mejs.romanian': 'Bahasa Romania',
'mejs.russian': 'Bahasa Rusia',
'mejs.serbian': 'Bahasa Serbia',
'mejs.slovak': 'Bahasa Slovak',
'mejs.slovenian': 'Bahasa Slovene',
'mejs.spanish': 'Bahasa Sepanyol',
'mejs.swahili': 'Bahasa Swahili',
'mejs.swedish': 'Bahasa Sweden',
'mejs.tagalog': 'Bahasa Tagalog',
'mejs.thai': 'Bahasa Thai',
'mejs.turkish': 'Bahasa Turki',
'mejs.ukrainian': 'Bahasa Ukraine',
'mejs.vietnamese': 'Bahasa Vietnam',
'mejs.welsh': 'Bahasa Wales',
'mejs.yiddish': 'Bahasa Yiddish'
};
}
})(mejs.i18n);

@ -1017,7 +1017,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
var mejs = {};
mejs.version = '4.2.8';
mejs.version = '4.2.9';
mejs.html5media = {
properties: ['volume', 'src', 'currentTime', 'muted', 'duration', 'paused', 'ended', 'buffered', 'error', 'networkState', 'readyState', 'seeking', 'seekable', 'currentSrc', 'preload', 'bufferedBytes', 'bufferedTime', 'initialTime', 'startOffsetTime', 'defaultPlaybackRate', 'playbackRate', 'played', 'autoplay', 'loop', 'controls'],
@ -1248,7 +1248,7 @@ Object.assign(_player2.default.prototype, {
t.exitFullscreenCallback = function (e) {
var key = e.which || e.keyCode || 0;
if (key === 27 && (Features.HAS_TRUE_NATIVE_FULLSCREEN && Features.IS_FULLSCREEN || t.isFullScreen)) {
if (t.options.enableKeyboard && key === 27 && (Features.HAS_TRUE_NATIVE_FULLSCREEN && Features.IS_FULLSCREEN || t.isFullScreen)) {
player.exitFullScreen();
}
};
@ -1302,6 +1302,10 @@ Object.assign(_player2.default.prototype, {
isNative = t.media.rendererName !== null && /(html5|native)/i.test(t.media.rendererName),
containerStyles = getComputedStyle(t.getElement(t.container));
if (!t.isVideo) {
return;
}
if (t.options.useFakeFullscreen === false && Features.IS_IOS && Features.HAS_IOS_FULLSCREEN && typeof t.media.originalNode.webkitEnterFullscreen === 'function' && t.media.originalNode.canPlayType((0, _media.getTypeFromFile)(t.media.getSrc()))) {
t.media.originalNode.webkitEnterFullscreen();
return;
@ -1390,6 +1394,10 @@ Object.assign(_player2.default.prototype, {
var t = this,
isNative = t.media.rendererName !== null && /(native|html5)/i.test(t.media.rendererName);
if (!t.isVideo) {
return;
}
clearTimeout(t.containerSizeTimeout);
if (Features.HAS_TRUE_NATIVE_FULLSCREEN && (Features.IS_FULLSCREEN || t.isFullScreen)) {
@ -1778,7 +1786,7 @@ Object.assign(_player2.default.prototype, {
handleMouseup = function handleMouseup() {
if (mouseIsDown && t.getCurrentTime() !== null && t.newTime.toFixed(4) !== t.getCurrentTime().toFixed(4)) {
t.setCurrentTime(t.newTime);
t.setCurrentRail();
t.setCurrentRailHandle(t.newTime);
t.updateCurrent(t.newTime);
}
if (t.forcedHandlePause) {
@ -1799,7 +1807,7 @@ Object.assign(_player2.default.prototype, {
startedPaused = t.paused;
}
if (t.options.keyActions.length) {
if (t.options.enableKeyboard && t.options.keyActions.length) {
var keyCode = e.which || e.keyCode || 0,
duration = t.getDuration(),
@ -2178,7 +2186,7 @@ Object.assign(_player2.default.prototype, {
var duration = t.getDuration();
if (isNaN(duration) || duration === Infinity || duration < 0) {
if (t.media !== undefined && (isNaN(duration) || duration === Infinity || duration < 0)) {
t.media.duration = t.options.duration = duration = 0;
}
@ -3218,7 +3226,7 @@ Object.assign(_player2.default.prototype, {
mouseIsOver = false;
});
mute.addEventListener('keydown', function (e) {
if (t.options.keyActions.length) {
if (t.options.enableKeyboard && t.options.keyActions.length) {
var keyCode = e.which || e.keyCode || 0,
volume = media.volume;
@ -5167,11 +5175,12 @@ var MediaElementPlayer = function () {
delete t.node.autoplay;
t.node.setAttribute('src', '');
if (t.media.canPlayType((0, _media.getTypeFromFile)(src)) !== '') {
t.node.setAttribute('src', src);
}
if (~rendererName.indexOf('iframe')) {
if (rendererName && rendererName.indexOf('iframe') > -1) {
var layer = _document2.default.getElementById(t.media.id + '-iframe-overlay');
layer.remove();
}
@ -5218,7 +5227,7 @@ var MediaElementPlayer = function () {
t.getElement(t.container).parentNode.insertBefore(t.node, t.getElement(t.container));
}
if (typeof t.media.renderer.destroy === 'function') {
if (t.media.renderer && typeof t.media.renderer.destroy === 'function') {
t.media.renderer.destroy();
}
@ -5443,6 +5452,11 @@ var DefaultPlayer = function () {
get: function get() {
return this.getDuration();
}
}, {
key: 'remainingTime',
get: function get() {
return this.getDuration() - this.currentTime();
}
}, {
key: 'volume',
set: function set(volume) {
@ -7473,7 +7487,7 @@ var SUPPORT_POINTER_EVENTS = exports.SUPPORT_POINTER_EVENTS = function () {
element.style.pointerEvents = 'auto';
element.style.pointerEvents = 'x';
documentElement.appendChild(element);
var supports = getComputedStyle && getComputedStyle(element, '').pointerEvents === 'auto';
var supports = getComputedStyle && (getComputedStyle(element, '') || {}).pointerEvents === 'auto';
element.remove();
return !!supports;
}();

File diff suppressed because one or more lines are too long

@ -1017,7 +1017,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
var mejs = {};
mejs.version = '4.2.8';
mejs.version = '4.2.9';
mejs.html5media = {
properties: ['volume', 'src', 'currentTime', 'muted', 'duration', 'paused', 'ended', 'buffered', 'error', 'networkState', 'readyState', 'seeking', 'seekable', 'currentSrc', 'preload', 'bufferedBytes', 'bufferedTime', 'initialTime', 'startOffsetTime', 'defaultPlaybackRate', 'playbackRate', 'played', 'autoplay', 'loop', 'controls'],
@ -3192,7 +3192,7 @@ var SUPPORT_POINTER_EVENTS = exports.SUPPORT_POINTER_EVENTS = function () {
element.style.pointerEvents = 'auto';
element.style.pointerEvents = 'x';
documentElement.appendChild(element);
var supports = getComputedStyle && getComputedStyle(element, '').pointerEvents === 'auto';
var supports = getComputedStyle && (getComputedStyle(element, '') || {}).pointerEvents === 'auto';
element.remove();
return !!supports;
}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -315,7 +315,7 @@ var FacebookRenderer = {
fbContainer.id = fb.id;
fbContainer.className = 'fb-video';
fbContainer.setAttribute('data-href', src);
fbContainer.setAttribute('data-allowfullscreen', true);
fbContainer.setAttribute('data-allowfullscreen', 'true');
fbContainer.setAttribute('data-controls', !!mediaElement.originalNode.controls);
mediaElement.originalNode.parentNode.insertBefore(fbContainer, mediaElement.originalNode);
mediaElement.originalNode.style.display = 'none';

File diff suppressed because one or more lines are too long

@ -9,4 +9,4 @@
* License: MIT
*
*/
!function e(t,n,r){function a(s,o){if(!n[s]){if(!t[s]){var u="function"==typeof require&&require;if(!o&&u)return u(s,!0);if(i)return i(s,!0);var d=new Error("Cannot find module '"+s+"'");throw d.code="MODULE_NOT_FOUND",d}var c=n[s]={exports:{}};t[s][0].call(c.exports,function(e){var n=t[s][1][e];return a(n||e)},c,c.exports,e,t,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s<r.length;s++)a(r[s]);return a}({1:[function(e,t,n){"use strict";var r={promise:null,load:function(e){"undefined"!=typeof SC?r._createPlayer(e):(r.promise=r.promise||mejs.Utils.loadScript("https://w.soundcloud.com/player/api.js"),r.promise.then(function(){r._createPlayer(e)}))},_createPlayer:function(e){var t=SC.Widget(e.iframe);window["__ready__"+e.id](t)}},a={name:"soundcloud_iframe",options:{prefix:"soundcloud_iframe"},canPlayType:function(e){return~["video/soundcloud","video/x-soundcloud"].indexOf(e.toLowerCase())},create:function(e,t,n){var a={},i=[],s=e.originalNode.autoplay,o=null!==e.originalNode&&"video"===e.originalNode.tagName.toLowerCase(),u=0,d=0,c=0,l=!0,p=!1,f=null,v=null;a.options=t,a.id=e.id+"_"+t.prefix,a.mediaElement=e;for(var m=mejs.html5media.properties,h=function(t){var n=""+t.substring(0,1).toUpperCase()+t.substring(1);a["get"+n]=function(){if(null!==f){switch(t){case"currentTime":return d;case"duration":return u;case"volume":return 1;case"paused":return l;case"ended":return p;case"muted":return!1;case"buffered":return{start:function(){return 0},end:function(){return c*u},length:1};case"src":return v?v.src:"";case"readyState":return 4}return null}return null},a["set"+n]=function(n){if(null!==f)switch(t){case"src":var r="string"==typeof n?n:n[0].src;f.load(r),s&&f.play();break;case"currentTime":f.seekTo(1e3*n);break;case"muted":n?f.setVolume(0):f.setVolume(1),setTimeout(function(){var t=mejs.Utils.createEvent("volumechange",a);e.dispatchEvent(t)},50);break;case"volume":f.setVolume(n),setTimeout(function(){var t=mejs.Utils.createEvent("volumechange",a);e.dispatchEvent(t)},50);break;case"readyState":var o=mejs.Utils.createEvent("canplay",a);e.dispatchEvent(o)}else i.push({type:"set",propName:t,value:n})}},y=0,E=m.length;y<E;y++)h(m[y]);for(var g=mejs.html5media.methods,S=function(e){a[e]=function(){if(null!==f)switch(e){case"play":return f.play();case"pause":return f.pause();case"load":return null}else i.push({type:"call",methodName:e})}},U=0,_=g.length;U<_;U++)S(g[U]);window["__ready__"+a.id]=function(t){if(e.scPlayer=f=t,s&&f.play(),i.length)for(var n=0,r=i.length;n<r;n++){var o=i[n];if("set"===o.type){var v=o.propName,m=""+v.substring(0,1).toUpperCase()+v.substring(1);a["set"+m](o.value)}else"call"===o.type&&a[o.methodName]()}f.bind(SC.Widget.Events.PLAY_PROGRESS,function(){l=!1,p=!1,f.getPosition(function(t){d=t/1e3;var n=mejs.Utils.createEvent("timeupdate",a);e.dispatchEvent(n)})}),f.bind(SC.Widget.Events.PAUSE,function(){l=!0;var t=mejs.Utils.createEvent("pause",a);e.dispatchEvent(t)}),f.bind(SC.Widget.Events.PLAY,function(){l=!1,p=!1;var t=mejs.Utils.createEvent("play",a);e.dispatchEvent(t)}),f.bind(SC.Widget.Events.FINISHED,function(){l=!1,p=!0;var t=mejs.Utils.createEvent("ended",a);e.dispatchEvent(t)}),f.bind(SC.Widget.Events.READY,function(){f.getDuration(function(t){u=t/1e3;var n=mejs.Utils.createEvent("loadedmetadata",a);e.dispatchEvent(n)})}),f.bind(SC.Widget.Events.LOAD_PROGRESS,function(){f.getDuration(function(t){if(u>0){c=u*t;var n=mejs.Utils.createEvent("progress",a);e.dispatchEvent(n)}}),f.getDuration(function(t){u=t;var n=mejs.Utils.createEvent("loadedmetadata",a);e.dispatchEvent(n)})});for(var h=["rendererready","loadeddata","loadedmetadata","canplay"],y=0,E=h.length;y<E;y++){var g=mejs.Utils.createEvent(h[y],a);e.dispatchEvent(g)}},(v=document.createElement("iframe")).id=a.id,v.width=o?"100%":1,v.height=o?"100%":1,v.frameBorder=0,v.style.visibility=o?"visible":"hidden",v.src=n[0].src,v.scrolling="no",e.appendChild(v),e.originalNode.style.display="none";var b={iframe:v,id:a.id};return r.load(b),a.setSize=function(){},a.hide=function(){a.pause(),v&&(v.style.display="none")},a.show=function(){v&&(v.style.display="")},a.destroy=function(){f.destroy()},a}};mejs.Utils.typeChecks.push(function(e){return/\/\/(w\.)?soundcloud.com/i.test(e)?"video/x-soundcloud":null}),mejs.Renderers.add(a)},{}]},{},[1]);
!function e(t,n,r){function a(s,o){if(!n[s]){if(!t[s]){var u="function"==typeof require&&require;if(!o&&u)return u(s,!0);if(i)return i(s,!0);var d=new Error("Cannot find module '"+s+"'");throw d.code="MODULE_NOT_FOUND",d}var c=n[s]={exports:{}};t[s][0].call(c.exports,function(e){var n=t[s][1][e];return a(n||e)},c,c.exports,e,t,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s<r.length;s++)a(r[s]);return a}({1:[function(e,t,n){"use strict";var r={promise:null,load:function(e){"undefined"!=typeof SC?r._createPlayer(e):(r.promise=r.promise||mejs.Utils.loadScript("https://w.soundcloud.com/player/api.js"),r.promise.then(function(){r._createPlayer(e)}))},_createPlayer:function(e){var t=SC.Widget(e.iframe);window["__ready__"+e.id](t)}},a={name:"soundcloud_iframe",options:{prefix:"soundcloud_iframe"},canPlayType:function(e){return~["video/soundcloud","video/x-soundcloud"].indexOf(e.toLowerCase())},create:function(e,t,n){var a={},i=[],s=e.originalNode.autoplay,o=null!==e.originalNode&&"video"===e.originalNode.tagName.toLowerCase(),u=0,d=0,c=0,l=!0,p=!1,f=null,v=null;a.options=t,a.id=e.id+"_"+t.prefix,a.mediaElement=e;for(var m=mejs.html5media.properties,h=0,y=m.length;h<y;h++)!function(t){var n=""+t.substring(0,1).toUpperCase()+t.substring(1);a["get"+n]=function(){if(null!==f){switch(t){case"currentTime":return d;case"duration":return u;case"volume":return 1;case"paused":return l;case"ended":return p;case"muted":return!1;case"buffered":return{start:function(){return 0},end:function(){return c*u},length:1};case"src":return v?v.src:"";case"readyState":return 4}return null}return null},a["set"+n]=function(n){if(null!==f)switch(t){case"src":var r="string"==typeof n?n:n[0].src;f.load(r),s&&f.play();break;case"currentTime":f.seekTo(1e3*n);break;case"muted":n?f.setVolume(0):f.setVolume(1),setTimeout(function(){var t=mejs.Utils.createEvent("volumechange",a);e.dispatchEvent(t)},50);break;case"volume":f.setVolume(n),setTimeout(function(){var t=mejs.Utils.createEvent("volumechange",a);e.dispatchEvent(t)},50);break;case"readyState":var o=mejs.Utils.createEvent("canplay",a);e.dispatchEvent(o)}else i.push({type:"set",propName:t,value:n})}}(m[h]);for(var E=mejs.html5media.methods,g=0,S=E.length;g<S;g++)!function(e){a[e]=function(){if(null!==f)switch(e){case"play":return f.play();case"pause":return f.pause();case"load":return null}else i.push({type:"call",methodName:e})}}(E[g]);window["__ready__"+a.id]=function(t){if(e.scPlayer=f=t,s&&f.play(),i.length)for(var n=0,r=i.length;n<r;n++){var o=i[n];if("set"===o.type){var v=o.propName,m=""+v.substring(0,1).toUpperCase()+v.substring(1);a["set"+m](o.value)}else"call"===o.type&&a[o.methodName]()}f.bind(SC.Widget.Events.PLAY_PROGRESS,function(){l=!1,p=!1,f.getPosition(function(t){d=t/1e3;var n=mejs.Utils.createEvent("timeupdate",a);e.dispatchEvent(n)})}),f.bind(SC.Widget.Events.PAUSE,function(){l=!0;var t=mejs.Utils.createEvent("pause",a);e.dispatchEvent(t)}),f.bind(SC.Widget.Events.PLAY,function(){l=!1,p=!1;var t=mejs.Utils.createEvent("play",a);e.dispatchEvent(t)}),f.bind(SC.Widget.Events.FINISHED,function(){l=!1,p=!0;var t=mejs.Utils.createEvent("ended",a);e.dispatchEvent(t)}),f.bind(SC.Widget.Events.READY,function(){f.getDuration(function(t){u=t/1e3;var n=mejs.Utils.createEvent("loadedmetadata",a);e.dispatchEvent(n)})}),f.bind(SC.Widget.Events.LOAD_PROGRESS,function(){f.getDuration(function(t){if(u>0){c=u*t;var n=mejs.Utils.createEvent("progress",a);e.dispatchEvent(n)}}),f.getDuration(function(t){u=t;var n=mejs.Utils.createEvent("loadedmetadata",a);e.dispatchEvent(n)})});for(var h=["rendererready","loadeddata","loadedmetadata","canplay"],y=0,E=h.length;y<E;y++){var g=mejs.Utils.createEvent(h[y],a);e.dispatchEvent(g)}},(v=document.createElement("iframe")).id=a.id,v.width=o?"100%":1,v.height=o?"100%":1,v.frameBorder=0,v.style.visibility=o?"visible":"hidden",v.src=n[0].src,v.scrolling="no",e.appendChild(v),e.originalNode.style.display="none";var U={iframe:v,id:a.id};return r.load(U),a.setSize=function(){},a.hide=function(){a.pause(),v&&(v.style.display="none")},a.show=function(){v&&(v.style.display="")},a.destroy=function(){f.destroy()},a}};mejs.Utils.typeChecks.push(function(e){return/\/\/(w\.)?soundcloud.com/i.test(e)?"video/x-soundcloud":null}),mejs.Renderers.add(a)},{}]},{},[1]);

File diff suppressed because one or more lines are too long

@ -367,9 +367,9 @@ var vimeoIframeRenderer = {
vimeoContainer.setAttribute('height', height);
vimeoContainer.setAttribute('frameBorder', '0');
vimeoContainer.setAttribute('src', '' + standardUrl + queryArgs);
vimeoContainer.setAttribute('webkitallowfullscreen', '');
vimeoContainer.setAttribute('mozallowfullscreen', '');
vimeoContainer.setAttribute('allowfullscreen', '');
vimeoContainer.setAttribute('webkitallowfullscreen', 'true');
vimeoContainer.setAttribute('mozallowfullscreen', 'true');
vimeoContainer.setAttribute('allowfullscreen', 'true');
mediaElement.originalNode.parentNode.insertBefore(vimeoContainer, mediaElement.originalNode);
mediaElement.originalNode.style.display = 'none';

File diff suppressed because one or more lines are too long

@ -1,5 +1,23 @@
### Version History
*4.2.9 (2018/03/27)*
* Fixed typo in documentation (https://github.com/mediaelement/mediaelement/pull/2512) @moagggi
* Added remainingTime getter (https://github.com/mediaelement/mediaelement/pull/2505) @Lewiscowles1986
* Fixed iframe parameters formatting for Vimeo/Facebook (https://github.com/mediaelement/mediaelement/pull/2498) @skreutzer
* Added Malay translation (https://github.com/mediaelement/mediaelement/pull/2490) @MuhdNurHidayat
* Fixed `getComputedStyle` in Firefox (https://github.com/mediaelement/mediaelement/pull/2487) @pgrzeszczak-neducatio
* Fixed documentation for React usage @rafa8626
* Added LICENSE file @rafa8626
* Added conditionals to avoid executing `fullscreen` methods in audio tags @rafa8626
* Added conditionals to avoid triggering keyboard events if `enableKeyboard` is false @rafa8626
* Added missing conditionals when destroying media @rafa8626
* Modified Malay language in the demo @rafa8626
* Set empty source when destroying player to prevent #2499 @rafa8626
* Added conditional to avoid setting duration when `media` has been destroyed @rafa8626
* Replaced callback when seeking in paused media to achieve correct behavior @rafa8626
* Updated documentation about using `startLanguage` and `toggleCaptionsButtonWhenOnlyOne` (https://github.com/mediaelement/mediaelement/pull/2520) @dmdewey
*4.2.8 (2018/01/16)*
* Clarify effect of setting `toggleCaptionsButtonWhenOnlyOne` (https://github.com/mediaelement/mediaelement/pull/2471) @cjcolvar
@ -27,7 +45,7 @@
* Fixed issues related to duplicated calls when triggering error and fixed style for poster when error is displayed @rafa8626
* Fixed issue with captions not being rendered inside video frame on any state @rafa8626
* Integrated `destroy` method in `MediaElement` class @rafa8626
* Added validation to modify `SoundCloud` iframe atributes when using `video` tag @rafa8626
* Added validation to modify `SoundCloud` iframe attributes when using `video` tag @rafa8626
* Fixed issue when checking for native dimensions of `video` element to set responsive dimensions correctly @rafa8626
* Added missing workflow to make `loop` work correctly in YouTube according to documentation @rafa8626
* Changed paths for `hls.js` and `flv.js` renderers to always be up-to-date @rafa8626

@ -0,0 +1,65 @@
'use strict';
if (mejs.i18n.ca !== undefined) {
mejs.i18n.ca['mejs.speed-rate'] = 'Velocitat';
}
if (mejs.i18n.cs !== undefined) {
mejs.i18n.cs['mejs.speed-rate'] = 'Rychlost';
}
if (mejs.i18n.de !== undefined) {
mejs.i18n.de['mejs.speed-rate'] = 'Geschwindigkeitsrate';
}
if (mejs.i18n.es !== undefined) {
mejs.i18n.es['mejs.speed-rate'] = 'Velocidad';
}
if (mejs.i18n.fa !== undefined) {
mejs.i18n.fa['mejs.speed-rate'] = 'نرخ سرعت';
}
if (mejs.i18n.fr !== undefined) {
mejs.i18n.fr['mejs.speed-rate'] = 'Vitesse';
}
if (mejs.i18n.hr !== undefined) {
mejs.i18n.hr['mejs.speed-rate'] = 'Brzina reprodukcije';
}
if (mejs.i18n.hu !== undefined) {
mejs.i18n.hu['mejs.speed-rate'] = 'Sebesség';
}
if (mejs.i18n.it !== undefined) {
mejs.i18n.it['mejs.speed-rate'] = 'Velocità';
}
if (mejs.i18n.ja !== undefined) {
mejs.i18n.ja['mejs.speed-rate'] = '高速';
}
if (mejs.i18n.ko !== undefined) {
mejs.i18n.ko['mejs.speed-rate'] = '속도 속도';
}
if (mejs.i18n.nl !== undefined) {
mejs.i18n.nl['mejs.speed-rate'] = 'Snelheidsgraad';
}
if (mejs.i18n.pl !== undefined) {
mejs.i18n.pl['mejs.speed-rate'] = 'Prędkość';
}
if (mejs.i18n.pt !== undefined) {
mejs.i18n.pt['mejs.speed-rate'] = 'Taxa de velocidade';
}
if (mejs.i18n.ro !== undefined) {
mejs.i18n.ro['mejs.speed-rate'] = 'Viteză de viteză';
}
if (mejs.i18n.ru !== undefined) {
mejs.i18n.ru['mejs.speed-rate'] = 'Скорость воспроизведения';
}
if (mejs.i18n.sk !== undefined) {
mejs.i18n.sk['mejs.speed-rate'] = 'Rýchlosť';
}
if (mejs.i18n.sv !== undefined) {
mejs.i18n.sv['mejs.speed-rate'] = 'Hastighet';
}
if (mejs.i18n.uk !== undefined) {
mejs.i18n.uk['mejs.speed-rate'] = 'Швидкість відтворення';
}
if (mejs.i18n.zh !== undefined) {
mejs.i18n.zh['mejs.speed-rate'] = '速度';
}
if (mejs.i18n['zh-CN'] !== undefined) {
mejs.i18n['zh-CN']['mejs.speed-rate'] = '速度';
}

@ -0,0 +1,94 @@
.mejs__speed-button,
.mejs-speed-button {
position: relative;
}
.mejs__speed-button > button,
.mejs-speed-button > button {
background: transparent;
color: #fff;
font-size: 11px;
line-height: normal;
margin: 11px 0 0;
width: 36px;
}
.mejs__speed-selector,
.mejs-speed-selector {
background: rgba(50, 50, 50, 0.7);
border: solid 1px transparent;
border-radius: 0;
height: 150px;
left: -10px;
overflow: hidden;
padding: 0;
position: absolute;
top: -100px;
visibility: hidden;
width: 60px;
}
.mejs__speed-selector,
.mejs-speed-selector {
visibility: visible;
}
.mejs__speed-selector-list,
.mejs-speed-selector-list {
display: block;
list-style-type: none !important;
margin: 0;
overflow: hidden;
padding: 0;
}
.mejs__speed-selector-list-item,
.mejs-speed-selector-list-item {
color: #fff;
display: block;
list-style-type: none !important;
margin: 0 0 6px;
overflow: hidden;
padding: 0 10px;
}
.mejs__speed-selector-list-item:hover,
.mejs-speed-selector-list-item:hover {
background-color: rgb(200, 200, 200) !important;
background-color: rgba(255, 255, 255, 0.4) !important;
}
.mejs__speed-selector-input,
.mejs-speed-selector-input {
clear: both;
float: left;
left: -1000px;
margin: 3px 3px 0 5px;
position: absolute;
}
.mejs__speed-selector-label,
.mejs-speed-selector-label {
color: white;
cursor: pointer;
float: left;
font-size: 11px;
line-height: 15px;
margin-left: 5px;
padding: 4px 0 0;
width: 60px;
}
.mejs__speed-selected,
.mejs-speed-selected {
color: rgba(33, 248, 248, 1);
}
.mejs__speed-selector,
.mejs-speed-selector {
visibility: hidden;
}
.mejs__speed-button:hover .mejs__speed-selector,
.mejs-speed-button:hover .mejs-speed-selector {
visibility: visible;
}

@ -0,0 +1,174 @@
/*!
* MediaElement.js
* http://www.mediaelementjs.com/
*
* Wrapper that mimics native HTML5 MediaElement (audio and video)
* using a variety of technologies (pure JavaScript, Flash, iframe)
*
* Copyright 2010-2017, John Dyer (http://j.hn/)
* License: MIT
*
*/(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
'use strict';
mejs.i18n.en['mejs.speed-rate'] = 'Speed Rate';
Object.assign(mejs.MepDefaults, {
speeds: ['2.00', '1.50', '1.25', '1.00', '0.75'],
defaultSpeed: '1.00',
speedChar: 'x',
speedText: null
});
Object.assign(MediaElementPlayer.prototype, {
buildspeed: function buildspeed(player, controls, layers, media) {
var t = this,
isNative = t.media.rendererName !== null && /(native|html5)/i.test(t.media.rendererName);
if (!isNative) {
return;
}
var speeds = [],
speedTitle = mejs.Utils.isString(t.options.speedText) ? t.options.speedText : mejs.i18n.t('mejs.speed-rate'),
getSpeedNameFromValue = function getSpeedNameFromValue(value) {
for (var i = 0, total = speeds.length; i < total; i++) {
if (speeds[i].value === value) {
return speeds[i].name;
}
}
};
var playbackSpeed = void 0,
defaultInArray = false;
for (var i = 0, total = t.options.speeds.length; i < total; i++) {
var s = t.options.speeds[i];
if (typeof s === 'string') {
speeds.push({
name: '' + s + t.options.speedChar,
value: s
});
if (s === t.options.defaultSpeed) {
defaultInArray = true;
}
} else {
speeds.push(s);
if (s.value === t.options.defaultSpeed) {
defaultInArray = true;
}
}
}
if (!defaultInArray) {
speeds.push({
name: t.options.defaultSpeed + t.options.speedChar,
value: t.options.defaultSpeed
});
}
speeds.sort(function (a, b) {
return parseFloat(b.value) - parseFloat(a.value);
});
t.cleanspeed(player);
player.speedButton = document.createElement('div');
player.speedButton.className = t.options.classPrefix + 'button ' + t.options.classPrefix + 'speed-button';
player.speedButton.innerHTML = '<button type="button" aria-controls="' + t.id + '" title="' + speedTitle + '" ' + ('aria-label="' + speedTitle + '" tabindex="0">' + getSpeedNameFromValue(t.options.defaultSpeed) + '</button>') + ('<div class="' + t.options.classPrefix + 'speed-selector ' + t.options.classPrefix + 'offscreen">') + ('<ul class="' + t.options.classPrefix + 'speed-selector-list"></ul>') + '</div>';
t.addControlElement(player.speedButton, 'speed');
for (var _i = 0, _total = speeds.length; _i < _total; _i++) {
var inputId = t.id + '-speed-' + speeds[_i].value;
player.speedButton.querySelector('ul').innerHTML += '<li class="' + t.options.classPrefix + 'speed-selector-list-item">' + ('<input class="' + t.options.classPrefix + 'speed-selector-input" type="radio" name="' + t.id + '_speed"') + ('disabled="disabled" value="' + speeds[_i].value + '" id="' + inputId + '" ') + ((speeds[_i].value === t.options.defaultSpeed ? ' checked="checked"' : '') + '/>') + ('<label for="' + inputId + '" class="' + t.options.classPrefix + 'speed-selector-label') + ((speeds[_i].value === t.options.defaultSpeed ? ' ' + t.options.classPrefix + 'speed-selected' : '') + '">') + (speeds[_i].name + '</label>') + '</li>';
}
playbackSpeed = t.options.defaultSpeed;
player.speedSelector = player.speedButton.querySelector('.' + t.options.classPrefix + 'speed-selector');
var inEvents = ['mouseenter', 'focusin'],
outEvents = ['mouseleave', 'focusout'],
radios = player.speedButton.querySelectorAll('input[type="radio"]'),
labels = player.speedButton.querySelectorAll('.' + t.options.classPrefix + 'speed-selector-label');
for (var _i2 = 0, _total2 = inEvents.length; _i2 < _total2; _i2++) {
player.speedButton.addEventListener(inEvents[_i2], function () {
mejs.Utils.removeClass(player.speedSelector, t.options.classPrefix + 'offscreen');
player.speedSelector.style.height = player.speedSelector.querySelector('ul').offsetHeight;
player.speedSelector.style.top = -1 * parseFloat(player.speedSelector.offsetHeight) + 'px';
});
}
for (var _i3 = 0, _total3 = outEvents.length; _i3 < _total3; _i3++) {
player.speedSelector.addEventListener(outEvents[_i3], function () {
mejs.Utils.addClass(this, t.options.classPrefix + 'offscreen');
});
}
for (var _i4 = 0, _total4 = radios.length; _i4 < _total4; _i4++) {
var radio = radios[_i4];
radio.disabled = false;
radio.addEventListener('click', function () {
var self = this,
newSpeed = self.value;
playbackSpeed = newSpeed;
media.playbackRate = parseFloat(newSpeed);
player.speedButton.querySelector('button').innerHTML = getSpeedNameFromValue(newSpeed);
var selected = player.speedButton.querySelectorAll('.' + t.options.classPrefix + 'speed-selected');
for (var _i5 = 0, _total5 = selected.length; _i5 < _total5; _i5++) {
mejs.Utils.removeClass(selected[_i5], t.options.classPrefix + 'speed-selected');
}
self.checked = true;
var siblings = mejs.Utils.siblings(self, function (el) {
return mejs.Utils.hasClass(el, t.options.classPrefix + 'speed-selector-label');
});
for (var j = 0, _total6 = siblings.length; j < _total6; j++) {
mejs.Utils.addClass(siblings[j], t.options.classPrefix + 'speed-selected');
}
});
}
for (var _i6 = 0, _total7 = labels.length; _i6 < _total7; _i6++) {
labels[_i6].addEventListener('click', function () {
var radio = mejs.Utils.siblings(this, function (el) {
return el.tagName === 'INPUT';
})[0],
event = mejs.Utils.createEvent('click', radio);
radio.dispatchEvent(event);
});
}
player.speedSelector.addEventListener('keydown', function (e) {
e.stopPropagation();
});
media.addEventListener('loadedmetadata', function () {
if (playbackSpeed) {
media.playbackRate = parseFloat(playbackSpeed);
}
});
},
cleanspeed: function cleanspeed(player) {
if (player) {
if (player.speedButton) {
player.speedButton.parentNode.removeChild(player.speedButton);
}
if (player.speedSelector) {
player.speedSelector.parentNode.removeChild(player.speedSelector);
}
}
}
});
},{}]},{},[1]);

@ -0,0 +1 @@
.mejs-speed-button,.mejs__speed-button{position:relative}.mejs-speed-button>button,.mejs__speed-button>button{background:transparent;color:#fff;font-size:11px;line-height:normal;margin:11px 0 0;width:36px}.mejs-speed-selector,.mejs__speed-selector{background:rgba(50,50,50,.7);border:1px solid transparent;border-radius:0;height:150px;left:-10px;overflow:hidden;padding:0;position:absolute;top:-100px;width:60px;visibility:visible}.mejs-speed-selector-list,.mejs__speed-selector-list{display:block;list-style-type:none!important;margin:0;overflow:hidden;padding:0}.mejs-speed-selector-list-item,.mejs__speed-selector-list-item{color:#fff;display:block;list-style-type:none!important;margin:0 0 6px;overflow:hidden;padding:0 10px}.mejs-speed-selector-list-item:hover,.mejs__speed-selector-list-item:hover{background-color:#c8c8c8!important;background-color:hsla(0,0%,100%,.4)!important}.mejs-speed-selector-input,.mejs__speed-selector-input{clear:both;float:left;left:-1000px;margin:3px 3px 0 5px;position:absolute}.mejs-speed-selector-label,.mejs__speed-selector-label{color:#fff;cursor:pointer;float:left;font-size:11px;line-height:15px;margin-left:5px;padding:4px 0 0;width:60px}.mejs-speed-selected,.mejs__speed-selected{color:#21f8f8}.mejs-speed-selector,.mejs__speed-selector{visibility:hidden}.mejs-speed-button:hover .mejs-speed-selector,.mejs__speed-button:hover .mejs__speed-selector{visibility:visible}

@ -0,0 +1,12 @@
/*!
* MediaElement.js
* http://www.mediaelementjs.com/
*
* Wrapper that mimics native HTML5 MediaElement (audio and video)
* using a variety of technologies (pure JavaScript, Flash, iframe)
*
* Copyright 2010-2017, John Dyer (http://j.hn/)
* License: MIT
*
*/
!function e(t,s,o){function n(l,i){if(!s[l]){if(!t[l]){var a="function"==typeof require&&require;if(!i&&a)return a(l,!0);if(r)return r(l,!0);var d=new Error("Cannot find module '"+l+"'");throw d.code="MODULE_NOT_FOUND",d}var p=s[l]={exports:{}};t[l][0].call(p.exports,function(e){var s=t[l][1][e];return n(s||e)},p,p.exports,e,t,s,o)}return s[l].exports}for(var r="function"==typeof require&&require,l=0;l<o.length;l++)n(o[l]);return n}({1:[function(e,t,s){"use strict";mejs.i18n.en["mejs.speed-rate"]="Speed Rate",Object.assign(mejs.MepDefaults,{speeds:["2.00","1.50","1.25","1.00","0.75"],defaultSpeed:"1.00",speedChar:"x",speedText:null}),Object.assign(MediaElementPlayer.prototype,{buildspeed:function(e,t,s,o){var n=this;if(null!==n.media.rendererName&&/(native|html5)/i.test(n.media.rendererName)){for(var r=[],l=mejs.Utils.isString(n.options.speedText)?n.options.speedText:mejs.i18n.t("mejs.speed-rate"),i=function(e){for(var t=0,s=r.length;t<s;t++)if(r[t].value===e)return r[t].name},a=void 0,d=!1,p=0,c=n.options.speeds.length;p<c;p++){var u=n.options.speeds[p];"string"==typeof u?(r.push({name:""+u+n.options.speedChar,value:u}),u===n.options.defaultSpeed&&(d=!0)):(r.push(u),u.value===n.options.defaultSpeed&&(d=!0))}d||r.push({name:n.options.defaultSpeed+n.options.speedChar,value:n.options.defaultSpeed}),r.sort(function(e,t){return parseFloat(t.value)-parseFloat(e.value)}),n.cleanspeed(e),e.speedButton=document.createElement("div"),e.speedButton.className=n.options.classPrefix+"button "+n.options.classPrefix+"speed-button",e.speedButton.innerHTML='<button type="button" aria-controls="'+n.id+'" title="'+l+'" aria-label="'+l+'" tabindex="0">'+i(n.options.defaultSpeed)+'</button><div class="'+n.options.classPrefix+"speed-selector "+n.options.classPrefix+'offscreen"><ul class="'+n.options.classPrefix+'speed-selector-list"></ul></div>',n.addControlElement(e.speedButton,"speed");for(var f=0,v=r.length;f<v;f++){var m=n.id+"-speed-"+r[f].value;e.speedButton.querySelector("ul").innerHTML+='<li class="'+n.options.classPrefix+'speed-selector-list-item"><input class="'+n.options.classPrefix+'speed-selector-input" type="radio" name="'+n.id+'_speed"disabled="disabled" value="'+r[f].value+'" id="'+m+'" '+(r[f].value===n.options.defaultSpeed?' checked="checked"':"")+'/><label for="'+m+'" class="'+n.options.classPrefix+"speed-selector-label"+(r[f].value===n.options.defaultSpeed?" "+n.options.classPrefix+"speed-selected":"")+'">'+r[f].name+"</label></li>"}a=n.options.defaultSpeed,e.speedSelector=e.speedButton.querySelector("."+n.options.classPrefix+"speed-selector");for(var h=["mouseenter","focusin"],S=["mouseleave","focusout"],x=e.speedButton.querySelectorAll('input[type="radio"]'),b=e.speedButton.querySelectorAll("."+n.options.classPrefix+"speed-selector-label"),g=0,y=h.length;g<y;g++)e.speedButton.addEventListener(h[g],function(){mejs.Utils.removeClass(e.speedSelector,n.options.classPrefix+"offscreen"),e.speedSelector.style.height=e.speedSelector.querySelector("ul").offsetHeight,e.speedSelector.style.top=-1*parseFloat(e.speedSelector.offsetHeight)+"px"});for(var P=0,j=S.length;P<j;P++)e.speedSelector.addEventListener(S[P],function(){mejs.Utils.addClass(this,n.options.classPrefix+"offscreen")});for(var B=0,E=x.length;B<E;B++){var C=x[B];C.disabled=!1,C.addEventListener("click",function(){var t=this,s=t.value;a=s,o.playbackRate=parseFloat(s),e.speedButton.querySelector("button").innerHTML=i(s);for(var r=e.speedButton.querySelectorAll("."+n.options.classPrefix+"speed-selected"),l=0,d=r.length;l<d;l++)mejs.Utils.removeClass(r[l],n.options.classPrefix+"speed-selected");t.checked=!0;for(var p=mejs.Utils.siblings(t,function(e){return mejs.Utils.hasClass(e,n.options.classPrefix+"speed-selector-label")}),c=0,u=p.length;c<u;c++)mejs.Utils.addClass(p[c],n.options.classPrefix+"speed-selected")})}for(var U=0,q=b.length;U<q;U++)b[U].addEventListener("click",function(){var e=mejs.Utils.siblings(this,function(e){return"INPUT"===e.tagName})[0],t=mejs.Utils.createEvent("click",e);e.dispatchEvent(t)});e.speedSelector.addEventListener("keydown",function(e){e.stopPropagation()}),o.addEventListener("loadedmetadata",function(){a&&(o.playbackRate=parseFloat(a))})}},cleanspeed:function(e){e&&(e.speedButton&&e.speedButton.parentNode.removeChild(e.speedButton),e.speedSelector&&e.speedSelector.parentNode.removeChild(e.speedSelector))}})},{}]},{},[1]);

@ -0,0 +1,26 @@
{
"name": "multiselect",
"version": "2.5.2",
"homepage": "https://github.com/crlcu/multiselect",
"authors": [
"Adrian Crisan <adrian.crisan88@gmail.com>"
],
"description": "jQuery multiselect plugin with two sides",
"main": "dist/js/multiselect.min.js",
"keywords": [
"multiselect"
],
"license": "MIT",
"dependencies": {
"jquery": ">= 1.7"
},
"_release": "2.5.2",
"_resolution": {
"type": "version",
"tag": "v2.5.2",
"commit": "5d850b4a2992dbab1cf913559da0d2398f5db30f"
},
"_source": "https://github.com/crlcu/multiselect.git",
"_target": "*",
"_originalSource": "multiselect-two-sides"
}

@ -0,0 +1,3 @@
/node_modules
.htaccess
.idea

@ -0,0 +1,35 @@
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="2.5.2"></a>
## [2.5.2](https://github.com/crlcu/multiselect/compare/v2.5.1...v2.5.2) (2018-07-10)
### Bug Fixes
* [#164](https://github.com/crlcu/multiselect/issues/164) - Invalid regular expression ([541bb68](https://github.com/crlcu/multiselect/commit/541bb68))
<a name="2.5.1"></a>
## [2.5.1](https://github.com/crlcu/multiselect/compare/v2.5.0...v2.5.1) (2018-04-27)
### Patches
* change the value for the main path ([22414f8](https://github.com/crlcu/multiselect/commit/22414f8))
### Features
* add afterInit event ([22e1523](https://github.com/crlcu/multiselect/commit/22e1523))
<a name="2.5.0"></a>
# [2.5.0](https://github.com/crlcu/multiselect/compare/v2.4.1...v2.5.0) (2018-02-06)
### Features
* now we can have different sort functions for left and right ([887ab4a](https://github.com/crlcu/multiselect/commit/887ab4a))

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Adrian Crisan (adrian.crisan88@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,66 @@
multiselect
===========
jQuery multiselect plugin with two sides. The user can select one or more items and send them to the other side.
# [Demo](http://crlcu.github.com/multiselect/)
## Requirements
- jQuery 1.7 or higher
## Quick start
Several quick start options are available:
- Clone the repo: `git clone https://github.com/crlcu/multiselect.git` or
- Install with [Bower](http://bower.io): `bower install multiselect-two-sides`.
### Usage example
```html
<div class="row">
<div class="col-xs-5">
<select name="from[]" id="multiselect" class="form-control" size="8" multiple="multiple">
<option value="1">Item 1</option>
<option value="3">Item 3</option>
<option value="2">Item 2</option>
</select>
</div>
<div class="col-xs-2">
<button type="button" id="multiselect_rightAll" class="btn btn-block"><i class="glyphicon glyphicon-forward"></i></button>
<button type="button" id="multiselect_rightSelected" class="btn btn-block"><i class="glyphicon glyphicon-chevron-right"></i></button>
<button type="button" id="multiselect_leftSelected" class="btn btn-block"><i class="glyphicon glyphicon-chevron-left"></i></button>
<button type="button" id="multiselect_leftAll" class="btn btn-block"><i class="glyphicon glyphicon-backward"></i></button>
</div>
<div class="col-xs-5">
<select name="to[]" id="multiselect_to" class="form-control" size="8" multiple="multiple"></select>
</div>
</div>
```
```javascript
<script type="text/javascript" src="path/to/jquery.min.js"></script>
<script type="text/javascript" src="path/to/multiselect.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('#multiselect').multiselect();
});
</script>
```
## Bugs and feature requests
If your problem or idea is not [addressed](https://github.com/crlcu/multiselect/issues) yet, [please open a new issue](https://github.com/crlcu/multiselect/issues/new).
## Versioning
For transparency into release cycle and in striving to maintain backward compatibility, multiselect is maintained under [the Semantic Versioning guidelines](http://semver.org/).
## License
The multiselect plugin is open-sourced software licensed under the [the MIT license](https://github.com/crlcu/multiselect/blob/master/LICENSE).

@ -0,0 +1,17 @@
{
"name": "multiselect",
"version": "2.5.2",
"homepage": "https://github.com/crlcu/multiselect",
"authors": [
"Adrian Crisan <adrian.crisan88@gmail.com>"
],
"description": "jQuery multiselect plugin with two sides",
"main": "dist/js/multiselect.min.js",
"keywords": [
"multiselect"
],
"license": "MIT",
"dependencies": {
"jquery": ">= 1.7"
}
}

@ -0,0 +1,26 @@
.github.ribbon {
position: fixed;
display: block;
top: 40px;
right: 0;
border: 0;
z-index: 1001;
}
.prettyprint {
padding: 10px!important;
}
#wrap {
padding-top: 70px;
}
#home h1 {
margin-bottom: 30px;
}
#demo,
#examples,
#support,
#download {
padding-top: 40px;
}

@ -0,0 +1,813 @@
/*
* @license
*
* Multiselect v2.5.2
* http://crlcu.github.io/multiselect/
*
* Copyright (c) 2016-2018 Adrian Crisan
* Licensed under the MIT license (https://github.com/crlcu/multiselect/blob/master/LICENSE)
*/
if (typeof jQuery === 'undefined') {
throw new Error('multiselect requires jQuery');
}
;(function ($) {
'use strict';
var version = $.fn.jquery.split(' ')[0].split('.');
if (version[0] < 2 && version[1] < 7) {
throw new Error('multiselect requires jQuery version 1.7 or higher');
}
})(jQuery);
;(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module depending on jQuery.
define(['jquery'], factory);
} else {
// No AMD. Register plugin with global jQuery object.
factory(jQuery);
}
}(function ($) {
'use strict';
var Multiselect = (function($) {
/** Multiselect object constructor
*
* @class Multiselect
* @constructor
**/
function Multiselect( $select, settings ) {
var id = $select.prop('id');
this.$left = $select;
this.$right = $( settings.right ).length ? $( settings.right ) : $('#' + id + '_to');
this.actions = {
$leftAll: $( settings.leftAll ).length ? $( settings.leftAll ) : $('#' + id + '_leftAll'),
$rightAll: $( settings.rightAll ).length ? $( settings.rightAll ) : $('#' + id + '_rightAll'),
$leftSelected: $( settings.leftSelected ).length ? $( settings.leftSelected ) : $('#' + id + '_leftSelected'),
$rightSelected: $( settings.rightSelected ).length ? $( settings.rightSelected ) : $('#' + id + '_rightSelected'),
$undo: $( settings.undo ).length ? $( settings.undo ) : $('#' + id + '_undo'),
$redo: $( settings.redo ).length ? $( settings.redo ) : $('#' + id + '_redo'),
$moveUp: $( settings.moveUp ).length ? $( settings.moveUp ) : $('#' + id + '_move_up'),
$moveDown: $( settings.moveDown ).length ? $( settings.moveDown ) : $('#' + id + '_move_down')
};
delete settings.leftAll;
delete settings.leftSelected;
delete settings.right;
delete settings.rightAll;
delete settings.rightSelected;
delete settings.undo;
delete settings.redo;
delete settings.moveUp;
delete settings.moveDown;
this.options = {
keepRenderingSort: settings.keepRenderingSort,
submitAllLeft: settings.submitAllLeft !== undefined ? settings.submitAllLeft : true,
submitAllRight: settings.submitAllRight !== undefined ? settings.submitAllRight : true,
search: settings.search,
ignoreDisabled: settings.ignoreDisabled !== undefined ? settings.ignoreDisabled : false,
matchOptgroupBy: settings.matchOptgroupBy !== undefined ? settings.matchOptgroupBy : 'label'
};
delete settings.keepRenderingSort, settings.submitAllLeft, settings.submitAllRight, settings.search, settings.ignoreDisabled, settings.matchOptgroupBy;
this.callbacks = settings;
if ( typeof this.callbacks.sort == 'function' ) {
var sort = this.callbacks.sort;
this.callbacks.sort = {
left: sort,
right: sort,
};
}
this.init();
}
Multiselect.prototype = {
init: function() {
var self = this;
self.undoStack = [];
self.redoStack = [];
if (self.options.keepRenderingSort) {
self.skipInitSort = true;
if (self.callbacks.sort !== false) {
self.callbacks.sort = {
left: function(a, b) {
return $(a).data('position') > $(b).data('position') ? 1 : -1;
},
right: function(a, b) {
return $(a).data('position') > $(b).data('position') ? 1 : -1;
},
};
}
self.$left.attachIndex();
self.$right.each(function(i, select) {
$(select).attachIndex();
});
}
if ( typeof self.callbacks.startUp == 'function' ) {
self.callbacks.startUp( self.$left, self.$right );
}
if ( !self.skipInitSort ) {
if ( typeof self.callbacks.sort.left == 'function' ) {
self.$left.mSort(self.callbacks.sort.left);
}
if ( typeof self.callbacks.sort.right == 'function' ) {
self.$right.each(function(i, select) {
$(select).mSort(self.callbacks.sort.right);
});
}
}
// Append left filter
if (self.options.search && self.options.search.left) {
self.options.search.$left = $(self.options.search.left);
self.$left.before(self.options.search.$left);
}
// Append right filter
if (self.options.search && self.options.search.right) {
self.options.search.$right = $(self.options.search.right);
self.$right.before($(self.options.search.$right));
}
// Initialize events
self.events();
if ( typeof self.callbacks.afterInit == 'function' ) {
self.callbacks.afterInit();
}
},
events: function() {
var self = this;
// Attach event to left filter
if (self.options.search && self.options.search.$left) {
self.options.search.$left.on('keyup', function(e) {
if (self.callbacks.fireSearch(this.value)) {
var $toShow = self.$left.find('option:search("' + this.value + '")').mShow();
var $toHide = self.$left.find('option:not(:search("' + this.value + '"))').mHide();
var $grpHide = self.$left.find('option').closest('optgroup').mHide();
var $grpShow = self.$left.find('option:not(.hidden)').parent('optgroup').mShow();
} else {
self.$left.find('option, optgroup').mShow();
}
});
}
// Attach event to right filter
if (self.options.search && self.options.search.$right) {
self.options.search.$right.on('keyup', function(e) {
if (self.callbacks.fireSearch(this.value)) {
var $toShow = self.$right.find('option:search("' + this.value + '")').mShow();
var $toHide = self.$right.find('option:not(:search("' + this.value + '"))').mHide();
var $grpHide = self.$right.find('option').closest('optgroup').mHide();
var $grpShow = self.$right.find('option:not(.hidden)').parent('optgroup').mShow();
} else {
self.$right.find('option, optgroup').mShow();
}
});
}
// Select all the options from left and right side when submiting the parent form
self.$right.closest('form').on('submit', function(e) {
if (self.options.search) {
// Clear left search input
if (self.options.search.$left) {
self.options.search.$left.val('').trigger('keyup');
}
// Clear right search input
if (self.options.search.$right) {
self.options.search.$right.val('').trigger('keyup');
}
}
self.$left.find('option').prop('selected', self.options.submitAllLeft);
self.$right.find('option').prop('selected', self.options.submitAllRight);
});
// Attach event for double clicking on options from left side
self.$left.on('dblclick', 'option', function(e) {
e.preventDefault();
var $options = self.$left.find('option:selected');
if ( $options.length ) {
self.moveToRight($options, e);
}
});
// Attach event for clicking on optgroup's from left side
self.$left.on('click', 'optgroup', function(e) {
if ($(e.target).prop('tagName') == 'OPTGROUP') {
$(this)
.children()
.prop('selected', true);
}
});
// Attach event for pushing ENTER on options from left side
self.$left.on('keypress', function(e) {
if (e.keyCode === 13) {
e.preventDefault();
var $options = self.$left.find('option:selected');
if ( $options.length ) {
self.moveToRight($options, e);
}
}
});
// Attach event for double clicking on options from right side
self.$right.on('dblclick', 'option', function(e) {
e.preventDefault();
var $options = self.$right.find('option:selected');
if ( $options.length ) {
self.moveToLeft($options, e);
}
});
// Attach event for clicking on optgroup's from right side
self.$right.on('click', 'optgroup', function(e) {
if ($(e.target).prop('tagName') == 'OPTGROUP') {
$(this)
.children()
.prop('selected', true);
}
});
// Attach event for pushing BACKSPACE or DEL on options from right side
self.$right.on('keydown', function(e) {
if (e.keyCode === 8 || e.keyCode === 46) {
e.preventDefault();
var $options = self.$right.find('option:selected');
if ( $options.length ) {
self.moveToLeft($options, e);
}
}
});
// dblclick support for IE
if ( navigator.userAgent.match(/MSIE/i) || navigator.userAgent.indexOf('Trident/') > 0 || navigator.userAgent.indexOf('Edge/') > 0) {
self.$left.dblclick(function(e) {
self.actions.$rightSelected.trigger('click');
});
self.$right.dblclick(function(e) {
self.actions.$leftSelected.trigger('click');
});
}
self.actions.$rightSelected.on('click', function(e) {
e.preventDefault();
var $options = self.$left.find('option:selected');
if ( $options.length ) {
self.moveToRight($options, e);
}
$(this).blur();
});
self.actions.$leftSelected.on('click', function(e) {
e.preventDefault();
var $options = self.$right.find('option:selected');
if ( $options.length ) {
self.moveToLeft($options, e);
}
$(this).blur();
});
self.actions.$rightAll.on('click', function(e) {
e.preventDefault();
var $options = self.$left.children(':not(span):not(.hidden)');
if ( $options.length ) {
self.moveToRight($options, e);
}
$(this).blur();
});
self.actions.$leftAll.on('click', function(e) {
e.preventDefault();
var $options = self.$right.children(':not(span):not(.hidden)');
if ( $options.length ) {
self.moveToLeft($options, e);
}
$(this).blur();
});
self.actions.$undo.on('click', function(e) {
e.preventDefault();
self.undo(e);
});
self.actions.$redo.on('click', function(e) {
e.preventDefault();
self.redo(e);
});
self.actions.$moveUp.on('click', function(e) {
e.preventDefault();
var $options = self.$right.find(':selected:not(span):not(.hidden)');
if ( $options.length ) {
self.moveUp($options, e);
}
$(this).blur();
});
self.actions.$moveDown.on('click', function(e) {
e.preventDefault();
var $options = self.$right.find(':selected:not(span):not(.hidden)');
if ( $options.length ) {
self.moveDown($options, e);
}
$(this).blur();
});
},
moveToRight: function( $options, event, silent, skipStack ) {
var self = this;
if ( typeof self.callbacks.moveToRight == 'function' ) {
return self.callbacks.moveToRight( self, $options, event, silent, skipStack );
}
if ( typeof self.callbacks.beforeMoveToRight == 'function' && !silent ) {
if ( !self.callbacks.beforeMoveToRight( self.$left, self.$right, $options ) ) {
return false;
}
}
self.moveFromAtoB(self.$left, self.$right, $options, event, silent, skipStack);
if ( !skipStack ) {
self.undoStack.push(['right', $options ]);
self.redoStack = [];
}
if ( typeof self.callbacks.sort.right == 'function' && !silent && !self.doNotSortRight ) {
self.$right.mSort(self.callbacks.sort.right);
}
if ( typeof self.callbacks.afterMoveToRight == 'function' && !silent ) {
self.callbacks.afterMoveToRight( self.$left, self.$right, $options );
}
return self;
},
moveToLeft: function( $options, event, silent, skipStack ) {
var self = this;
if ( typeof self.callbacks.moveToLeft == 'function' ) {
return self.callbacks.moveToLeft( self, $options, event, silent, skipStack );
}
if ( typeof self.callbacks.beforeMoveToLeft == 'function' && !silent ) {
if ( !self.callbacks.beforeMoveToLeft( self.$left, self.$right, $options ) ) {
return false;
}
}
self.moveFromAtoB(self.$right, self.$left, $options, event, silent, skipStack);
if ( !skipStack ) {
self.undoStack.push(['left', $options ]);
self.redoStack = [];
}
if ( typeof self.callbacks.sort.left == 'function' && !silent ) {
self.$left.mSort(self.callbacks.sort.left);
}
if ( typeof self.callbacks.afterMoveToLeft == 'function' && !silent ) {
self.callbacks.afterMoveToLeft( self.$left, self.$right, $options );
}
return self;
},
moveFromAtoB: function( $source, $destination, $options, event, silent, skipStack ) {
var self = this;
if ( typeof self.callbacks.moveFromAtoB == 'function' ) {
return self.callbacks.moveFromAtoB(self, $source, $destination, $options, event, silent, skipStack);
}
$options.each(function(index, option) {
var $option = $(option);
if (self.options.ignoreDisabled && $option.is(':disabled')) {
return true;
}
if ($option.is('optgroup') || $option.parent().is('optgroup')) {
var $sourceGroup = $option.is('optgroup') ? $option : $option.parent();
var optgroupSelector = 'optgroup[' + self.options.matchOptgroupBy + '="' + $sourceGroup.prop(self.options.matchOptgroupBy) + '"]';
var $destinationGroup = $destination.find(optgroupSelector);
if (!$destinationGroup.length) {
$destinationGroup = $sourceGroup.clone(true);
$destinationGroup.empty();
$destination.move($destinationGroup);
}
if ($option.is('optgroup')) {
$destinationGroup.move($option.find('option'));
} else {
$destinationGroup.move($option);
}
$sourceGroup.removeIfEmpty();
} else {
$destination.move($option);
}
});
return self;
},
moveUp: function($options) {
var self = this;
if ( typeof self.callbacks.beforeMoveUp == 'function' ) {
if ( !self.callbacks.beforeMoveUp( $options ) ) {
return false;
}
}
$options.first().prev().before($options);
if ( typeof self.callbacks.afterMoveUp == 'function' ) {
self.callbacks.afterMoveUp( $options );
}
},
moveDown: function($options) {
var self = this;
if ( typeof self.callbacks.beforeMoveDown == 'function' ) {
if ( !self.callbacks.beforeMoveDown( $options ) ) {
return false;
}
}
$options.last().next().after($options);
if ( typeof self.callbacks.afterMoveDown == 'function' ) {
self.callbacks.afterMoveDown( $options );
}
},
undo: function(event) {
var self = this;
var last = self.undoStack.pop();
if ( last ) {
self.redoStack.push(last);
switch(last[0]) {
case 'left':
self.moveToRight(last[1], event, false, true);
break;
case 'right':
self.moveToLeft(last[1], event, false, true);
break;
}
}
},
redo: function(event) {
var self = this;
var last = self.redoStack.pop();
if ( last ) {
self.undoStack.push(last);
switch(last[0]) {
case 'left':
self.moveToLeft(last[1], event, false, true);
break;
case 'right':
self.moveToRight(last[1], event, false, true);
break;
}
}
}
}
return Multiselect;
})($);
$.multiselect = {
defaults: {
/** will be executed once - remove from $left all options that are already in $right
*
* @method startUp
* @attribute $left jQuery object
* @attribute $right jQuery object
**/
startUp: function( $left, $right ) {
$right.find('option').each(function(index, rightOption) {
if ($(rightOption).parent().prop('tagName') == 'OPTGROUP') {
var optgroupSelector = 'optgroup[label="' + $(rightOption).parent().attr('label') + '"]';
$left.find(optgroupSelector + ' option[value="' + rightOption.value + '"]').each(function(index, leftOption) {
leftOption.remove();
});
$left.find(optgroupSelector).removeIfEmpty();
} else {
var $option = $left.find('option[value="' + rightOption.value + '"]');
$option.remove();
}
});
},
/** will be executed after initialize plugin
*
* @method afterInit
*
* @default true
* @return {boolean}
**/
afterInit: function(){ return true; },
/** will be executed each time before moving option[s] to right
*
* IMPORTANT : this method must return boolean value
* true : continue to moveToRight method
* false : stop
*
* @method beforeMoveToRight
* @attribute $left jQuery object
* @attribute $right jQuery object
* @attribute $options HTML object (the option[s] which was selected to be moved)
*
* @default true
* @return {boolean}
**/
beforeMoveToRight: function($left, $right, $options) { return true; },
/* will be executed each time after moving option[s] to right
*
* @method afterMoveToRight
* @attribute $left jQuery object
* @attribute $right jQuery object
* @attribute $options HTML object (the option[s] which was selected to be moved)
**/
afterMoveToRight: function($left, $right, $options) {},
/** will be executed each time before moving option[s] to left
*
* IMPORTANT : this method must return boolean value
* true : continue to moveToRight method
* false : stop
*
* @method beforeMoveToLeft
* @attribute $left jQuery object
* @attribute $right jQuery object
* @attribute $options HTML object (the option[s] which was selected to be moved)
*
* @default true
* @return {boolean}
**/
beforeMoveToLeft: function($left, $right, $options) { return true; },
/* will be executed each time after moving option[s] to left
*
* @method afterMoveToLeft
* @attribute $left jQuery object
* @attribute $right jQuery object
* @attribute $options HTML object (the option[s] which was selected to be moved)
**/
afterMoveToLeft: function($left, $right, $options) {},
/** will be executed each time before moving option[s] up
*
* IMPORTANT : this method must return boolean value
* true : continue to moveUp method
* false : stop
*
* @method beforeMoveUp
* @attribute $options HTML object (the option[s] which was selected to be moved)
*
* @default true
* @return {boolean}
**/
beforeMoveUp: function($options) { return true; },
/* will be executed each time after moving option[s] up
*
* @method afterMoveUp
* @attribute $left jQuery object
* @attribute $right jQuery object
* @attribute $options HTML object (the option[s] which was selected to be moved)
**/
afterMoveUp: function($options) {},
/** will be executed each time before moving option[s] down
*
* IMPORTANT : this method must return boolean value
* true : continue to moveUp method
* false : stop
*
* @method beforeMoveDown
* @attribute $options HTML object (the option[s] which was selected to be moved)
*
* @default true
* @return {boolean}
**/
beforeMoveDown: function($options) { return true; },
/* will be executed each time after moving option[s] down
*
* @method afterMoveUp
* @attribute $left jQuery object
* @attribute $right jQuery object
* @attribute $options HTML object (the option[s] which was selected to be moved)
**/
afterMoveDown: function($options) {},
/** sort options by option text
*
* @method sort
* @attribute a HTML option
* @attribute b HTML option
*
* @return 1/-1
**/
sort: function(a, b) {
if (a.innerHTML == 'NA') {
return 1;
} else if (b.innerHTML == 'NA') {
return -1;
}
return (a.innerHTML > b.innerHTML) ? 1 : -1;
},
/* will tell if the search can start
*
* @method fireSearch
* @attribute value String
*
* @return {boolean}
**/
fireSearch: function(value) {
return value.length > 1;
}
}
};
var ua = window.navigator.userAgent;
var isIE = (ua.indexOf("MSIE ") + ua.indexOf("Trident/") + ua.indexOf("Edge/")) > -3;
var isSafari = ua.toLowerCase().indexOf("safari") > -1;
var isFirefox = ua.toLowerCase().indexOf("firefox") > -1;
$.fn.multiselect = function( options ) {
return this.each(function() {
var $this = $(this),
data = $this.data('crlcu.multiselect'),
settings = $.extend({}, $.multiselect.defaults, $this.data(), (typeof options === 'object' && options));
if (!data) {
$this.data('crlcu.multiselect', (data = new Multiselect($this, settings)));
}
});
};
// append options
// then set the selected attribute to false
$.fn.move = function( $options ) {
this
.append($options)
.find('option')
.prop('selected', false);
return this;
};
$.fn.removeIfEmpty = function() {
if (!this.children().length) {
this.remove();
}
return this;
};
$.fn.mShow = function() {
this.removeClass('hidden').show();
if (isIE || isSafari) {
this.each(function(index, option) {
// Remove <span> to make it compatible with IE
if($(option).parent().is('span')) {
$(option).parent().replaceWith(option);
}
$(option).show();
});
}
if(isFirefox){
this.attr('disabled', false)
}
return this;
};
$.fn.mHide = function() {
this.addClass('hidden').hide();
if (isIE || isSafari) {
this.each(function(index, option) {
// Wrap with <span> to make it compatible with IE
if(!$(option).parent().is('span')) {
$(option).wrap('<span>').hide();
}
});
}
if(isFirefox){
this.attr('disabled', true)
}
return this;
};
// sort options then reappend them to the select
$.fn.mSort = function(callback) {
this
.children()
.sort(callback)
.appendTo(this);
this
.find('optgroup')
.each(function(i, group) {
$(group).children()
.sort(callback)
.appendTo(group);
})
return this;
};
// attach index to children
$.fn.attachIndex = function() {
this.children().each(function(index, option) {
var $option = $(option);
if ($option.is('optgroup')) {
$option.children().each(function(i, children) {
$(children).data('position', i);
});
}
$option.data('position', index);
});
};
$.expr[":"].search = function(elem, index, meta) {
var regex = new RegExp(meta[3].replace(/([^a-zA-Z0-9])/g, "\\$1"), "i");
return $(elem).text().match(regex);
}
}));

File diff suppressed because one or more lines are too long

@ -0,0 +1,481 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="description" lang="en" content="jQuery multiselect plugin with two sides. The user can select one or more items and send them to the other side."/>
<meta name="keywords" lang="en" content="jQuery multiselect plugin" />
<base href="//crlcu.github.io/multiselect/" />
<title>jQuery multiselect plugin with two sides</title>
<link rel="icon" type="image/x-icon" href="https://github.com/favicon.ico" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
<link rel="stylesheet" href="lib/google-code-prettify/prettify.css" />
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<a href="https://github.com/crlcu/multiselect" class="github ribbon">
<img src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png" alt="Fork me on GitHub">
</a>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand scroll" href="#">Multiselect</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="#" class="scroll">Home</a></li>
<li><a href="#documentation" class="scroll">Documentation</a></li>
<li><a href="#browser-support" class="scroll">Browser Support</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Examples <span class="caret"></span ></a>
<ul class="dropdown-menu" role="menu">
<li><a href="examples/zero-configuration.html">Zero configuration</a></li>
<li><a href="examples/data-options.html">With <code>data</code> options</a></li>
<li><a href="examples/javascript-options.html">With <code>javascript</code> options</a></li>
<li><a href="examples/search.html">With search</a></li>
<li><a href="examples/keep-rendering-sort.html">Keep rendering sort</a></li>
<li><a href="examples/undo-redo.html">Undo / Redo</a></li>
<li><a href="examples/multiple-destinations.html">Multiple destinations</a></li>
<li><a href="examples/optgroup.html">With <code>optgroup</code></a></li>
<li><a href="examples/move-up-down.html">With move <code>up</code>/<code>down</code> buttons</a></li>
</ul>
</li>
<li><a href="#download" class="scroll">Download</a></li>
</ul>
</div>
</div>
</div>
</div>
<div id="wrap" class="container">
<div id="home">
<div class="jumbotron">
<h1>Multiselect</h1>
<p>This is a small <a href="http://jquery.com/" target="_blank">jQuery</a> plugin that helps you improve the
<strong>user experience</strong> regarding the use of multiple cross selects.
</p>
<p>It is very <strong>easy to install</strong> and can be <strong>easily customized</strong> because it has <strong>callbacks</strong> for many events, such as :
<div class="row">
<div class="col-xs-6">
<ul>
<li><a href="#beforeMoveToRight">beforeMoveToRight</a></li>
<li><a href="#beforeMoveToLeft">beforeMoveToLeft</a></li>
</ul>
</div>
<div class="col-xs-6">
<ul>
<li><a href="#afterMoveToRight">afterMoveToRight</a></li>
<li><a href="#afterMoveToLeft">afterMoveToLeft</a></li>
</ul>
</div>
</div>
</p>
<p>It also has a <a href="#sort">sort</a> function that can be defined in order of your needs for each multiple select.</p>
<p>You can <strong>fully customize</strong> it via <strong>CSS</strong>, you can extend it and have as <strong>many multi selects</strong> as you want on the page.</p>
<p><a href="#documentation" class="btn btn-primary btn-lg">Documentation</a></p>
</div>
</div>
<div id="documentation">
<div class="page-header"><h3>Documentation</h3></div>
<h4>Options:</h4>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>type</th>
<th>default</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>right</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_to</td>
<td>The <code>select</code> where the options are moved to</td>
</tr>
<tr>
<td>rightSelected</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_rightSelected</td>
<td>The <code>button</code> that moves selected options from left to right</td>
</tr>
<tr>
<td>rightAll</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_rightAll</td>
<td>The <code>button</code> that moves all options from left to right</td>
</tr>
<tr>
<td>leftSelected</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_leftSelected</td>
<td>The <code>button</code> that moves selected options from right to left</td>
</tr>
<tr>
<td>leftAll</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_leftAll</td>
<td>The <code>button</code> that moves all options from right to left</td>
</tr>
<tr>
<td>undo</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_undo</td>
<td>The <code>button</code> that triggers the undo action</td>
</tr>
<tr>
<td>redo</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_redo</td>
<td>The <code>button</code> that triggers the redo action</td>
</tr>
<tr>
<td>moveUp</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_move_up</td>
<td>The <code>button</code> that move options from right side to the top</td>
</tr>
<tr>
<td>moveDown</td>
<td><code>string</code> jQuery selector</td>
<td><code>multiselect_id</code>_move_down</td>
<td>The <code>button</code> that move options from right side to the bottom</td>
</tr>
<tr>
<td>startUp</td>
<td><code>function</code> or <code>false</code></td>
<td><code>remove</code> from left all options that are present in right</td>
<td>Whatever you want to do with <code>$left</code> and <code>$right</code> elements when the <code>multiselect</code> plugin is initialised</td>
</tr>
<tr id="sort">
<td>sort</td>
<td><code>function</code> or <code>false</code></td>
<td><code>sort</code> options alphabetically</td>
<td>Will sort options when an action happend into right or left elements.</td>
</tr>
<tr id="beforeMoveToRight">
<td>beforeMoveToRight</td>
<td><code>function</code></td>
<td>return <code>true</code></td>
<td>
Whatever you want to do with <code>$left</code>, <code>$right</code> and <code>$options</code> elements before they are moved to right.<br/><br/>
<code>beforeMoveToRight: function($left, $right, $options) { ...; return Boolean; }</code>.<br/><br/>
<ul>
<li><code>$left</code> - jQuery element pointing to left side <code>select</code> element</li>
<li><code>$right</code> - jQuery element pointing to right side <code>select</code> element</li>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
Keep in mind that this function must return a boolean value.<br/>
<ul>
<li><code>true</code> will let the action to be performed</li>
<li><code>false</code> will stop the action</li>
</ul>
</td>
</tr>
<tr id="moveToRight">
<td>moveToRight</td>
<td><code>function</code></td>
<td>return <code>this</code></td>
<td>
If you want to define your own <code>moveToRight</code> functionality you can do this by defining<br/><br/>
<code>moveToRight: function(Multiselect, $options, event, silent, skipStack) { ... }</code><br/><br/>
<ul>
<li><code>Multiselect</code> - current instance of multiselect</li>
<li><code>$options</code> - jQuery element containing all selected options</li>
<li><code>event</code> - the event that initialised the action</li>
<li><code>silent</code> - <code>boolean</code> that tells if you have to trigger <code>beforeMoveToRight</code> and <code>afterMoveToRight</code></li>
<li><code>skipStack</code> - <code>boolean</code> that tells if you have to handle <code>undo/redo</code> stack</li>
</ul>
</td>
</tr>
<tr id="afterMoveToRight">
<td>afterMoveToRight</td>
<td><code>function</code></td>
<td>no action</td>
<td>
Whatever you want to do with <code>$left</code>, <code>$right</code> and <code>$options</code> elements after they are moved to right.<br/><br/>
<code>afterMoveToRight: function($left, $right, $options) { ...; }</code><br/><br/>
<ul>
<li><code>$left</code> - jQuery element pointing to left side <code>select</code> element</li>
<li><code>$right</code> - jQuery element pointing to right side <code>select</code> element</li>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
</td>
</tr>
<tr id="beforeMoveToLeft">
<td>beforeMoveToLeft</td>
<td><code>function</code></td>
<td>return <code>true</code></td>
<td>
Whatever you want to do with <code>$left</code>, <code>$right</code> and <code>$options</code> elements before they are moved to left.<br/><br/>
<code>beforeMoveToLeft: function($left, $right, $options) { ...; return Boolean; }</code>.<br/><br/>
<ul>
<li><code>$left</code> - jQuery element pointing to left side <code>select</code> element</li>
<li><code>$right</code> - jQuery element pointing to right side <code>select</code> element</li>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
Keep in mind that this function must return a boolean value.<br/>
<ul>
<li><code>true</code> will let the action to be performed</li>
<li><code>false</code> will stop the action</li>
</ul>
</td>
</tr>
<tr id="moveToLeft">
<td>moveToLeft</td>
<td><code>function</code></td>
<td>return <code>this</code></td>
<td>
If you want to define your own <code>moveToLeft</code> functionality you can do this by defining<br/><br/>
<code>moveToLeft: function(Multiselect, $options, event, silent, skipStack) { ... }</code><br/><br/>
<ul>
<li><code>Multiselect</code> - current instance of multiselect</li>
<li><code>$options</code> - jQuery element containing all selected options</li>
<li><code>event</code> - the event that initialised the action</li>
<li><code>silent</code> - <code>boolean</code> that tells if you have to trigger <code>beforeMoveToRight</code> and <code>afterMoveToRight</code></li>
<li><code>skipStack</code> - <code>boolean</code> that tells if you have to handle <code>undo/redo</code> stack</li>
</ul>
</td>
</tr>
<tr id="afterMoveToLeft">
<td>afterMoveToLeft</td>
<td><code>function</code></td>
<td>no action</td>
<td>
Whatever you want to do with <code>$left</code>, <code>$right</code> and <code>$options</code> elements after they are moved to right.<br/><br/>
<code>afterMoveToLeft: function($left, $right, $options) { ...; }</code><br/><br/>
<ul>
<li><code>$left</code> - jQuery element pointing to left side <code>select</code> element</li>
<li><code>$right</code> - jQuery element pointing to right side <code>select</code> element</li>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
</td>
</tr>
<tr id="beforeMoveUp">
<td>beforeMoveUp</td>
<td><code>function</code></td>
<td>return <code>true</code></td>
<td>
Whatever you want to do with <code>$options</code> elements before they are moved up.<br/><br/>
<code>beforeMoveUp: function( $options) { ...; return Boolean; }</code>.<br/><br/>
<ul>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
Keep in mind that this function must return a boolean value.<br/>
<ul>
<li><code>true</code> will let the action to be performed</li>
<li><code>false</code> will stop the action</li>
</ul>
</td>
</tr>
<tr id="afterMoveUp">
<td>afterMoveUp</td>
<td><code>function</code></td>
<td>no action</td>
<td>
Whatever you want to do with <code>$options</code> elements after they are moved up.<br/><br/>
<code>afterMoveUp: function($options) { ...; }</code><br/><br/>
<ul>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
</td>
</tr>
<tr id="beforeMoveDown">
<td>beforeMoveDown</td>
<td><code>function</code></td>
<td>return <code>true</code></td>
<td>
Whatever you want to do with <code>$options</code> elements before they are moved down.<br/><br/>
<code>beforeMoveDown: function( $options) { ...; return Boolean; }</code>.<br/><br/>
<ul>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
Keep in mind that this function must return a boolean value.<br/>
<ul>
<li><code>true</code> will let the action to be performed</li>
<li><code>false</code> will stop the action</li>
</ul>
</td>
</tr>
<tr id="afterMoveDown">
<td>afterMoveDown</td>
<td><code>function</code></td>
<td>no action</td>
<td>
Whatever you want to do with <code>$options</code> elements after they are moved down.<br/><br/>
<code>afterMoveDown: function($options) { ...; }</code><br/><br/>
<ul>
<li><code>$options</code> - jQuery element containing all selected options</li>
</ul>
</td>
</tr>
<tr>
<td>keepRenderingSort</td>
<td><code>boolean</code></td>
<td><code>false</code></td>
<td>
When you want to keep options sorted as they was rendered, <code>keepRenderingSort</code> must be <code>true</code>.<br/>
When <code>keepRenderingSort</code> is <code>true</code>, <code>sort</code> function will be ignored.
</td>
</tr>
<tr>
<td>submitAllLeft</td>
<td><code>boolean</code></td>
<td><code>true</code></td>
<td>
When you don&rsquo;t want to send all options from the left side to server, <code>submitAllLeft</code> must be <code>false</code>.
</td>
</tr>
<tr>
<td>submitAllRight</td>
<td><code>boolean</code></td>
<td><code>true</code></td>
<td>
When you don&rsquo;t want to send all options from the right side to server, <code>submitAllRight</code> must be <code>false</code>.
</td>
</tr>
<tr>
<td>search</td>
<td><code>Object</code></td>
<td><code>null</code></td>
<td>
When you want to have search fields for <code>left</code> and <code>right</code> elements you can do this by providing following value:<br/>
<pre class="prettyprint linenums">
search: {
left: &#39;&lt;input type=&quot;text&quot; name=&quot;q&quot; class=&quot;form-control&quot; placeholder=&quot;Search...&quot; /&gt;&#39;,
right: &#39;&lt;input type=&quot;text&quot; name=&quot;q&quot; class=&quot;form-control&quot; placeholder=&quot;Search...&quot; /&gt;&#39;,
}
</pre>
When you want to have search field only for <code>left</code> element you can do this by providing following value:<br/>
<pre class="prettyprint linenums">
search: {
left: &#39;&lt;input type=&quot;text&quot; name=&quot;q&quot; class=&quot;form-control&quot; placeholder=&quot;Search...&quot; /&gt;&#39;,
}
</pre>
</td>
</tr>
<tr id="fireSearch">
<td>fireSearch</td>
<td><code>function</code></td>
<td>return <code>true</code> if search length is great than 1</td>
<td>
Tell to multiselect when to start applying the search.<br/><br/>
<code>fireSearch: function(value) { ...; return Boolean; }</code><br/><br/>
<ul>
<li><code>value</code> - String search entered by the user</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div id="browser-support">
<div class="page-header"><h3>Browser Support</h3></div>
<h4>The following browsers are supported:</h4>
<ul>
<li>Internet Explorer 7+</li>
<li>Google Chrome</li>
<li>Mozilla Firefox</li>
<li>Safari</li>
<li>Opera</li>
</ul>
</div>
<div id="download">
<div class="page-header"><h3>Download</h3></div>
<div class="row">
<div class="col-xs-4">
<p><a class="btn btn-lg btn-primary" href="https://raw.github.com/crlcu/multiselect/master/dist/js/multiselect.js">Download multiselect.js</a></p>
</div>
<div class="col-xs-4">
<p><a class="btn btn-lg btn-success" href="https://raw.github.com/crlcu/multiselect/master/dist/js/multiselect.min.js">Download multiselect.min.js</a></p>
</div>
<div class="col-xs-4">
<p><a class="btn btn-lg btn-info" href="https://github.com/crlcu/multiselect/archive/master.zip">Download multiselect.zip</a></p>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-39934286-1', 'github.com');
ga('send', 'pageview');
</script>
<script type="text/javascript">
$(document).ready(function() {
// make code pretty
window.prettyPrint && prettyPrint();
if ( window.location.hash ) {
scrollTo(window.location.hash);
}
$('.nav').on('click', 'a.scroll', function(e) {
scrollTo($(this).attr('href'));
});
});
function scrollTo( id ) {
if ( $(id).length ) {
$('html,body').animate({scrollTop: $(id).offset().top - 60},'slow');
}
}
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@
{
"name": "multiselect-two-sides",
"version": "2.5.2",
"description": "jQuery multiselect plugin with two sides",
"main": "dist/js/multiselect.min.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"release": "standard-version"
},
"repository": {
"type": "git",
"url": "git+https://github.com/crlcu/multiselect.git"
},
"keywords": [
"multiselect"
],
"author": "Adrian Crisan <adrian.crisan88@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/crlcu/multiselect/issues"
},
"homepage": "https://github.com/crlcu/multiselect#readme",
"devDependencies": {
"gulp": "^3.9.1",
"gulp-rename": "^1.2.2",
"gulp-strip-debug": "^1.1.0",
"gulp-uglify": "^2.0.0",
"jasmine": "^2.5.3",
"standard-version": "^4.3.0"
},
"dependencies": {
"jquery": ">=1.7"
}
}

File diff suppressed because it is too large Load Diff

@ -1,143 +1,143 @@
.page-chat{
padding: 10px 0;
background:#ffffff;
}
.message-form-chat .nav{
margin-bottom: 0;
}
.message-form-chat .nav-tabs .active a{
background-color: #f6f6f6;
}
.message-student .chat-image, .message-teacher .chat-image{
width: 80px;
height: auto;
max-height: 80px;
display: inline-block;
vertical-align: top;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
border:2px solid #fff;
box-shadow: 1px 1px 2px rgba(0,0,0,0.2);
}
.message-student .content-message{
background-color: #F9F2E0;
.page-chat {
padding: 10px 0;
background: #ffffff;
}
.message-form-chat .nav {
margin-bottom: 0;
}
.message-form-chat .nav-tabs .active a {
background-color: #f6f6f6;
}
.message-student .chat-image, .message-teacher .chat-image {
width: 80px;
height: auto;
max-height: 80px;
display: inline-block;
vertical-align: top;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
border: 2px solid #fff;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
}
.message-student .content-message {
background-color: #F9F2E0;
color: #000;
padding: 10px;
margin-bottom: 15px;
border-top: 1px solid #F9F2E0;
border-left: 1px solid #F9F2E0;
border-right: 2px solid #F7E5B9;
border-bottom: 3px solid #F7E5B9;
border: 1px solid #F9F2E0;
border-right: 2px #F7E5B9;
border-bottom: 3px #F7E5B9;
border-radius: 10px;
display: inline-block;
font-size: 13px;
padding: 15px;
vertical-align: top;
width: calc(100% - 100px);
}
.message-teacher .icon-message {
display: inline-block;
margin-left: -6px;
margin-top: 10px;
width: 0;
height: 0;
border-top: 0 solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #EBEFF3;
}
display: inline-block;
font-size: 13px;
padding: 15px;
vertical-align: top;
width: calc(100% - 100px);
}
.message-teacher .icon-message{
display: inline-block;
margin-left: -6px;
margin-top: 10px;
width: 0;
height: 0;
border-top: 0px solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #EBEFF3;
}
.message-student .icon-message{
display: inline-block;
margin-right: -6px;
margin-top: 10px;
width: 0;
height: 0;
border-top: 0px solid transparent;
border-bottom: 15px solid transparent;
border-right: 15px solid #F9F2E0;
}
.message-teacher .content-message{
background-color: #EBEFF3;
.message-student .icon-message {
display: inline-block;
margin-right: -6px;
margin-top: 10px;
width: 0;
height: 0;
border-top: 0 solid transparent;
border-bottom: 15px solid transparent;
border-right: 15px solid #F9F2E0;
}
.message-teacher .content-message {
background-color: #EBEFF3;
color: #000;
padding: 10px;
margin-bottom: 15px;
border-top: 1px solid #EBEFF3;
border-left: 2px solid #DEE1E5;
border-right: 1px solid #EBEFF3;
border-bottom: 3px solid #DEE1E5;
border: 1px solid #EBEFF3;
border-bottom: 3px #DEE1E5;
border-left: 2px #DEE1E5;
border-radius: 10px;
display: inline-block;
font-size: 13px;
padding: 15px;
vertical-align: top;
width: calc(100% - 100px);
}
.message-teacher .message-date{
color: #666;
font-style: italic;
font-size: 10px;
text-align: right;
margin-right: 35px;
margin-top: 10px;
}
.message-student .message-date{
color: #666;
font-style: italic;
font-size: 10px;
text-align: right;
margin-top: 10px;
display: inline-block;
font-size: 13px;
padding: 15px;
vertical-align: top;
width: calc(100% - 100px);
}
.message-teacher .message-date {
color: #666;
font-style: italic;
font-size: 10px;
text-align: right;
margin-right: 35px;
margin-top: 10px;
}
.message-student .message-date {
color: #666;
font-style: italic;
font-size: 10px;
text-align: right;
margin-top: 10px;
}
.chat-user {
background-color: #EEEEEE;
border: 1px solid #E2E2E2;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
color: #666;
margin-bottom: 5px;
padding: 10px;
cursor: pointer;
background-color: #EEEEEE;
border: 1px solid #E2E2E2;
border-radius: 10px;
color: #666;
margin-bottom: 5px;
padding: 10px;
cursor: pointer;
}
.chat-user .user-image-chat {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border: 2px solid #FFF;
border-radius: 10px;
display: inline-block;
height: auto;
margin-right: 10px;
max-height: 40px;
float: left;
width: 40px;
}
.emoticons-chat img{
width: 24px;
border: none;
}
.message-form-chat{
margin-top: 10px;
padding: 5px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
background: #EEEEEE;
border: 2px solid #FFF;
border-radius: 10px;
display: inline-block;
height: auto;
margin-right: 10px;
max-height: 40px;
float: left;
width: 40px;
}
.emoticons-chat img {
width: 24px;
border: none;
}
.message-form-chat {
margin-top: 10px;
padding: 5px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
background: #EEEEEE;
}
.emoji-menu {
margin-left: -234px;
margin-top: -224px;
width: 468px;
margin-left: -234px;
margin-top: -224px;
width: 468px;
}
.emoji-wysiwyg-editor-preview,
.emoji-wysiwyg-editor {
padding: 3%;
height: 50px;
border: 0;
background-color: #ffffff;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
@ -147,18 +147,19 @@
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35) inset;
}
#chat-tabs .tab-content{
margin-top: 15px;
#chat-tabs .tab-content {
margin-top: 15px;
}
.chat-history {
height: 400px;
overflow: auto;
height: 400px;
overflow: auto;
}
#chat-users {
margin-left: 0;
margin-left: 0;
}
#chat-users div.chat-user > div {
padding: 5px;
padding: 5px;
}

@ -0,0 +1,36 @@
/*
Stylesheet for HTML documents created with CKEditor.
This stylesheet must import editor_content.css file
*/
@import url(./editor_content.css);
body {
color: #666;
font-family: 'Open Sans', sans-serif;
line-height: 30px;
padding: 1em;
}
blockquote {
background: #f9f9f9;
border-left: 8px solid #ccc;
display: inline-block;
font: 14px/20px italic Times, serif;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C" "\201D" "\2018" "\2019";
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote p {
display: inline;
}

@ -1,4 +1,6 @@
/*
Deprecated stylesheet. Use document.css.
Theme Name: Frame Scorm
URI Project: http://www.chamilo.org
Description: Styles main base of Chamilo LMS appearance, works with Bootstrap 3.0.x
@ -88,7 +90,7 @@ body{
}
.ck-title2:after{
background-color:#E95839;
margin: 10px 0px 0px;
margin: 10px 0 0;
width: 50px;
height: 2px;
display: block;
@ -100,17 +102,17 @@ figure .image.alignleft{
margin: 0 20px 20px 0;
}
figure .image.alignright{
margin: 0 20px 0px 20px;
margin: 0 20px 0 20px;
}
figure .image{
margin: 0 20px 0px 20px;
margin: 0 20px 0 20px;
}
figure figcaption{
text-align: left;
background-color: #f3f3f3;
padding-top: 0px;
margin: 0px;
border-radius: 0px;
padding-top: 0;
margin: 0;
border-radius: 0;
padding-left: 5px;
}
figure{
@ -263,6 +265,11 @@ blockquote p {
vertical-align: text-bottom;
}
.text-center .img-responsive {
margin-left: auto;
margin-right: auto;
}
@media (max-width: 767px) {
.img-responsive {
display: block;

@ -0,0 +1,216 @@
/*
Stylesheet for HTML blocks created with CKEditor to embed in Chamilo pages.
*/
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
src: local('Open Sans Light'),
local('OpenSans-Light'),
url(themes/chamilo/fonts/OpenSans-Light.woff2) format('woff2'),
url(themes/chamilo/fonts/OpenSans-Light.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans'),
local('OpenSans'),
url(themes/chamilo/fonts/OpenSans.woff2) format('woff2'),
url(themes/chamilo/fonts/OpenSans.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
src: local('Open Sans Semibold'),
local('OpenSans-Semibold'),
url(themes/chamilo/fonts/OpenSans-Semibold.woff2) format('woff2'),
url(themes/chamilo/fonts/OpenSans-Semibold.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'),
local('OpenSans-Bold'),
url(themes/chamilo/fonts/OpenSans-Bold.woff2) format('woff2'),
url(themes/chamilo/fonts/OpenSans-Bold.woff) format('woff');
}
body {
font-family: 'Open Sans', sans-serif;
}
/* Hack for show Bootstrap alerts in CKEditor' style select */
.cke_panel_listItem a > :first-child {
margin-bottom: 0;
}
.ck {
display: block;
font-feature-settings: normal;
font-kerning: auto;
font-language-override: normal;
font-size-adjust: none;
font-stretch: normal;
font-style: normal;
font-synthesis: weight style;
font-variant: normal;
font-weight: normal;
line-height: 1;
}
.ck-article {
color: #E95839;
background: url(../../main/img/document/border-title.png) repeat-x 0 80%;
font-weight: bold;
margin-bottom: 10px;
padding-bottom: 2%;
text-transform: uppercase;
}
.ck-article:before {
content: "\f15c";
font-family: FontAwesome;
font-weight: normal;
margin-right: 5px;
}
.ck-paragraph-box {
background-color: #F5EEE2;
line-height: 20px;
padding: 2% 3%;
}
.ck-title {
color: #000;
display: block;
font-weight: bold;
}
.ck-title2 {
color: #000;
font-weight: 500;
margin-top: 0;
padding-top: 0;
position: relative;
}
.ck-title2:after {
background-color: #E95839;
content: "";
display: block;
height: 2px;
margin: 10px 0 0;
width: 50px;
}
.ck-stand-out {
background-color: yellow;
}
.ck-style1 {
background: url(../../main/img/document/hr-1.png) repeat-x 0 0;
border: 0;
height: 6px;
}
.ck-style2 {
background: url(../../main/img/document/hr-2.png) repeat-x 0 0;
border: 0;
height: 6px;
}
.ck-style3 {
border-top: 1px dashed #8c8b8b;
}
.ck-style3:after {
background: #FFF;
color: #8c8b8b;
content: '\002702';
display: inline-block;
font-size: 18px;
left: 40px;
padding: 0 3px;
position: relative;
top: -12px;
}
.img-va-baseline {
vertical-align: baseline;
}
.img-va-top {
vertical-align: top;
}
.img-va-bottom {
vertical-align: bottom;
}
.img-va-middle {
vertical-align: middle;
}
.img-va-super {
vertical-align: super;
}
.img-va-sub {
vertical-align: sub;
}
.img-va-text-top {
vertical-align: text-top;
}
.img-va-text-bottom {
vertical-align: text-bottom;
}
figure.image {
border: 1px solid #ddd;
border-radius: 5px;
display: inline-block;
margin-bottom: 1em;
padding: 5px;
}
.cke_widget_image.pull-right,
img.pull-right,
figure.image.pull-right {
margin-left: 1em;
}
.cke_widget_image.pull-left,
img.pull-left,
figure.image.pull-left {
margin-right: 1em;
}
.text-center .img-responsive {
margin-left: auto;
margin-right: auto;
}
.cke_widget_image.pull-left figure.image,
.cke_widget_image.pull-right figure.image,
figure.image.pull-left,
figure.image.pull-right {
display: block;
}
figure.image figcaption {
background-color: #f3f3f3;
border-radius: 0;
margin: 0;
padding-left: 5px;
padding-top: 0;
text-align: left;
}

@ -91,9 +91,9 @@ float: left;
}
#contentfloatholder:after {
/* this is for NN6 to clear floats */
content: ".";
content: ".";
display: block;
height: 0px;
height: 0;
clear: both;
visibility: hidden;
}
@ -113,7 +113,7 @@ content: ".";
}
/* Hide from IE5-mac. Only IE-win sees this. \*/
* html #toolnav {
margin-right: 0px;
margin-right: 0;
}
* html #center {
height: 1%;
@ -169,13 +169,11 @@ content: ".";
/* normal and error message-box */
.normal-message, .error-message {
position: relative;
margin: 10px auto;
margin-left: -250px;
margin: 10px auto 10px -250px;
width: 500px;
left: 50%;
right: 50%;
border-width: 1px;
border-style: solid;
border: 1px solid;
padding: 5px;
}
.normal-message {
@ -261,7 +259,7 @@ dl.upload_option {
/* styles for general formatting */
.clear {
clear: both;
line-height: 0px;
line-height: 0;
height: 0;
}
p, blockquote, ol, ul {
@ -275,8 +273,8 @@ h2 {
}
h3 {
font-size: 15px;
margin-top:0px;
padding-top:0px;
margin-top:0;
padding-top:0;
}
h4 {
font-size: 12px;
@ -302,21 +300,21 @@ a:active {
}
input.link_alike {
background-color: #FFFFFF;
border-width: 0px;
border-width: 0;
color: #4171b5;
font-weight: bold;
text-align: left;
padding: 0px;
margin: 0px;
padding: 0;
margin: 0;
}
input.link_alike:hover {
background-color: #FFFFFF;
border-width: 0px;
border-width: 0;
color: #FF0000;
font-weight: bold;
text-align: left;
padding: 0px;
margin: 0px;
padding: 0;
margin: 0;
}
/* the following for the greyed out elements */
a.nobold:link, a.nobold:visited, a.nobold:active {
@ -553,8 +551,7 @@ padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;}
float: left;
position: relative;
margin: 0;
padding: 0px;
padding-top: 8px;
padding: 8px 0 0;
background: #fff;
}
.home_news {
@ -562,8 +559,7 @@ padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;}
float: right;
position: relative;
margin: 0;
padding: 0px;
padding-top: 8px;
padding: 8px 0 0;
background: #fff;
}
label.left {

@ -6,43 +6,50 @@ See https://support.chamilo.org/issues/6976
/* LP SCORM */
/* Default LP left column values */
.audio-scorm #container{
.audio-scorm #container {
min-height: 45px;
margin-bottom: 5px;
}
#author_image {
border: 1px solid #CCCCCC;
float: left;
margin: 0;
padding: 8px;
position: relative;
width: 100%;
border: 1px solid #CCCCCC;
float: left;
margin: 0;
padding: 8px;
position: relative;
width: 100%;
}
#author_name {
float: left;
text-align:center;
width: 100%;
font-size: 11px;
color: #888;
margin-top: 2px;
float: left;
text-align: center;
width: 100%;
font-size: 11px;
color: #888;
margin-top: 2px;
}
#learning_path_left_zone #scorm-gamification{
#learning_path_left_zone #scorm-gamification {
}
#scorm-gamification .fa-star{
#scorm-gamification .fa-star {
font-size: 20px;
color: #C2C2C2;
}
#scorm-gamification .fa-star.level{
#scorm-gamification .fa-star.level {
color: #D9534F;
}
#learning_path_left_zone .navegation-bar .buttons{
padding-top: 5px;
padding-bottom: 5px;
#learning_path_left_zone .navegation-bar .buttons {
padding-top: 5px;
padding-bottom: 5px;
}
#learning_path_left_zone .description-autor{
text-align: left;
padding-top: 10px;
#learning_path_left_zone .description-autor {
text-align: left;
}
.inner_lp_toc .scorm_item a {
font-weight: bold;
font-size: 14px;
@ -51,253 +58,311 @@ See https://support.chamilo.org/issues/6976
text-decoration: none;
color: #2F3E46;
}
.inner_lp_toc .scorm_item a.chapter_module {
font-weight: normal;
margin-right: 10px;
}
.inner_lp_toc .scorm_item_highlight {
border: 1px solid #999;
background:#999;
font-weight:bold;
text-shadow:0 -1px 1px #666;
background-image:-webkit-gradient(linear,left top,left bottom,from(#666),to(#999));
background-image:-webkit-linear-gradient(top,#666,#999);
background-image:-moz-linear-gradient(top,#666,#999);
background-image:-ms-linear-gradient(top,#666,#999);
background-image:-o-linear-gradient(top,#666,#999);
background-image:linear-gradient(top,#666,#999);
margin-right: 0px;
padding: 10px 0px 10px 0px;
background: #999;
font-weight: bold;
text-shadow: 0 -1px 1px #666;
background-image: -webkit-gradient(linear, left top, left bottom, from(#666), to(#999));
background-image: -webkit-linear-gradient(top, #666, #999);
background-image: -moz-linear-gradient(top, #666, #999);
background-image: -ms-linear-gradient(top, #666, #999);
background-image: -o-linear-gradient(top, #666, #999);
background-image: linear-gradient(top, #666, #999);
margin-right: 0;
padding: 10px 0 10px 0;
text-decoration: none;
}
.inner_lp_toc .scorm_item_highlight a {
color:#fff;
color: #fff;
margin-right: 1px;
text-decoration: none;
font-weight: bold;
}
.inner_lp_toc .scorm_item {
font-size: 16px;
margin-left: 10px;
margin-right:10px;
margin-right: 10px;
text-decoration: none;
border-color: rgba(255, 255, 255, 0.3);
}
.inner_lp_toc .scorm_item_section .scorm_item:before {
content : url('../../main/img/lp_section.png'); /* path from main/lp/lp_controller.php file */
content: url('../../main/img/lp_section.png'); /* path from main/lp/lp_controller.php file */
vertical-align: text-top;
margin-right : 5px;
margin-right: 5px;
}
/* learning path's classes for section and item for 5 levels of depth */
.scorm_section_level_0 {
margin: 0 0 0 0;
}
.scorm_section_level_1 {
margin: 0 0.5em 0 20px;
}
.scorm_section_level_2 {
margin: 0 0.5em 0 40px;
}
.scorm_section_level_3 {
margin: 0 0.5em 0 60px;
}
.scorm_section_level_4 {
margin: 0 0.5em 0 70px;
}
.scorm_section_level_5 {
margin: 0 0.5em 0 10em;
}
.scorm_item_normal{
.scorm_item_normal {
line-height: 25px;
border-bottom: 1px solid #DDD;
padding: 5px 0;
padding: 10px 0;
}
.level_0 {
margin: 0;
padding-left: 30px;
}
.level_1 {
margin: 0 0.5em 0 20px;
padding-left: 30px;
}
.level_2 {
margin: 0 0.5em 0 40px;
padding-left: 30px;
}
.level_3 {
margin: 0 0.5em 0 60px;
padding-left: 30px;
padding-top: 5px;
padding-bottom: 5px;
padding-right: 5px;
padding: 5px 5px 5px 30px;
line-height: 14px;
}
.level_4 {
margin: 0 0.5em 0 70px;
padding-left: 30px;
}
.level_5 {
margin: 0 0.5em 0 90px;
padding-left: 30px;
}
#learning_path_breadcrumb_zone .breadcrumb{
background-color: #2b3d53;
border-radius: 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
color: #ffffff;
font-size: 12px;
#learning_path_breadcrumb_zone .breadcrumb {
background-color: #2b3d53;
border-radius: 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
color: #ffffff;
font-size: 12px;
}
#learning_path_breadcrumb_zone .breadcrumb a{
color: #ffffff;
text-decoration: none;
#learning_path_breadcrumb_zone .breadcrumb a {
color: #ffffff;
text-decoration: none;
}
#learning_path_breadcrumb_zone .breadcrumb a:hover{
color: #dddddd;
#learning_path_breadcrumb_zone .breadcrumb a:hover {
color: #dddddd;
}
.total{
width: 100%;
.total {
width: 100%;
}
.view-options{
.view-options {
padding-top: 3px;
margin-right: 5px;
}
.panel-default .panel-heading .btn{
top: -25px;
.panel-default .panel-heading .btn {
top: -25px;
}
.panel-default .panel-heading .dropdown-menu{
top: 30%;
.panel-default .panel-heading .dropdown-menu {
top: 30%;
}
#content-scorm .breadcrumb{
padding: 5px;
#content-scorm .breadcrumb {
padding: 5px;
}
.scorm-heading{
font-size: 14px;
padding-top: 5px;
padding-bottom: 5px;
.scorm-heading {
font-size: 14px;
padding-top: 5px;
padding-bottom: 5px;
}
#control-bottom{
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
left: 0;
#control-bottom {
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
left: 0;
}
#control-bottom.well{
margin-bottom: 0;
#control-bottom.well {
margin-bottom: 0;
}
/*SCORM CSS BASE */
#learning_path_left_zone .home{
display: inline-block;
width: 94%;
margin-bottom: .5em;
margin-top: .5em;
font-size: 12px;
}
#learning_path_left_zone .scorm_title{
font-size: 20px;
color: #666;
margin-bottom: .5em;
margin-top: .5em;
#learning_path_left_zone .home {
display: inline-block;
width: 94%;
margin-bottom: .5em;
margin-top: .5em;
font-size: 12px;
}
/* for section */
.scorm_item_section{
.scorm_item_section {
border-bottom: 1px solid #DDD;
font-size: 12px;
font-size: 14px;
font-weight: bold;
}
.scorm_item_section .section{
background:url("../../main/img/scorm/folder-item-closed.png") no-repeat 10px center;
.scorm_item_section .section {
background: url("../../main/img/scorm/folder-item-closed.png") no-repeat 10px center;
padding: 10px 20px 10px 35px;
}
.scorm_item_section.scorm_completed .section{
background:url("../../main/img/scorm/folder-item-open.png") no-repeat 10px center;
padding-right: 1.5em;
.scorm_item_section.scorm_completed .section {
background: url("../../main/img/scorm/folder-item-open.png") no-repeat 10px center;
padding-right: 1.5em;
}
.scorm_item_normal a.items-list{
color: #666666;
font-size: 12px;
.scorm_item_normal a.items-list {
color: #666666;
display: inline-block;
width: 100%;
}
.scorm_item_normal a:hover{
text-decoration: none;
.scorm_item_normal a:hover {
text-decoration: none;
}
.scorm_item_normal.scorm_completed{
background-color: #F5F5F5;
.scorm_item_normal.scorm_completed {
background-color: #F5F5F5;
}
.scorm_item_section.scorm_completed{
background-color: #F5F5F5;
color: #000;
.scorm_item_section.scorm_completed {
background-color: #F5F5F5;
color: #000;
}
.scorm_completed .item {
background:url("../../main/img/scorm/scorm_completed.png") no-repeat 10px center;
background: url("../../main/img/scorm/scorm_completed.png") no-repeat 10px center;
}
.scorm_completed .item a{
color: #000;
.scorm_completed .item a {
color: #000;
}
.scorm_failed .item{
background:url("../../main/img/scorm/scorm_failed.png") no-repeat 10px center;
.scorm_failed .item {
background: url("../../main/img/scorm/scorm_failed.png") no-repeat 10px center;
}
.scorm_not_attempted .item{
background:url("../../main/img/scorm/scorm_not_attempted.png") no-repeat 10px center;
.scorm_not_attempted .item {
background: url("../../main/img/scorm/scorm_not_attempted.png") no-repeat 10px center;
}
.scorm_highlight .item{
background:url("../../main/img/scorm/scorm_highlight.png") no-repeat 10px center !important;
color: #009AB8;
.scorm_highlight .item {
background: url("../../main/img/scorm/scorm_highlight.png") no-repeat 10px center !important;
color: #009AB8;
}
.scorm_highlight.scorm_completed .item{
background:url("../../main/img/scorm/scorm_current.png") no-repeat 10px center !important;
.scorm_highlight.scorm_completed .item {
background: url("../../main/img/scorm/scorm_current.png") no-repeat 10px center !important;
}
.scorm_highlight.scorm_failed .item{
background:url("../../main/img/scorm/scorm_failed.png") no-repeat 10px center !important;
.scorm_highlight.scorm_failed .item {
background: url("../../main/img/scorm/scorm_failed.png") no-repeat 10px center !important;
}
.scorm_highlight a{
color: #FFFFFF !important;
.scorm_highlight a {
/* color: #FFFFFF !important;*/
}
.scorm_item_normal.scorm_highlight{
.scorm_item_normal.scorm_highlight {
background-color: #00829C;
}
.scorm_item_normal.scorm_highlight a {
/* color: #FFFFFF; once again this make links invisible!! */
}
/* END SCORM CSS BASE*/
#scorm-info{
padding: 10px;
margin-bottom: 5px;
border: 1px solid #ddd;
border-radius: 5px;
#scorm-info {
padding: 0;
background-color: #EBF3F5;
display: inline-block;
width: 100%;
}
#scorm-info .description-autor p {
font-size: 12px;
line-height: 20px;
color: #666666;
}
#scorm-info hr {
background-color: #DDD;
width: 100%;
border-top: 1px solid #d7d7d7;
margin-top: 5px;
margin-bottom: 10px;
}
#scorm-info #progress_bar .progress{
#scorm-info #progress_bar .progress {
margin-bottom: 5px;
}
.image-avatar .media-left img,
.image-avatar img{
.image-avatar img {
border: 1px solid #DDD;
border-radius: 5px;
}
.image-avatar{
.image-avatar {
padding-top: 10px;
padding-bottom: 10px;
}
.scorm-title{
color: #333;
.scorm-title {
padding: 10px;
display: inline-block;
width: 100%;
}
.scorm-body{
display: inline-block;
width: 100%;
}
.scorm-title h4 {
font-size: 18px;
font-weight: bold;
line-height: 1.5em;
margin: 0 0 15px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-bottom: 5px;
border-bottom: 1px solid #DDD;
color: #686f7a;
}
#learning_path_right_zone .lp-view-tabs li a {
padding: 5px 10px;
}
#learning_path_main {
height: 100%;
margin: 0;
@ -305,6 +370,7 @@ See https://support.chamilo.org/issues/6976
position: absolute;
width: 100%;
}
#learning_path_left_zone,
#learning_path_right_zone {
bottom: 0;
@ -318,36 +384,42 @@ See https://support.chamilo.org/issues/6976
transition-property: left, width;
transition-duration: 0.5s;
}
#learning_path_right_zone #wrapper-iframe {
height: 100%;
width: 100%;
}
#learning_path_left_zone .lp-view-zone-container,
#learning_path_right_zone .lp-view-zone-container {
bottom: 0;
left: 0;
padding: 5px;
padding: 0;
position: absolute;
right: 0;
top: 0;
}
#learning_path_toc {
bottom: 5px;
left: 5px;
bottom: 0;
left: 0;
overflow: auto;
position: absolute;
right: 5px;
right: 0;
top: 100%;
transition-property: top;
transition-duration: 0.1s;
}
#learning_path_right_zone {
left: 100%;
width: 100%;
}
#learning_path_right_zone.no-right-col {
left: 0;
}
#learning_path_right_zone .lp-view-tabs .tab-content {
bottom: 5px;
left: 5px;
@ -355,35 +427,43 @@ See https://support.chamilo.org/issues/6976
right: 5px;
top: 60px;
}
#learning_path_right_zone .tab-pane{
#learning_path_right_zone .tab-pane {
height: 100%;
position: relative;
}
#learning_path_right_zone .tab-pane iframe {
border: 0 none;
height: 100%;
width: 100%;
}
#learning_path_main.lp-view-collapsed #learning_path_left_zone {
left: -100%;
}
#learning_path_main.lp-view-collapsed #learning_path_right_zone {
left: 0;
width: 100%;
}
.nav-tabs li.active a{
.nav-tabs li.active a {
color: #006A84;
}
.nav-tabs li a{
.nav-tabs li a {
color: #666666;
}
#lp-view-expand-button{
#lp-view-expand-button {
border: none;
background: none;
padding: 0;
margin: 0;
}
.icon-toolbar .fa{
.icon-toolbar .fa {
width: 35px;
height: 35px;
line-height: 35px;
@ -393,36 +473,34 @@ See https://support.chamilo.org/issues/6976
font-size: 20px;
color: #FFF;
}
.icon-toolbar .fa:hover{
.icon-toolbar .fa:hover {
background-color: #000;
}
.expand .fa{
.expand .fa {
background-color: #00829C;
}
.expand .fa:hover{
.expand .fa:hover {
background-color: #006A84;
color: #ffffff;
}
.expand .fa:active, .expand .fa:focus{
.expand .fa:active, .expand .fa:focus {
color: #ffffff;
}
#learning_path_toc {
bottom: 10px;
left: 10px;
right: 5px;
margin-top:20px;
border: 1px solid #ddd;
padding: 10px;
border-radius: 5px;
}
#scorm-info .progress-bar{
#scorm-info .progress-bar {
line-height: 17px;
}
.actions_lp{
.actions_lp {
display: inline-block;
width: 100%;
text-align: center;
}
.navegation-bar {
display: block;
padding: 5px 10px 5px 5px;
@ -431,44 +509,128 @@ See https://support.chamilo.org/issues/6976
position: absolute;
z-index: 9999;
}
/* LP VIEW COLLAPSE */
.scorm-collapse .panel:hover {
box-shadow: 0 3px 1px 0 rgba(20, 23, 28, .1);
}
.scorm-collapse .panel-default .panel-heading {
background: url(../../../../main/img/icons/svg/arrow-down.svg) #FFF right center no-repeat;
padding-top: 0;
padding-bottom: 0;
}
.scorm-collapse a.item-header {
padding-bottom: 4px;
padding-top: 10px;
}
.scorm-collapse .panel-default .panel-heading h4 {
font-size: 18px;
font-weight: 600;
}
.scorm-collapse a {
color: #686f7a;
}
.scorm-collapse a.item-action,
.scorm-collapse a.item-header {
width: 100%;
display: inline-block;
}
.scorm-collapse .panel-body {
padding: 0;
}
.scorm-collapse .panel-body .list {
list-style: none;
padding: 0;
margin: 0;
}
.scorm-collapse .panel-body .list li {
padding: 15px 30px 15px 20px;
border-bottom: 1px solid #dddddd;
margin-bottom: 0;
}
.scorm-collapse .panel-body .list li:last-child {
border-bottom: none;
}
.scorm_item_normal.scorm_completed {
background: url(../../../../main/img/icons/svg/check-completed.svg) #F5F5F5 right center no-repeat;
}
.scorm_item_normal.scorm_not_attempted {
background: url(../../../../main/img/icons/svg/check-not-attempted.svg) #FFFFFF right center no-repeat;
}
.scorm_item_normal.scorm_highlight.scorm_not_attempted {
background: url(../../../../main/img/icons/svg/check-not-attempted.svg) #00829C right center no-repeat;
}
.scorm_item_normal.scorm_highlight.scorm_completed {
background: url(../../../../main/img/icons/svg/check-highlight.svg) #00829C right center no-repeat;
}
.sidebar-scorm {
border-right: 1px solid #e6e6e6;
}
.scorm-collapse-list {
padding: 0;
margin: 0 0 10px 0;
list-style: none;
}
.scorm-collapse-list li {
margin-bottom: 0;
padding-bottom: 10px;
padding-top: 10px;
}
.scorm-collapse-list .sub-item {
padding-left: 20px;
}
.scorm_item_normal.scorm_highlight a {
color: #FFFFFF;
}
/* END LP VIEW COLLAPSE */
/* Small devices (tablets, 768px and up) */
@media (min-width: 768px) {
#scorm-info{
padding: 10px;
margin-bottom: 10px;
}
#learning_path_left_zone {
width: 250px;
}
#learning_path_right_zone {
left: 250px;
width: calc(100% - 250px);
}
#learning_path_right_zone.no-right-col {
width: 100%;
}
#learning_path_main.lp-view-include-breadcrumb #learning_path_left_zone,
#learning_path_main.lp-view-include-breadcrumb #learning_path_right_zone {
top: 0;
}
#learning_path_left_zone .lp-view-zone-container,
#learning_path_right_zone .lp-view-zone-container {
padding: 10px;
}
#learning_path_left_zone .lp-view-zone-container {
padding-right: 5px;
}
#learning_path_right_zone .lp-view-zone-container {
padding-left: 5px;
}
#learning_path_right_zone .lp-view-tabs .tab-content {
bottom: 10px;
right: 10px;
top: 60px;
}
#learning_path_right_zone .tab-pane{
#learning_path_right_zone .tab-pane {
}
#learning_path_right_zone .tab-pane iframe {
}
}
@ -488,90 +650,115 @@ See https://support.chamilo.org/issues/6976
/* Large devices (large desktops, 1200px and up) */
@media (min-width: 1200px) {
#learning_path_left_zone {
width: 350px;
width: 450px;
}
#learning_path_right_zone {
left: 350px;
width: calc(100% - 350px);
left: 465px;
width: calc(100% - 465px);
}
}
@media (min-width: 979px) and (max-width: 1024px) {
#panel-scorm .image-avatar {
display: none;
}
}
@media (min-width : 480px) and (max-width: 767px) {
#panel-scorm .image-avatar{
@media (min-width: 768px) and (max-width: 978px) {
#panel-scorm .image-avatar {
display: none;
}
}
@media (min-width: 480px) and (max-width: 767px) {
#panel-scorm .image-avatar {
display: none;
}
#scorm-info {
padding: 0;
margin: 0;
padding: 0;
margin: 0;
}
#learning_path_left_zone .lp-view-zone-container,
#learning_path_right_zone .lp-view-zone-container{
#learning_path_right_zone .lp-view-zone-container {
padding: 0;
}
#scorm-info {
padding: 30px 0 0 0;
margin: 0;
padding: 30px 0 0 0;
margin: 0;
}
.nav-tabs-bar{
.nav-tabs-bar {
display: none;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
.scorm-title{
.scorm-title {
font-size: 14px;
padding: 10px;
}
.movil-toolbar{
.movil-toolbar {
width: 100%;
height: auto;
}
.scorm_item_normal{
.scorm_item_normal {
padding: 10px 5px;
}
.actions_lp .btn-group .btn-sm{
padding: 10px;
}
.icon-toolbar .fa {
width: 35px;
height: 35px;
line-height: 35px;
font-size: 22px;
width: 35px;
height: 35px;
line-height: 35px;
font-size: 22px;
}
.btn-movil {
width: 130px;
margin: auto;
width: 130px;
margin: auto;
}
#scorm-info {
padding: 30px 0 0 0;
margin: 0;
}
#learning_path_toc {
left: 0;
right: 0;
padding: 0;
border-radius: 5px;
padding: 30px 0 0 0;
margin: 0;
}
#learning_path_right_zone {
left: 100%;
width: auto;
}
#panel-scorm .image-avatar{
#panel-scorm .image-avatar {
display: none;
}
.navegation-bar {
display: block;
padding: 2px;
position: fixed;
top: 0;
right: 15px;
z-index: 999;
display: block;
padding: 2px;
position: fixed;
top: 0;
right: 0;
z-index: 999;
width: 100%;
text-align: center;
}
.nav-tabs-bar{
.nav-tabs-bar {
display: none;
}
#learning_path_left_zone .lp-view-zone-container,
#learning_path_right_zone .lp-view-zone-container{
#learning_path_right_zone .lp-view-zone-container {
padding: 0;
}
#scorm-info hr {
display: none;
}
}

@ -52,6 +52,9 @@ a:focus {
-moz-border-radius: 0;
-webkit-border-radius: 0;
}
.navbar-nav > li{
margin-bottom: 0;
}
.navbar-collapse{
overflow: hidden !important;
}

@ -79,7 +79,6 @@ assetic:
# - bundles/chamilocore/components/jquery.scrollbar/jquery.scrollbar.js
# - bundles/chamilocore/components/mediaelement/build/mediaelement-and-player.js
# - bundles/chamilocore/js/upload.js
# - bundles/chamilocore/js/pear/qfamsHandler.js
# - bundles/chamilocore/components/image-map-resizer/js/imageMapResizer.js
#
# - bundles/chamilocore/components/blueimp-load-image/js/load-image.all.min.js

@ -8,36 +8,3 @@
*/
$course_info_is_editable = true;
/*
//if (basename($_SERVER['SCRIPT_FILENAME']) == basename(__FILE__)) die('Va voir ailleurs');
$showLinkToExportThisCourse = true;
$showLinkToBackupThisCourse = true;
$showLinkToRecycleThisCourse = true;
$showLinkToRestoreCourse = true;
$showLinkToCopyThisCourse = true;
*/
// If true, these fileds keep the previous content.
/*
$canBeEmpty['screenCode'] = false;
$canBeEmpty['course_title'] = false;
$canBeEmpty['course_category'] = true;
$canBeEmpty['description'] = true;
$canBeEmpty['visibility'] = false;
$canBeEmpty['titulary'] = false;
$canBeEmpty['course_language'] = false;
$canBeEmpty['department_name'] = true;
$canBeEmpty['department_url'] = true;
*/
$showDiskQuota = true;
//$showDiskUse = true;
//$showLinkToChangeDiskQuota = true;
$showExpirationDate = true;
$showCreationDate = true;
$showLastEdit = true;
$showLastVisit = true;
$canReportExpirationDate = true; // Needs to be true
// if ScriptToReportExpirationDate
// is not automaticly called
//$linkToChangeDiskQuota = 'changeQuota.php';
$urlScriptToReportExpirationDate = 'postpone.php'; // external script to postpone the expiration of course.

@ -33,7 +33,10 @@
"qtip2": "*",
"js-cookie": "2.1.*",
"flag-icon-css": "*",
"jquery.easy-pie-chart": "^2.1.6"
"jquery.easy-pie-chart": "^2.1.6",
"jqueryui-touch-punch": "*",
"multiselect-two-sides": "*"
},
"resolutions": {
"jquery": "2.1.4"

@ -10,6 +10,8 @@ require_once '../main/inc/global.inc.php';
$action = isset($_GET['action']) ? $_GET['action'] : null;
$certificate = new Certificate($_GET['id']);
CustomCertificatePlugin::redirectCheck($certificate, $_GET['id']);
switch ($action) {
case 'export':
$hideExportLink = api_get_setting('hide_certificate_export_link');

@ -44,8 +44,13 @@ $rootWeb = api_get_path('WEB_PATH');
<head>
<title>Custompage - login</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="<?php echo $rootWeb; ?>web/assets/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="<?php echo $rootWeb; ?>web/assets/flag-icon-css/css/flag-icon.min.css" />
<script type="text/javascript" src="<?php echo $rootWeb; ?>web/assets/jquery/dist/jquery.min.js"></script>
<script type="text/javascript">
<script type="text/javascript" src="<?php echo $rootWeb; ?>web/assets/bootstrap/dist/js/bootstrap.min.js"></script>
<script>
$(document).ready(function() {
if (top.location != location) {
top.location.href = document.location.href;

@ -16,6 +16,9 @@ $rootWeb = api_get_path('WEB_PATH');
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="<?php echo $rootWeb; ?>web/assets/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="<?php echo $rootWeb; ?>web/assets/bootstrap/dist/css/bootstrap.min.css" />
<script type="text/javascript" src="<?php echo $rootWeb; ?>web/assets/bootstrap/dist/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
// Handler pour la touche retour

@ -57,24 +57,19 @@
date = city;
city = '';
}
var link = $("<a/>", {
text: title,
"title": title,
"href": "#"+version
});
$("#index").append(
$('<tr>').append($('<td>').append(link), $('<td>').append(city), $('<td>').append(date))
);
});
});
</script>
<div class="container"><div class="row"><div class="col-md-12">
<ul class="breadcrumb">
<li>
<a href="index.html">Documentation</a>
@ -120,6 +115,9 @@
<h3>Release name</h3>
<p>?????????????</p>
<h3>Security fixes</h3>
<ul aria-role="log" aria-live="off">
<li>[2018-05-29] (<a href="https://github.com/chamilo/chamilo-lms/commit/0de84700648f098c1fbf6b807dee28ec640efe62">4ffe5edb</a> - <a href="https://github.com/chamilo/chamilo-lms/issues/2532">#2532</a>) Security: Add Security::remove_XSS to clean variables from $_REQUEST</li>
</ul>
<h3>Possibly breaking changes</h3>
<h3>Notable new Features</h3>
<h4>For end-users, teachers and Chamilo admins</h4>
@ -131,6 +129,7 @@
<h3>Improvements (minor features) and debug</h3>
<h3>Stylesheets and theming</h3>
<h3>Web services</h3>
<li>Parameters encode change from bas64 to json in file main/webservices/api/v2.php <a href="https://github.com/chamilo/chamilo-lms/commit/0de84700648f098c1fbf6b807dee28ec640efe62">4ffe5edb</a></li>
<h3>Removals</h3>
<h3>Known issues</h3>
</div>

@ -827,6 +827,102 @@ If you have issues with files taking a long time to download, make sure you reco
You cannot, however, only allow .htaccess files in the main httpd.conf file, as OS X will override it with the
domain-specific configuration file.
</div>
<h3>IIS</h3>
<div>
User @ullfindsmit on Github was kind enough to provide this IIS configuration after testing it based on our
configurations above. This has not been tested by the Chamilo team, but the rules look legit:<br /><br />
<pre>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;configuration&gt;
&lt;system.webServer&gt;
&lt;rewrite&gt;
&lt;rules&gt;
&lt;rule name="rule 1v" stopProcessing="true"&gt;
&lt;match url="^certificates/$" /&gt;
&lt;action type="Rewrite" url="/certificates/index.php?id=%1" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 2v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/?$" /&gt;
&lt;action type="Rewrite" url="/main/course_home/course_home.php?cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 3v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/index.php$" /&gt;
&lt;action type="Rewrite" url="/main/course_home/course_home.php?cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 4v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/scorm/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/document/download_scorm.php?doc_url=/{R:2}&amp;cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 5v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/document/certificates/(.*)$" /&gt;
&lt;action type="Rewrite" url="/app/courses/{R:1}/document/certificates/{R:2}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 6v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/document/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/document/download.php?doc_url=/{R:2}&amp;cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 7v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/upload/([^/]+)/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/document/download_uploaded_files.php?code={R:1}&amp;type={R:2}&amp;file={R:3}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 8v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/work/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/work/download.php?file=work/{R:2}&amp;cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 9v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/course-pic85x85.png$" /&gt;
&lt;action type="Rewrite" url="/main/inc/ajax/course.ajax.php?a=get_course_image&amp;code={R:1}&amp;image=course_image_source" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 10v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/course-pic.png$" /&gt;
&lt;action type="Rewrite" url="/main/inc/ajax/course.ajax.php?a=get_course_image&amp;code={R:1}&amp;image=course_image_large_source" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 11v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/(.*)$" /&gt;
&lt;action type="Rewrite" url="/app/courses/{R:1}/{R:2}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 12v" stopProcessing="true"&gt;
&lt;match url="^session/(\d{1,})/about/?$" /&gt;
&lt;action type="Rewrite" url="/main/session/about.php?session_id={R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 13v" stopProcessing="true"&gt;
&lt;match url="^badge/(\d{1,})" /&gt;
&lt;action type="Rewrite" url="/main/badge/issued.php?issue={R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 14v" stopProcessing="true"&gt;
&lt;match url="^skill/(\d{1,})/user/(\d{1,})" /&gt;
&lt;action type="Rewrite" url="/main/badge/issued_all.php?skill={R:1}&amp;user={R:2}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 15v" stopProcessing="true"&gt;
&lt;match url="^badge/(\d{1,})/user/(\d{1,})" /&gt;
&lt;action type="Rewrite" url="/main/badge/issued_all.php?skill={R:1}&amp;user={R:2}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 16v" stopProcessing="true"&gt;
&lt;match url="^main/exercice/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/exercise/{R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 17v" stopProcessing="true"&gt;
&lt;match url="^main/newscorm/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/lp/{R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 18v" stopProcessing="true"&gt;
&lt;match url="^service/(\d{1,})$" /&gt;
&lt;action type="Rewrite" url="/plugin/buycourses/src/service_information.php?service_id={R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 19v" stopProcessing="true"&gt;
&lt;match url="^([^/.]+)/?$" /&gt;
&lt;action type="Rewrite" url="/user.php?{R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 20v" stopProcessing="true"&gt;
&lt;match url="^(tests|.git)" ignoreCase="true" /&gt;
&lt;action type="Rewrite" url="/-" /&gt;
&lt;/rule&gt;
&lt;/rules&gt;
&lt;/rewrite&gt;
&lt;/system.webServer&gt;
&lt;/configuration&gt;
</pre>
</div>
<h2><a name="17._Git_Upgrade"></a>15. Upgrading from Git</h2>
If you have sufficient experience with Git and have installed your initial Chamilo portal

File diff suppressed because it is too large Load Diff

@ -876,7 +876,106 @@ Ce sont uniquement les redirections à placer dans un bloc server{}, comme les a
deny all;
}
</pre>
<hr style="width: 100%; height: 2px;" />
<h3>IIS</h3>
<div>
L'utilisateur @ullfindsmit sur Github a été bien aimable en nous partageant la configuration suivante
pour IIS, qu'il a testé au préalable. Ces règles sont basées sur les configurations ci-dessus et, bien
que nous n'ayons pas testé la configuration nous-même, elle est suffisamment proche de la configuration
d'Apache et de Nginx pour nous permettre de vous la suggérer ici:<br /><br />
<pre>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;configuration&gt;
&lt;system.webServer&gt;
&lt;rewrite&gt;
&lt;rules&gt;
&lt;rule name="rule 1v" stopProcessing="true"&gt;
&lt;match url="^certificates/$" /&gt;
&lt;action type="Rewrite" url="/certificates/index.php?id=%1" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 2v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/?$" /&gt;
&lt;action type="Rewrite" url="/main/course_home/course_home.php?cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 3v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/index.php$" /&gt;
&lt;action type="Rewrite" url="/main/course_home/course_home.php?cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 4v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/scorm/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/document/download_scorm.php?doc_url=/{R:2}&amp;cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 5v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/document/certificates/(.*)$" /&gt;
&lt;action type="Rewrite" url="/app/courses/{R:1}/document/certificates/{R:2}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 6v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/document/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/document/download.php?doc_url=/{R:2}&amp;cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 7v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/upload/([^/]+)/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/document/download_uploaded_files.php?code={R:1}&amp;type={R:2}&amp;file={R:3}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 8v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/work/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/work/download.php?file=work/{R:2}&amp;cDir={R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 9v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/course-pic85x85.png$" /&gt;
&lt;action type="Rewrite" url="/main/inc/ajax/course.ajax.php?a=get_course_image&amp;code={R:1}&amp;image=course_image_source" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 10v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/course-pic.png$" /&gt;
&lt;action type="Rewrite" url="/main/inc/ajax/course.ajax.php?a=get_course_image&amp;code={R:1}&amp;image=course_image_large_source" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 11v" stopProcessing="true"&gt;
&lt;match url="^courses/([^/]+)/(.*)$" /&gt;
&lt;action type="Rewrite" url="/app/courses/{R:1}/{R:2}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 12v" stopProcessing="true"&gt;
&lt;match url="^session/(\d{1,})/about/?$" /&gt;
&lt;action type="Rewrite" url="/main/session/about.php?session_id={R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 13v" stopProcessing="true"&gt;
&lt;match url="^badge/(\d{1,})" /&gt;
&lt;action type="Rewrite" url="/main/badge/issued.php?issue={R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 14v" stopProcessing="true"&gt;
&lt;match url="^skill/(\d{1,})/user/(\d{1,})" /&gt;
&lt;action type="Rewrite" url="/main/badge/issued_all.php?skill={R:1}&amp;user={R:2}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 15v" stopProcessing="true"&gt;
&lt;match url="^badge/(\d{1,})/user/(\d{1,})" /&gt;
&lt;action type="Rewrite" url="/main/badge/issued_all.php?skill={R:1}&amp;user={R:2}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 16v" stopProcessing="true"&gt;
&lt;match url="^main/exercice/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/exercise/{R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 17v" stopProcessing="true"&gt;
&lt;match url="^main/newscorm/(.*)$" /&gt;
&lt;action type="Rewrite" url="/main/lp/{R:1}" appendQueryString="true" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 18v" stopProcessing="true"&gt;
&lt;match url="^service/(\d{1,})$" /&gt;
&lt;action type="Rewrite" url="/plugin/buycourses/src/service_information.php?service_id={R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 19v" stopProcessing="true"&gt;
&lt;match url="^([^/.]+)/?$" /&gt;
&lt;action type="Rewrite" url="/user.php?{R:1}" /&gt;
&lt;/rule&gt;
&lt;rule name="rule 20v" stopProcessing="true"&gt;
&lt;match url="^(tests|.git)" ignoreCase="true" /&gt;
&lt;action type="Rewrite" url="/-" /&gt;
&lt;/rule&gt;
&lt;/rules&gt;
&lt;/rewrite&gt;
&lt;/system.webServer&gt;
&lt;/configuration&gt;
</pre>
</div>
<hr style="width: 100%; height: 2px;" />
<p>
<br />

@ -205,6 +205,10 @@ ALTER TABLE c_lp_item_view ADD INDEX idx_clpiv_c_i_v (c_id, id, view_count);
<pre>
CREATE INDEX idx_accessurs_sid ON access_url_rel_session (session_id);
</pre>
And finally, if you have lots of gradebook stuff, add this
<pre>
ALTER TABLE gradebook_result ADD INDEX idx_gb_uid_eid (user_id, evaluation_id);
</pre>
<hr />
<h2><a name="3.Indexes-caching"></a>3. Indexes caching</h2>
One good reference: <a href="http://dev.mysql.com/doc/refman/5.6/en/multiple-key-caches.html">MySQL documentation on multiple key caches</a><br />

@ -106,35 +106,34 @@ This will prevent direct access to your settings and make it seem totally the sa
something that a .htaccess file cannot revert, and we need to set it
for each of those directories. This can be done as follows inside
your VirtualHost definition in Apache (you'll have to translate it for
Nginx configueations), where "/var/www/URL/" is the path of your VirtualHost web root:<br />
Nginx configurations), where "/var/www/URL/" is the path of your VirtualHost web root:<br />
<pre>
<Directory /var/www/URL/app/cache>
&lt;Directory /var/www/URL/app/cache&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/app/courses>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/app/courses&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/app/home>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/app/home&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/app/logs>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/app/logs&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/app/upload>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/app/upload&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/main/default_course_document/images>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/main/default_course_document/images&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/main/lang>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/main/lang&gt;
php_admin_value engine Off
</Directory>
<Directory /var/www/URL/web/css>
&lt;/Directory&gt;
&lt;Directory /var/www/URL/web/css&gt;
php_admin_value engine Off
</Directory>
&lt;/Directory&gt;
</pre>
<br />
<hr />
<h2><a name="6.HSTS">HTTP Headers Security</a></h2>
<p>A relatively recent development in web security, HTTP headers can be modified either

@ -14,13 +14,15 @@ define('CHAMILO_LOAD_WYSIWYG', false);
$cidReset = true;
require_once 'main/inc/global.inc.php';
$allow = api_get_configuration_value('plugin_redirection_enabled');
if ($allow) {
// The section (for the tabs).
$this_section = SECTION_CAMPUS; //rewritten below if including HTML file
$includeFile = !empty($_GET['include']);
if ($includeFile) {
$this_section = SECTION_INCLUDE;
} elseif (api_get_configuration_value('plugin_redirection_enabled')) {
RedirectionPlugin::redirectUser(api_get_user_id());
}
// The section (for the tabs).
$this_section = SECTION_CAMPUS;
$header_title = null;
if (!api_is_anonymous()) {
$header_title = ' ';
@ -61,7 +63,7 @@ if (api_get_setting('allow_terms_conditions') === 'true') {
Session::erase('term_and_condition');
}
//If we are not logged in and customapages activated
if (!api_get_user_id() && CustomPages::enabled()) {
if (!api_user_is_login() && CustomPages::enabled()) {
if (Request::get('loggedout')) {
CustomPages::display(CustomPages::LOGGED_OUT);
} else {
@ -97,6 +99,15 @@ if (!empty($_POST['submitAuth'])) {
// Only if login form was not sent because if the form is sent the user was already on the page.
Event::open();
}
if (!api_is_anonymous()) {
$url = api_get_configuration_value('redirect_index_to_url_for_logged_users');
if (!empty($url)) {
header("Location: $url");
exit;
}
}
if (api_get_setting('display_categories_on_homepage') === 'true') {
$controller->tpl->assign('course_category_block', $controller->return_courses_in_categories());
}
@ -106,11 +117,7 @@ $controller->set_login_form();
if (!api_is_anonymous()) {
$controller->tpl->assign('profile_block', $controller->return_profile_block());
$controller->tpl->assign('user_image_block', $controller->return_user_image_block());
if (api_is_platform_admin()) {
$controller->tpl->assign('course_block', $controller->return_course_block());
} else {
$controller->tpl->assign('teacher_block', $controller->return_teacher_link());
}
$controller->tpl->assign('course_block', $controller->return_course_block());
}
$hotCourses = '';
$announcements_block = '';
@ -143,8 +150,16 @@ if (api_get_configuration_value('show_hot_sessions') === true) {
}
$controller->tpl->assign('hot_courses', $hotCourses);
$controller->tpl->assign('announcements_block', $announcements_block);
$controller->tpl->assign('home_welcome', $controller->return_home_page());
$controller->tpl->assign('navigation_course_links', $controller->return_navigation_links());
if ($includeFile) {
// If we are including a static page, then home_welcome is empty
$controller->tpl->assign('home_welcome', '');
$controller->tpl->assign('home_include', $controller->return_home_page($includeFile));
} else {
// If we are including the real homepage, then home_include is empty
$controller->tpl->assign('home_welcome', $controller->return_home_page(false));
$controller->tpl->assign('home_include', '');
}
$controller->tpl->assign('navigation_links', $controller->return_navigation_links());
$controller->tpl->assign('notice_block', $controller->return_notice());
//$controller->tpl->assign('main_navigation_block', $controller->return_navigation_links());
$controller->tpl->assign('help_block', $controller->return_help());

@ -15,7 +15,7 @@ if (!isset($_REQUEST['u'])) {
}
$em = Database::getManager();
$relationsRepo = $em->getRepository('ChamiloCoreBundle:UserRelUser');
$userRepository = $em->getRepository('ChamiloUserBundle:User');
/** @var UserEntity $user */
$user = UserManager::getManager()->find($_REQUEST['u']);
@ -23,10 +23,12 @@ if (!$user) {
api_not_allowed(true);
}
$subscribedUsers = $user->getHrm();
$subscribedUsers = $userRepository->getAssignedHrmUserList(
$user->getId(),
api_get_current_access_url_id()
);
$hrmOptions = [];
/** @var UserRelUser $subscribedUser */
foreach ($subscribedUsers as $subscribedUser) {
/** @var UserEntity $hrm */
@ -58,7 +60,6 @@ if ($form->validate()) {
foreach ($subscribedUsers as $subscribedUser) {
$em->remove($subscribedUser);
}
$em->flush();
$values = $form->exportValues();

@ -380,11 +380,11 @@ if (!empty($action)) {
case 'edit_tabs':
case 'insert_link':
case 'edit_link':
$link_index = intval($_POST['link_index']);
$insert_where = intval($_POST['insert_where']);
$link_index = (isset($_POST['link_index']) ? intval($_POST['link_index']) : 0);
$insert_where = (isset($_POST['insert_where']) ? intval($_POST['insert_where']) : 0);
$link_name = trim(stripslashes($_POST['link_name']));
$link_url = trim(stripslashes($_POST['link_url']));
$add_in_tab = intval($_POST['add_in_tab']);
$add_in_tab = (isset($_POST['add_in_tab']) ? intval($_POST['add_in_tab']) : 0);
$link_html = trim(stripslashes($_POST['link_html']));
$filename = trim(stripslashes($_POST['filename']));
$target_blank = isset($_POST['target_blank']);

@ -229,7 +229,7 @@ if (!empty($coursesInSession) && $allowEditSessionCoaches) {
}
}
$groupName = 'session_coaches['.$sessionId.']';
$groupName = 'session_coaches_'.$sessionId;
$platformTeacherId = 'platform_teachers_by_session_'.$sessionId;
$coachId = 'coaches_by_session_'.$sessionId;
$platformTeacherName = 'platform_teachers_by_session';
@ -397,17 +397,21 @@ if ($form->validate()) {
// Updating teachers
if ($addTeacherToSessionCourses) {
// Updating session coaches
$sessionCoaches = $course['session_coaches'];
if (!empty($sessionCoaches)) {
foreach ($sessionCoaches as $sessionId => $teacherInfo) {
$coachesToSubscribe = $teacherInfo['coaches_by_session'];
SessionManager::updateCoaches(
$sessionId,
$courseId,
$coachesToSubscribe,
true
);
foreach ($coursesInSession as $session) {
$sessionId = $session['id'];
// Updating session coaches
$sessionCoaches = isset($course['session_coaches_'.$sessionId]) ? $course['session_coaches_'.$sessionId] : [];
if (!empty($sessionCoaches)) {
foreach ($sessionCoaches as $teacherInfo) {
$coachesToSubscribe = isset($teacherInfo['coaches_by_session']) ? $teacherInfo['coaches_by_session'] : [];
SessionManager::updateCoaches(
$sessionId,
$courseId,
$coachesToSubscribe,
true
);
}
}
}
@ -422,18 +426,18 @@ if ($form->validate()) {
// Normal behaviour
CourseManager::updateTeachers($courseInfo, $teachers, true, false);
// Updating session coaches
$sessionCoaches = isset($course['session_coaches']) ? $course['session_coaches'] : [];
if (!empty($sessionCoaches)) {
foreach ($sessionCoaches as $sessionId => $coachesToSubscribe) {
if (!empty($coachesToSubscribe)) {
SessionManager::updateCoaches(
$sessionId,
$courseId,
$coachesToSubscribe,
true
);
}
foreach ($coursesInSession as $session) {
$sessionId = $session['id'];
// Updating session coaches
$sessionCoaches = isset($course['session_coaches_'.$sessionId]) ? $course['session_coaches_'.$sessionId] : [];
if (!empty($sessionCoaches)) {
SessionManager::updateCoaches(
$sessionId,
$courseId,
$sessionCoaches,
true
);
}
}
}

@ -342,6 +342,8 @@ if (isset($_POST['action'])) {
$obj_cat->update_category_delete($course_code);
}
}
Display::addFlash(Display::return_message(get_lang('Deleted')));
break;
}
}
@ -409,6 +411,7 @@ if (isset($_GET['search']) && $_GET['search'] === 'advanced') {
CourseManager::delete_course($_GET['delete_course']);
$obj_cat = new Category();
$obj_cat->update_category_delete($_GET['delete_course']);
Display::addFlash(Display::return_message(get_lang('Deleted')));
}
// Create a search-box
$form = new FormValidator(

@ -66,7 +66,7 @@ $htmlHeadXtra[] = '<script>
if(confirm("'.get_lang('ConfirmYourChoice').'")) {
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("#id_content_message").html("<div class=\"warning-message alert alert-warning\"><em class=\"fa fa-refresh fa-spin\"></em> '.get_lang('Loading').'</div>");
},
type: "GET",
@ -108,7 +108,7 @@ $htmlHeadXtra[] = '<script>
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("#id_content_message").html("<div class=\"warning-message alert alert-warning\"><em class=\"fa fa-refresh fa-spin\"></em> '.get_lang('Loading').'</div>");
},
type: "POST",

@ -521,6 +521,10 @@ function uploadStylesheet($values, $picture)
$result = false;
// Valid name for the stylesheet folder.
$style_name = api_preg_replace('/[^A-Za-z0-9]/', '', $values['name_stylesheet']);
if (empty($style_name) || is_array($style_name)) {
// The name of the uploaded stylesheet doesn't have the expected format
return $result;
}
$cssToUpload = CSS_UPLOAD_PATH;
// Check if a virtual instance vchamilo is used
@ -1006,7 +1010,7 @@ function getNumberOfTemplates()
* @param int $from the start of the limit statement
* @param int $number_of_items the number of elements that have to be retrieved from the database
* @param int $column the column that is
* @param string $direction the sorting direction (ASC or DESC<EFBFBD>
* @param string $direction the sorting direction (ASC or DESC)
*
* @return array
*
@ -1120,8 +1124,7 @@ function addEditTemplate()
get_lang('Text'),
true,
true,
['ToolbarSet' => 'Documents', 'Width' => '100%', 'Height' => '400'],
true
['ToolbarSet' => 'Documents', 'Width' => '100%', 'Height' => '400']
);
// Setting the form elements: the form to upload an image to be used with the template.

@ -25,11 +25,7 @@ $nameTools = get_lang('SpecialExports');
$export = '';
$querypath = '';
// include additional libraries
if (function_exists('ini_set')) {
api_set_memory_limit('256M');
ini_set('max_execution_time', 0);
}
api_set_more_memory_and_time_limits();
// Displaying the header
Display::display_header($nameTools);

@ -14,22 +14,27 @@ api_protect_admin_script();
$interbreadcrumb[] = ['url' => '../index.php', 'name' => get_lang('PlatformAdmin')];
$report = isset($_REQUEST['report']) ? $_REQUEST['report'] : '';
$sessionDuration = isset($_GET['session_duration']) ? (int) $_GET['session_duration'] : '';
if ($report) {
if ($report == 'recentlogins') {
$htmlHeadXtra[] = api_get_js('chartjs/Chart.min.js');
$htmlHeadXtra[] = '
<script>
$(document).ready(function() {
$.ajax({
url: "'.api_get_path(WEB_CODE_PATH).'inc/ajax/statistics.ajax.php?a=recentlogins",
type: "POST",
success: function(data) {
Chart.defaults.global.responsive = true;
var myLine = new Chart(document.getElementById("canvas").getContext("2d")).Line(data);
}
});
<script>
$(document).ready(function() {
$.ajax({
url: "'.api_get_path(WEB_CODE_PATH).'inc/ajax/statistics.ajax.php?a=recentlogins&session_duration='.$sessionDuration.'",
type: "POST",
success: function(data) {
Chart.defaults.global.responsive = true;
var myLine = new Chart(document.getElementById("canvas").getContext("2d")).Line(data);
}
});
</script>';
});
</script>';
}
if ($report == 'user_session') {
$htmlHeadXtra[] = api_get_jqgrid_js();
}
$tool_name = get_lang('Statistics');
@ -61,6 +66,10 @@ $tools[$strUsers]['report=zombies'] = get_lang('Zombies');
// system ...
$tools[$strSystem]['report=activities'] = get_lang('ImportantActivities');
if (api_is_global_platform_admin() && api_is_multiple_url_enabled()) {
$tools[$strSystem]['report=user_session'] = get_lang('PortalUserSessionStats');
}
// social ...
$tools[$strSocial]['report=messagesent'] = get_lang('MessagesSent');
$tools[$strSocial]['report=messagereceived'] = get_lang('MessagesReceived');
@ -80,9 +89,106 @@ foreach ($tools as $section => $items) {
echo '</tr></table>';
$course_categories = Statistics::getCourseCategories();
echo '<br/><br/>'; //@todo: spaces between elements should be handled in the css, br should be removed if only there for presentation
//@todo: spaces between elements should be handled in the css, br should be removed if only there for presentation
echo '<br/><br/>';
switch ($report) {
case 'user_session':
$form = new FormValidator('user_session', 'get');
$form->addDateRangePicker('range', get_lang('DateRange'), true);
$form->addHidden('report', 'user_session');
$form->addButtonSearch(get_lang('Search'));
$date = new DateTime($now);
$startDate = $date->format('Y-m-d').' 00:00:00';
$endDate = $date->format('Y-m-d').' 23:59:59';
$start = $startDate;
$end = $endDate;
if ($form->validate()) {
$values = $form->getSubmitValues();
$start = $values['range_start'];
$end = $values['range_end'];
}
echo $form->returnForm();
$url = api_get_path(WEB_AJAX_PATH).'statistics.ajax.php?a=get_user_session&start='.$start.'&end='.$end;
$columns = [
'URL',
get_lang('Session'),
get_lang('Course'),
get_lang('CountUsers'),
];
$columnModel = [
[
'name' => 'url',
'index' => 'url',
'width' => '120',
'align' => 'left',
],
[
'name' => 'session',
'index' => 'session',
'width' => '180',
'align' => 'left',
'sortable' => 'false',
],
[
'name' => 'course',
'index' => 'course',
'width' => '100',
'align' => 'left',
'sortable' => 'false',
],
[
'name' => 'count',
'index' => 'count',
'width' => '50',
'align' => 'left',
'sortable' => 'false',
],
];
$extraParams['autowidth'] = 'true'; //use the width of the parent
$extraParams['height'] = 'auto'; //use the width of the parent
$actionLinks = '';
?>
<script>
$(function() {
<?php
echo Display::grid_js(
'user_session_grid',
$url,
$columns,
$columnModel,
$extraParams,
[],
$actionLinks,
true
);
?>
jQuery("#user_session_grid").jqGrid("navGrid","#user_session_grid_pager",{
view:false,
edit:false,
add:false,
del:false,
search:false,
excel:true
});
jQuery("#user_session_grid").jqGrid("navButtonAdd","#user_session_grid_pager", {
caption:"",
onClickButton : function () {
jQuery("#user_session_grid").jqGrid("excelExport",{"url":"<?php echo $url; ?>&export_format=xls"});
}
});
});
</script>
<?php
echo Display::grid_html('user_session_grid');
break;
case 'courses':
// total amount of courses
foreach ($course_categories as $code => $name) {
@ -123,9 +229,16 @@ switch ($report) {
break;
case 'recentlogins':
echo '<h2>'.sprintf(get_lang('LastXDays'), '15').'</h2>';
$form = new FormValidator('session_time', 'get', api_get_self().'?report=recentlogins&session_duration='.$sessionDuration);
$sessionTimeList = ['', 5 => 5, 15 => 15, 30 => 30, 60 => 60];
$form->addSelect('session_duration', [get_lang('SessionMinDuration'), get_lang('Minutes')], $sessionTimeList);
$form->addButtonSend(get_lang('Filter'));
$form->addHidden('report', 'recentlogins');
$form->display();
echo '<canvas class="col-md-12" id="canvas" height="100px" style="margin-bottom: 20px"></canvas>';
Statistics::printRecentLoginStats();
Statistics::printRecentLoginStats(true);
Statistics::printRecentLoginStats(false, $sessionDuration);
Statistics::printRecentLoginStats(true, $sessionDuration);
break;
case 'logins':
Statistics::printLoginStats($_GET['type']);

@ -27,7 +27,7 @@ $htmlHeadXtra[] = '<script>
if (is_new_language.length>0 && is_new_language!="_" && file_id!="" && button_name!="") {
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("#div_message_information_id").html("<div class=\"alert alert-info\"><img src=\'../inc/lib/javascript/indicator.gif\' /></div>");
},
type: "POST",

@ -230,7 +230,7 @@ if (!empty($selectedSession)) {
if (!empty($selectedTeacher)) {
$withFilter = true;
$teacher = api_get_user_info();
$teacher = api_get_user_info($selectedTeacher);
$teacherData = [
'userId' => $teacher['user_id'],
'lastname' => $teacher['lastname'],

@ -19,29 +19,6 @@ $is_platform_admin = api_is_platform_admin() ? 1 : 0;
$message = null;
$htmlHeadXtra[] = api_get_password_checker_js('#username', '#password');
$checkPass = api_get_setting('allow_strength_pass_checker');
if ($checkPass == 'true') {
$htmlHeadXtra[] = '
<script>
$(document).ready(function() {
$("#password").keypress(function() {
$("#password").each(function(index, value) {
var value = $(this).attr("value");
if (value == 0) {
$("#password_progress").show();
$(".password-verdict").show();
$(".error-list").show();
} else {
$("#password_progress").hide();
$(".password-verdict").hide();
$(".error-list").hide();
}
});
});
});
</script>';
}
$htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css');
$htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js');
$htmlHeadXtra[] = '
@ -247,15 +224,6 @@ $form->addGroup($group, 'password', get_lang('Password'));
$form->addPasswordRule('password', 'password');
$form->addGroupRule('password', get_lang('EnterPassword'), 'required', null, 1);
if ($checkPass) {
$strengthLabels = '
<div id="password-verdict"></div>
<div id="password-errors"></div>
<div id="password_progress" style="display:none"></div>
';
$form->addElement('label', null, $strengthLabels);
}
// Status
$status = [];
$status[COURSEMANAGER] = get_lang('Teacher');

@ -327,15 +327,30 @@ $returnParams = $extraField->addElements(
[],
true
);
$jquery_ready_content = $returnParams['jquery_ready_content'];
$jqueryReadyContent = $returnParams['jquery_ready_content'];
// the $jquery_ready_content variable collects all functions that will be load in the $(document).ready javascript function
// the $jqueryReadyContent variable collects all functions that will be load in the
// $(document).ready javascript function
$htmlHeadXtra[] = '<script>
$(document).ready(function(){
'.$jquery_ready_content.'
'.$jqueryReadyContent.'
});
</script>';
// Freeze user conditions, admin cannot updated them
$extraConditions = api_get_configuration_value('show_conditions_to_user');
if ($extraConditions && isset($extraConditions['conditions'])) {
$extraConditions = $extraConditions['conditions'];
foreach ($extraConditions as $condition) {
/** @var HTML_QuickForm_group $element */
$element = $form->getElement('extra_'.$condition['variable']);
if ($element) {
$element->freeze();
}
}
}
// Submit button
$form->addButtonSave(get_lang('Save'));

@ -223,7 +223,7 @@ function save_data($users)
foreach ($user['Sessions'] as $sessionId) {
$sessionInfo = api_get_session_info($sessionId);
if (!empty($sessionInfo)) {
SessionManager::subscribe_users_to_session(
SessionManager::subscribeUsersToSession(
$sessionId,
[$user_id],
SESSION_VISIBLE_READ_ONLY,
@ -387,7 +387,7 @@ function parse_xml_data($file)
}
$this_section = SECTION_PLATFORM_ADMIN;
api_protect_admin_script(true, null, 'login');
api_protect_admin_script(true, null);
api_protect_limit_for_session_admin();
$defined_auth_sources[] = PLATFORM_AUTH_SOURCE;

@ -620,7 +620,13 @@ if ($studentBossList) {
echo $studentBossListToString;
}
$hrmList = $userEntity->getHrm();
$em = Database::getManager();
$userRepository = $em->getRepository('ChamiloUserBundle:User');
$hrmList = $userRepository->getAssignedHrmUserList(
$userEntity->getId(),
api_get_current_access_url_id()
);
if ($hrmList) {
echo Display::page_subheader(get_lang('HrmList'));
@ -629,21 +635,20 @@ if ($hrmList) {
/** @var UserRelUser $hrm */
foreach ($hrmList as $hrm) {
$hrmInfo = api_get_user_info($hrm->getFriendUserId());
$userPicture = isset($hrmInfo["avatar_medium"]) ? $hrmInfo["avatar_medium"] : $hrmInfo["avatar"];
$userPicture = isset($hrmInfo['avatar_medium']) ? $hrmInfo['avatar_medium'] : $hrmInfo['avatar'];
echo '<div class="col-sm-4 col-md-3">';
echo '<div class="media">';
echo '<div class="media-left">';
echo Display::img($userPicture, $hrmInfo['complete_name'], ['class' => 'media-object'], false);
echo '</div>';
echo '<div class="media-body">';
echo '<h4 class="media-heading">'.$hrmInfo['complete_name'].'</h4>';
echo '<h4 class="media-heading">'.$hrmInfo['complete_name_with_message_link'].'</h4>';
echo $hrmInfo['username'];
echo '</div>';
echo '</div>';
echo '</div>';
}
echo '</div>';
}
@ -656,7 +661,7 @@ if ($user['status'] == DRH) {
foreach ($usersAssigned as $userAssigned) {
$userAssigned = api_get_user_info($userAssigned['user_id']);
$userPicture = isset($userAssigned["avatar_medium"]) ? $userAssigned["avatar_medium"] : $userAssigned["avatar"];
$userPicture = isset($userAssigned['avatar_medium']) ? $userAssigned['avatar_medium'] : $userAssigned['avatar'];
echo '<div class="col-sm-4 col-md-3">';
echo '<div class="media">';
@ -664,13 +669,12 @@ if ($user['status'] == DRH) {
echo Display::img($userPicture, $userAssigned['complete_name'], ['class' => 'media-object'], false);
echo '</div>';
echo '<div class="media-body">';
echo '<h4 class="media-heading">'.$userAssigned['complete_name'].'</h4>';
echo $userAssigned['username'];
echo '<h4 class="media-heading">'.$userAssigned['complete_name_with_message_link'].'</h4>';
echo $userAssigned['official_code'];
echo '</div>';
echo '</div>';
echo '</div>';
}
echo '</div>';
}
}

@ -76,7 +76,7 @@ $htmlHeadXtra[] = '<script>
function load_course_list (div_course,my_user_id) {
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("div#"+div_course).html("<img src=\'../inc/lib/javascript/indicator.gif\' />"); },
type: "POST",
url: "'.$url.'",
@ -92,7 +92,7 @@ function load_course_list (div_course,my_user_id) {
function load_session_list(div_session, my_user_id) {
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("div#"+div_session).html("<img src=\'../inc/lib/javascript/indicator.gif\' />"); },
type: "POST",
url: "'.$urlSession.'",
@ -119,7 +119,7 @@ function active_user(element_div) {
if (confirm("'.get_lang('AreYouSureToEditTheUserStatus', '').'")) {
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$(ident).attr("src","'.Display::returnIconPath('loading1.gif').'"); }, //candy eye stuff
type: "GET",
url: "'.api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=active_user",
@ -208,17 +208,17 @@ function trimVariables()
* Prepares the shared SQL query for the user table.
* See get_user_data() and get_number_of_users().
*
* @param bool $is_count Whether to count, or get data
* @param bool $getCount Whether to count, or get data
*
* @return string SQL query
*/
function prepare_user_sql_query($is_count)
function prepare_user_sql_query($getCount)
{
$sql = '';
$user_table = Database::get_main_table(TABLE_MAIN_USER);
$admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
if ($is_count) {
if ($getCount) {
$sql .= "SELECT COUNT(u.id) AS total_number_of_items FROM $user_table u";
} else {
$sql .= "SELECT u.id AS col0, u.official_code AS col2, ";
@ -325,6 +325,7 @@ function prepare_user_sql_query($is_count)
if (!empty($keywordListValues['keyword_officialcode'])) {
$sql .= " AND u.official_code LIKE '".Database::escape_string("%".$keywordListValues['keyword_officialcode']."%")."' ";
}
$sql .= "
$keyword_admin
$keyword_extra_value
@ -342,6 +343,11 @@ function prepare_user_sql_query($is_count)
$sql .= " ) ";
}
$preventSessionAdminsToManageAllUsers = api_get_setting('prevent_session_admins_to_manage_all_users');
if (api_is_session_admin() && $preventSessionAdminsToManageAllUsers === 'true') {
$sql .= " AND u.creator_id = ".api_get_user_id();
}
$variables = Session::read('variables_to_show', []);
if (!empty($variables)) {
$extraField = new ExtraField('user');
@ -434,6 +440,8 @@ function get_number_of_users()
* @param int Column to sort on
* @param string Order (ASC,DESC)
*
* @return array Users list
*
* @see SortableTable#get_table_data($from)
*/
function get_user_data($from, $number_of_items, $column, $direction)
@ -445,12 +453,6 @@ function get_user_data($from, $number_of_items, $column, $direction)
$column = intval($column);
$from = intval($from);
$number_of_items = intval($number_of_items);
$preventSessionAdminsToManageAllUsers = api_get_setting('prevent_session_admins_to_manage_all_users');
if (api_is_session_admin() && $preventSessionAdminsToManageAllUsers === 'true') {
$sql .= " WHERE u.creator_id = ".api_get_user_id();
}
$sql .= " ORDER BY col$column $direction ";
$sql .= " LIMIT $from,$number_of_items";
@ -511,7 +513,9 @@ function email_filter($email)
/**
* Returns a mailto-link.
*
* @param string $email An email-address
* @param string $email An email-address
* @param array $params Deprecated
* @param array $row
*
* @return string HTML-code with a mailto-link
*/
@ -527,6 +531,8 @@ function user_filter($name, $params, $row)
* @param string URL params to add to table links
* @param array Row of elements to alter
*
* @throws Exception
*
* @return string Some HTML-code with modify-buttons
*/
function modify_filter($user_id, $url_params, $row)
@ -887,17 +893,17 @@ if (!empty($action)) {
case 'delete':
if (api_is_platform_admin()) {
$number_of_selected_users = count($_POST['id']);
$number_of_deleted_users = 0;
$number_of_affected_users = 0;
if (is_array($_POST['id'])) {
foreach ($_POST['id'] as $index => $user_id) {
if ($user_id != $_user['user_id']) {
if (UserManager::delete_user($user_id)) {
$number_of_deleted_users++;
$number_of_affected_users++;
}
}
}
}
if ($number_of_selected_users == $number_of_deleted_users) {
if ($number_of_selected_users == $number_of_affected_users) {
$message = Display::return_message(
get_lang('SelectedUsersDeleted'),
'confirmation'
@ -910,6 +916,58 @@ if (!empty($action)) {
}
}
break;
case 'disable':
if (api_is_platform_admin()) {
$number_of_selected_users = count($_POST['id']);
$number_of_affected_users = 0;
if (is_array($_POST['id'])) {
foreach ($_POST['id'] as $index => $user_id) {
if ($user_id != $_user['user_id']) {
if (UserManager::disable($user_id)) {
$number_of_affected_users++;
}
}
}
}
if ($number_of_selected_users == $number_of_affected_users) {
$message = Display::return_message(
get_lang('SelectedUsersDisabled'),
'confirmation'
);
} else {
$message = Display::return_message(
get_lang('SomeUsersNotDisabled'),
'error'
);
}
}
break;
case 'enable':
if (api_is_platform_admin()) {
$number_of_selected_users = count($_POST['id']);
$number_of_affected_users = 0;
if (is_array($_POST['id'])) {
foreach ($_POST['id'] as $index => $user_id) {
if ($user_id != $_user['user_id']) {
if (UserManager::enable($user_id)) {
$number_of_affected_users++;
}
}
}
}
if ($number_of_selected_users == $number_of_affected_users) {
$message = Display::return_message(
get_lang('SelectedUsersEnabled'),
'confirmation'
);
} else {
$message = Display::return_message(
get_lang('SomeUsersNotEnabled'),
'error'
);
}
}
break;
}
Security::clear_token();
}
@ -1053,13 +1111,15 @@ $table->set_column_filter(8, 'active_filter');
$table->set_column_filter(10, 'modify_filter');
// Only show empty actions bar if delete users has been blocked
$actionsList = [];
if (api_is_platform_admin() &&
!api_get_configuration_value('deny_delete_users')
) {
$table->set_form_actions(['delete' => get_lang('DeleteFromPlatform')]);
} else {
$table->set_form_actions(['none' => get_lang('NoActionAvailable')]);
$actionsList['delete'] = get_lang('DeleteFromPlatform');
}
$actionsList['disable'] = get_lang('Disable');
$actionsList['enable'] = get_lang('Enable');
$table->set_form_actions($actionsList);
$table_result = $table->return_table();
$extra_search_options = '';

@ -115,7 +115,7 @@ if (isset($_REQUEST['load_ajax'])) {
echo 'User added to the session';
}
//Registering user to the new session
SessionManager::subscribe_users_to_session(
SessionManager::subscribeUsersToSession(
$new_session_id,
[$user_id],
false
@ -641,7 +641,7 @@ $htmlHeadXtra[] = '<script>
var session_id = document.getElementById(unique_id).options[document.getElementById(unique_id).selectedIndex].value;
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("div#reponse_"+unique_id).html("<img src=\'../inc/lib/javascript/indicator.gif\' />"); },
type: "POST",
url: "user_move_stats.php",
@ -656,7 +656,7 @@ $htmlHeadXtra[] = '<script>
$.ajax({
contentType: "application/x-www-form-urlencoded",
beforeSend: function(objeto) {
beforeSend: function(myObject) {
$("div#reponse_"+unique_id).html("<img src=\'../inc/lib/javascript/indicator.gif\' />"); },
type: "POST",
url: "user_move_stats.php",

@ -337,7 +337,7 @@ function parse_xml_data($file)
}
$this_section = SECTION_PLATFORM_ADMIN;
api_protect_admin_script(true, null, 'login');
api_protect_admin_script(true, null);
$defined_auth_sources[] = PLATFORM_AUTH_SOURCE;

@ -83,12 +83,11 @@ if (!empty($group_id)) {
}
}
/* Tracking */
/* Tracking */
Event::event_access_tool(TOOL_ANNOUNCEMENT);
$announcement_id = isset($_GET['id']) ? intval($_GET['id']) : null;
$action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : 'list';
$announcement_number = AnnouncementManager::getNumberAnnouncements();
$homeUrl = api_get_self().'?action=list&'.api_get_cidreq();
@ -97,6 +96,10 @@ $searchFormToString = '';
switch ($action) {
case 'move':
if (!$allowToEdit) {
api_not_allowed(true);
}
/* Move announcement up/down */
if (!empty($_GET['down'])) {
$thisAnnouncementId = intval($_GET['down']);
@ -114,7 +117,6 @@ switch ($action) {
}
$announcementInfo = AnnouncementManager::get_by_id($courseId, $thisAnnouncementId);
$sql = "SELECT DISTINCT announcement.id, announcement.display_order
FROM $tbl_announcement announcement,
$tbl_item_property itemproperty
@ -138,7 +140,6 @@ switch ($action) {
Database::query($sql);
$sql = "UPDATE $tbl_announcement SET display_order = '$thisAnnouncementOrder'
WHERE c_id = $courseId AND id = $nextAnnouncementId";
Database::query($sql);
break;
}
@ -282,7 +283,7 @@ switch ($action) {
if (empty($count)) {
$html = '';
if ($allowToEdit && (empty($_GET['origin']) or $_GET['origin'] !== 'learnpath')) {
if ($allowToEdit && (empty($_GET['origin']) || $_GET['origin'] !== 'learnpath')) {
$html .= '<div id="no-data-view">';
$html .= '<h3>'.get_lang('Announcements').'</h3>';
$html .= Display::return_icon('valves.png', '', [], 64);
@ -346,6 +347,10 @@ switch ($action) {
api_not_allowed();
}
if (!$allowToEdit) {
api_not_allowed(true);
}
if (!api_is_session_general_coach() ||
api_is_element_in_the_session(TOOL_ANNOUNCEMENT, $_GET['id'])
) {
@ -368,6 +373,10 @@ switch ($action) {
api_not_allowed(true);
}
if (!$allowToEdit) {
api_not_allowed(true);
}
// DISPLAY ADD ANNOUNCEMENT COMMAND
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$url = api_get_self().'?action='.$action.'&id='.$id.'&'.api_get_cidreq();
@ -466,7 +475,6 @@ switch ($action) {
}
$announcementInfo = AnnouncementManager::get_by_id($courseId, $id);
if (isset($announcementInfo) && !empty($announcementInfo)) {
$to = AnnouncementManager::load_edit_users('announcement', $id);
@ -584,6 +592,7 @@ switch ($action) {
'success'
)
);
Security::clear_token();
header('Location: '.$homeUrl);
exit;
}
@ -616,25 +625,28 @@ switch ($action) {
$sendToUsersInSession
);
}
Display::addFlash(
Display::return_message(
get_lang('AnnouncementAdded'),
'success'
)
);
// Send mail
if (isset($data['email_ann']) && $data['email_ann']) {
AnnouncementManager::sendEmail(
api_get_course_info(),
api_get_session_id(),
$insert_id,
$sendToUsersInSession
if ($insert_id) {
Display::addFlash(
Display::return_message(
get_lang('AnnouncementAdded'),
'success'
)
);
// Send mail
if (isset($data['email_ann']) && $data['email_ann']) {
AnnouncementManager::sendEmail(
api_get_course_info(),
api_get_session_id(),
$insert_id,
$sendToUsersInSession
);
}
Security::clear_token();
header('Location: '.$homeUrl);
exit;
}
header('Location: '.$homeUrl);
exit;
api_not_allowed(true);
} // end condition token
}
}

@ -36,7 +36,7 @@ $form->addHtmlEditor(
get_lang('Description'),
false,
false,
['ToolbarSet' => 'TrainingDescription', 'Width' => '100%', 'Height' => '150']
['ToolbarSet' => 'Basic', 'Width' => '100%', 'Height' => '150']
);
// Advanced Parameters

@ -40,7 +40,7 @@ $form->addHtmlEditor(
false,
false,
[
'ToolbarSet' => 'TrainingDescription',
'ToolbarSet' => 'Basic',
'Width' => '100%',
'Height' => '200',
]

@ -19,7 +19,7 @@ require_once __DIR__.'/../inc/global.inc.php';
$ctok = Security::get_existing_token();
// Get Limit data
$limit = CourseCategory::getLimitArray();
$limit = CoursesController::getLimitArray();
// Section for the tabs.
$this_section = SECTION_CATALOG;
@ -187,6 +187,7 @@ if (isset($_POST['unsubscribe'])) {
$courseController->unsubscribe_user_from_course($_POST['unsubscribe']);
}
}
switch ($action) {
case 'subscribe_user_with_password':
$courseController->subscribe_user(
@ -301,7 +302,7 @@ switch ($action) {
}
}
SessionManager::subscribe_users_to_session(
SessionManager::subscribeUsersToSession(
$_GET['session_id'],
[$userId],
SESSION_VISIBLE_READ_ONLY,

@ -308,8 +308,8 @@ function returnThumbnail($course, $registeredUser)
{
$html = '';
$title = cut($course['title'], 70);
$linkCourse = api_get_course_url($course['code']);
//$linkCourse = api_get_course_url($course['code']);
$linkCourse = api_get_path(WEB_PATH).'course/'.$course['real_id'].'/about';
// course path
$course_path = api_get_path(SYS_COURSE_PATH).$course['directory'];
@ -329,15 +329,9 @@ function returnThumbnail($course, $registeredUser)
}
$html .= '<div class="image">';
if (!$registeredUser) {
$html .= '<img class="img-responsive"'
.' src="'.$courseMediumImage.'" '
.' alt="'.api_htmlentities($title).'"/>';
} else {
$html .= '<a href="'.$linkCourse.'" title="'.$course['title'].'">'
.'<img class="img-responsive" src="'.$courseMediumImage.'" '
.'alt="'.api_htmlentities($title).'"/></a>';
}
$html .= '<a href="'.$linkCourse.'" title="'.$course['title'].'">'
.'<img class="img-responsive" src="'.$courseMediumImage.'" '
.'alt="'.api_htmlentities($title).'"/></a>';
$categoryTitle = isset($course['category_title']) ? $course['category_title'] : '';
if (!empty($categoryTitle)) {
@ -421,14 +415,10 @@ function return_teacher($courseInfo)
*/
function return_title($course, $registeredUser)
{
$linkCourse = api_get_course_url($course['code']);
//$linkCourse = api_get_course_url($course['code']);
$linkCourse = api_get_path(WEB_PATH).'course/'.$course['real_id'].'/about';
$html = '<div class="block-title"><h4 class="title">';
if (!$registeredUser) {
$html .= $course['title'];
} else {
$html .= '<a title="'.$course['title'].'" href="'.$linkCourse.'">'.$course['title'].'</a>';
}
$html .= '<a title="'.$course['title'].'" href="'.$linkCourse.'">'.$course['title'].'</a>';
$html .= '</h4></div>';
if (api_get_configuration_value('hide_course_rating') === false) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save