diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 0000000000000000000000000000000000000000..107785fb3eedd250fb4cb87d75d2493cde6ae1a8
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,3 @@
+{
+	"directory": "core/vendor"
+}
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000000000000000000000000000000000000..3a3791e5c6da37d8a06bc8f138c1f7ce682ec7c8
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,18 @@
+{
+	"name": "ownCloud",
+	"version": "8.0 pre alpha",
+	"homepage": "https://www.owncloud.org",
+	"license": "AGPL",
+	"private": true,
+	"ignore": [
+		"**/.*",
+		"node_modules",
+		"bower_components",
+		"core/vendor",
+		"test",
+		"tests"
+	],
+	"dependencies": {
+		"moment": "~2.8.3"
+	}
+}
diff --git a/core/js/core.json b/core/js/core.json
index e2da14028888d0712f7fe14d466f960477cfe8d1..3297b44318d61e5fb33f9130ce56d5a6682457cd 100644
--- a/core/js/core.json
+++ b/core/js/core.json
@@ -1,4 +1,7 @@
 {
+	"vendor": [
+		"moment/min/moment-with-locales.js"
+	],
 	"libraries": [
 		"jquery-1.10.0.min.js",
 		"jquery-migrate-1.2.1.min.js",
@@ -19,7 +22,6 @@
 		"eventsource.js",
 		"config.js",
 		"multiselect.js",
-		"oc-requesttoken.js",
-		"moment.js"
+		"oc-requesttoken.js"
 	]
 }
diff --git a/core/vendor/.gitignore b/core/vendor/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8d4f4fbe84dbe38202dd8b24cc7bc9aec452283f
--- /dev/null
+++ b/core/vendor/.gitignore
@@ -0,0 +1,8 @@
+# momentjs - ignore all files except the two listed below
+moment/benchmarks
+moment/locale
+moment/min/**
+moment/*.js*
+moment/*.md
+!moment/LICENSE
+!moment/min/moment-with-locales.js
diff --git a/core/vendor/moment/LICENSE b/core/vendor/moment/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..bd172467a6bc64bafe7df67543ef0c33e059b809
--- /dev/null
+++ b/core/vendor/moment/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2011-2014 Tim Wood, Iskren Chernev, Moment.js contributors
+
+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.
diff --git a/core/js/moment.js b/core/vendor/moment/min/moment-with-locales.js
similarity index 98%
rename from core/js/moment.js
rename to core/vendor/moment/min/moment-with-locales.js
index 6f1dcc5dfabbb3f130eda1c9771fd828b1565d09..23d06ef355110dff6ae8340ca54fa9a5b40e330b 100644
--- a/core/js/moment.js
+++ b/core/vendor/moment/min/moment-with-locales.js
@@ -1,5 +1,5 @@
 //! moment.js
-//! version : 2.8.2
+//! version : 2.8.3
 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
 //! license : MIT
 //! momentjs.com
@@ -10,7 +10,7 @@
     ************************************/
 
     var moment,
-        VERSION = '2.8.2',
+        VERSION = '2.8.3',
         // the global-scope this is NOT the global object in Node.js
         globalScope = typeof global !== 'undefined' ? global : this,
         oldGlobalMoment,
@@ -1493,6 +1493,9 @@
         for (i = 0; i < config._f.length; i++) {
             currentScore = 0;
             tempConfig = copyConfig({}, config);
+            if (config._useUTC != null) {
+                tempConfig._useUTC = config._useUTC;
+            }
             tempConfig._pf = defaultParsingFlags();
             tempConfig._f = config._f[i];
             makeDateFromStringAndFormat(tempConfig);
@@ -1557,6 +1560,14 @@
         }
     }
 
+    function map(arr, fn) {
+        var res = [], i;
+        for (i = 0; i < arr.length; ++i) {
+            res.push(fn(arr[i], i));
+        }
+        return res;
+    }
+
     function makeDateFromInput(config) {
         var input = config._i, matched;
         if (input === undefined) {
@@ -1568,7 +1579,9 @@
         } else if (typeof input === 'string') {
             makeDateFromString(config);
         } else if (isArray(input)) {
-            config._a = input.slice(0);
+            config._a = map(input.slice(0), function (obj) {
+                return parseInt(obj, 10);
+            });
             dateFromConfig(config);
         } else if (typeof(input) === 'object') {
             dateFromObject(config);
@@ -2123,7 +2136,7 @@
                 this._isUTC = false;
 
                 if (keepLocalTime) {
-                    this.add(this._d.getTimezoneOffset(), 'm');
+                    this.add(this._dateTzOffset(), 'm');
                 }
             }
             return this;
@@ -2141,7 +2154,7 @@
         diff : function (input, units, asFloat) {
             var that = makeAs(input, this),
                 zoneDiff = (this.zone() - that.zone()) * 6e4,
-                diff, output;
+                diff, output, daysAdjust;
 
             units = normalizeUnits(units);
 
@@ -2152,11 +2165,12 @@
                 output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
                 // adjust by taking difference in days, average number of days
                 // and dst in the given months.
-                output += ((this - moment(this).startOf('month')) -
-                        (that - moment(that).startOf('month'))) / diff;
+                daysAdjust = (this - moment(this).startOf('month')) -
+                    (that - moment(that).startOf('month'));
                 // same as above but with zones, to negate all dst
-                output -= ((this.zone() - moment(this).startOf('month').zone()) -
-                        (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff;
+                daysAdjust -= ((this.zone() - moment(this).startOf('month').zone()) -
+                        (that.zone() - moment(that).startOf('month').zone())) * 6e4;
+                output += daysAdjust / diff;
                 if (units === 'year') {
                     output = output / 12;
                 }
@@ -2265,18 +2279,33 @@
         },
 
         isAfter: function (input, units) {
-            units = typeof units !== 'undefined' ? units : 'millisecond';
-            return +this.clone().startOf(units) > +moment(input).startOf(units);
+            units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
+            if (units === 'millisecond') {
+                input = moment.isMoment(input) ? input : moment(input);
+                return +this > +input;
+            } else {
+                return +this.clone().startOf(units) > +moment(input).startOf(units);
+            }
         },
 
         isBefore: function (input, units) {
-            units = typeof units !== 'undefined' ? units : 'millisecond';
-            return +this.clone().startOf(units) < +moment(input).startOf(units);
+            units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
+            if (units === 'millisecond') {
+                input = moment.isMoment(input) ? input : moment(input);
+                return +this < +input;
+            } else {
+                return +this.clone().startOf(units) < +moment(input).startOf(units);
+            }
         },
 
         isSame: function (input, units) {
-            units = units || 'ms';
-            return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
+            units = normalizeUnits(units || 'millisecond');
+            if (units === 'millisecond') {
+                input = moment.isMoment(input) ? input : moment(input);
+                return +this === +input;
+            } else {
+                return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
+            }
         },
 
         min: deprecate(
@@ -2316,7 +2345,7 @@
                     input = input * 60;
                 }
                 if (!this._isUTC && keepLocalTime) {
-                    localAdjust = this._d.getTimezoneOffset();
+                    localAdjust = this._dateTzOffset();
                 }
                 this._offset = input;
                 this._isUTC = true;
@@ -2334,7 +2363,7 @@
                     }
                 }
             } else {
-                return this._isUTC ? offset : this._d.getTimezoneOffset();
+                return this._isUTC ? offset : this._dateTzOffset();
             }
             return this;
         },
@@ -2438,10 +2467,15 @@
         // instance.  Otherwise, it will return the locale configuration
         // variables for this instance.
         locale : function (key) {
+            var newLocaleData;
+
             if (key === undefined) {
                 return this._locale._abbr;
             } else {
-                this._locale = moment.localeData(key);
+                newLocaleData = moment.localeData(key);
+                if (newLocaleData != null) {
+                    this._locale = newLocaleData;
+                }
                 return this;
             }
         },
@@ -2452,14 +2486,19 @@
                 if (key === undefined) {
                     return this.localeData();
                 } else {
-                    this._locale = moment.localeData(key);
-                    return this;
+                    return this.locale(key);
                 }
             }
         ),
 
         localeData : function () {
             return this._locale;
+        },
+
+        _dateTzOffset : function () {
+            // On Firefox.24 Date#getTimezoneOffset returns a floating point.
+            // https://github.com/moment/moment/pull/1871
+            return Math.round(this._d.getTimezoneOffset() / 15) * 15;
         }
     });
 
@@ -2657,19 +2696,21 @@
             var days, months;
             units = normalizeUnits(units);
 
-            days = this._days + this._milliseconds / 864e5;
             if (units === 'month' || units === 'year') {
+                days = this._days + this._milliseconds / 864e5;
                 months = this._months + daysToYears(days) * 12;
                 return units === 'month' ? months : months / 12;
             } else {
-                days += yearsToDays(this._months / 12);
+                // handle milliseconds separately because of floating point math errors (issue #1867)
+                days = this._days + yearsToDays(this._months / 12);
                 switch (units) {
-                    case 'week': return days / 7;
-                    case 'day': return days;
-                    case 'hour': return days * 24;
-                    case 'minute': return days * 24 * 60;
-                    case 'second': return days * 24 * 60 * 60;
-                    case 'millisecond': return days * 24 * 60 * 60 * 1000;
+                    case 'week': return days / 7 + this._milliseconds / 6048e5;
+                    case 'day': return days + this._milliseconds / 864e5;
+                    case 'hour': return days * 24 + this._milliseconds / 36e5;
+                    case 'minute': return days * 24 * 60 + this._milliseconds / 6e4;
+                    case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000;
+                    // Math.floor prevents floating point math errors here
+                    case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds;
                     default: throw new Error('Unknown unit ' + units);
                 }
             }
@@ -2973,9 +3014,10 @@
     });
 }));
 // moment.js locale configuration
-// locale : Arabic (ar)
-// author : Abdel Said : https://github.com/abdelsaid
-// changes in months, weekdays : Ahmed Elkhatib
+// Locale: Arabic (ar)
+// Author: Abdel Said: https://github.com/abdelsaid
+// Changes in months, weekdays: Ahmed Elkhatib
+// Native plural forms: forabi https://github.com/forabi
 
 (function (factory) {
     factory(moment);
@@ -3002,11 +3044,42 @@
         '٨': '8',
         '٩': '9',
         '٠': '0'
-    };
+    }, pluralForm = function (n) {
+        return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
+    }, plurals = {
+        s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'],
+        m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'],
+        h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'],
+        d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'],
+        M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'],
+        y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام']
+    }, pluralize = function (u) {
+        return function (number, withoutSuffix, string, isFuture) {
+            var f = pluralForm(number),
+                str = plurals[u][pluralForm(number)];
+            if (f === 2) {
+                str = str[withoutSuffix ? 0 : 1];
+            }
+            return str.replace(/%d/i, number);
+        };
+    }, months = [
+        'كانون الثاني يناير',
+        'شباط فبراير',
+        'آذار مارس',
+        'نيسان أبريل',
+        'أيار مايو',
+        'حزيران يونيو',
+        'تموز يوليو',
+        'آب أغسطس',
+        'أيلول سبتمبر',
+        'تشرين الأول أكتوبر',
+        'تشرين الثاني نوفمبر',
+        'كانون الأول ديسمبر'
+    ];
 
     return moment.defineLocale('ar', {
-        months : 'يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول'.split('_'),
-        monthsShort : 'يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول'.split('_'),
+        months : months,
+        monthsShort : months,
         weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
         weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
         weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'),
@@ -3025,27 +3098,27 @@
             }
         },
         calendar : {
-            sameDay: '[اليوم على الساعة] LT',
-            nextDay: '[غدا على الساعة] LT',
-            nextWeek: 'dddd [على الساعة] LT',
-            lastDay: '[أمس على الساعة] LT',
-            lastWeek: 'dddd [على الساعة] LT',
+            sameDay: '[اليوم عند الساعة] LT',
+            nextDay: '[غدًا عند الساعة] LT',
+            nextWeek: 'dddd [عند الساعة] LT',
+            lastDay: '[أمس عند الساعة] LT',
+            lastWeek: 'dddd [عند الساعة] LT',
             sameElse: 'L'
         },
         relativeTime : {
-            future : 'في %s',
+            future : 'بعد %s',
             past : 'منذ %s',
-            s : 'ثوان',
-            m : 'دقيقة',
-            mm : '%d دقائق',
-            h : 'ساعة',
-            hh : '%d ساعات',
-            d : 'يوم',
-            dd : '%d أيام',
-            M : 'شهر',
-            MM : '%d أشهر',
-            y : 'سنة',
-            yy : '%d سنوات'
+            s : pluralize('s'),
+            m : pluralize('m'),
+            mm : pluralize('m'),
+            h : pluralize('h'),
+            hh : pluralize('h'),
+            d : pluralize('d'),
+            dd : pluralize('d'),
+            M : pluralize('M'),
+            MM : pluralize('M'),
+            y : pluralize('y'),
+            yy : pluralize('y')
         },
         preparse: function (string) {
             return string.replace(/[۰-۹]/g, function (match) {
@@ -3954,7 +4027,7 @@
         weekdaysShort : 'ne_po_út_st_čt_pá_so'.split('_'),
         weekdaysMin : 'ne_po_út_st_čt_pá_so'.split('_'),
         longDateFormat : {
-            LT: 'H.mm',
+            LT: 'H:mm',
             L : 'DD. MM. YYYY',
             LL : 'D. MMMM YYYY',
             LLL : 'D. MMMM YYYY LT',
diff --git a/lib/base.php b/lib/base.php
index 7fa53c3a074f83df9d2af0032d3b99de0785842f..11c5167786dc2e5aa84934b65075a0c49eaee640 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -348,7 +348,7 @@ class OC {
 		OC_Util::addScript("oc-requesttoken");
 		OC_Util::addScript("apps");
 		OC_Util::addScript("snap");
-		OC_Util::addScript("moment");
+		OC_Util::addVendorScript('moment/min/moment-with-locales');
 
 		// avatars
 		if (\OC_Config::getValue('enable_avatars', true) === true) {
diff --git a/lib/private/template/functions.php b/lib/private/template/functions.php
index dede604c01de2da44e8fd02b2054714c2879b63d..0e2c5775c4695886145d96b2711a74d1f4499926 100644
--- a/lib/private/template/functions.php
+++ b/lib/private/template/functions.php
@@ -39,6 +39,22 @@ function script($app, $file) {
 	}
 }
 
+/**
+ * Shortcut for adding vendor scripts to a page
+ * @param string $app the appname
+ * @param string|string[] $file the filename,
+ * if an array is given it will add all scripts
+ */
+function vendorScript($app, $file) {
+	if(is_array($file)) {
+		foreach($file as $f) {
+			OC_Util::addVendorScript($app, $f);
+		}
+	} else {
+		OC_Util::addVendorScript($app, $file);
+	}
+}
+
 /**
  * Shortcut for adding styles to a page
  * @param string $app the appname
@@ -55,6 +71,22 @@ function style($app, $file) {
 	}
 }
 
+/**
+ * Shortcut for adding vendor styles to a page
+ * @param string $app the appname
+ * @param string|string[] $file the filename,
+ * if an array is given it will add all styles
+ */
+function vendorStyle($app, $file) {
+	if(is_array($file)) {
+		foreach($file as $f) {
+			OC_Util::addVendorStyle($app, $f);
+		}
+	} else {
+		OC_Util::addVendorStyle($app, $file);
+	}
+}
+
 /**
  * Shortcut for adding translations to a page
  * @param string $app the appname
diff --git a/lib/private/util.php b/lib/private/util.php
index de4bef4cb8a05a60a52a1113f120dbccbe6a4d1b..bee0a579192a625dc29d851f31a44767e228c4b1 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -331,24 +331,47 @@ class OC_Util {
 	}
 
 	/**
-	 * add a javascript file
+	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
 	 *
-	 * @param string $application application id
-	 * @param string|null $file filename
-	 * @return void
+	 * @param $application application to get the files from
+	 * @param $directory directory withing this application (css, js, vendor, etc)
+	 * @param $file the file inside of the above folder
+	 * @return string the path
 	 */
-	public static function addScript($application, $file = null) {
+	private static function generatePath($application, $directory, $file) {
 		if (is_null($file)) {
 			$file = $application;
 			$application = "";
 		}
 		if (!empty($application)) {
-			self::$scripts[] = "$application/js/$file";
+			return "$application/$directory/$file";
 		} else {
-			self::$scripts[] = "js/$file";
+			return "$directory/$file";
 		}
 	}
 
+	/**
+	 * add a javascript file
+	 *
+	 * @param string $application application id
+	 * @param string|null $file filename
+	 * @return void
+	 */
+	public static function addScript($application, $file = null) {
+		self::$scripts[] = OC_Util::generatePath($application, 'js', $file);
+	}
+
+	/**
+	 * add a javascript file from the vendor sub folder
+	 *
+	 * @param string $application application id
+	 * @param string|null $file filename
+	 * @return void
+	 */
+	public static function addVendorScript($application, $file = null) {
+		self::$scripts[] = OC_Util::generatePath($application, 'vendor', $file);
+	}
+
 	/**
 	 * add a translation JS file
 	 *
@@ -375,15 +398,18 @@ class OC_Util {
 	 * @return void
 	 */
 	public static function addStyle($application, $file = null) {
-		if (is_null($file)) {
-			$file = $application;
-			$application = "";
-		}
-		if (!empty($application)) {
-			self::$styles[] = "$application/css/$file";
-		} else {
-			self::$styles[] = "css/$file";
-		}
+		self::$styles[] = OC_Util::generatePath($application, 'css', $file);
+	}
+
+	/**
+	 * add a css file from the vendor sub folder
+	 *
+	 * @param string $application application id
+	 * @param string|null $file filename
+	 * @return void
+	 */
+	public static function addVendorStyle($application, $file = null) {
+		self::$styles[] = OC_Util::generatePath($application, 'vendor', $file);
 	}
 
 	/**
diff --git a/tests/karma.config.js b/tests/karma.config.js
index 357fcf3f122fac912f8d9a370d0530c9f3d8a562..f67fa9977c2d6f65438edd99dcbfb264ab80b831 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -99,6 +99,7 @@ module.exports = function(config) {
 	// note that the loading order is important that's why they
 	// are specified in a separate file
 	var corePath = 'core/js/';
+	var vendorPath = 'core/vendor/';
 	var coreModule = require('../' + corePath + 'core.json');
 	var testCore = false;
 	var files = [];
@@ -134,6 +135,12 @@ module.exports = function(config) {
 		}
 	}
 
+	// add vendor library files
+	for ( i = 0; i < coreModule.vendor.length; i++ ) {
+		srcFile = vendorPath + coreModule.vendor[i];
+		files.push(srcFile);
+	}
+
 	// TODO: settings pages
 
 	// need to test the core app as well ?