diff --git a/pom.xml b/pom.xml
index 3834c24d..0a76b27e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -258,6 +258,11 @@
mousetrap
1.3
+
+ org.webjars
+ codemirror
+ 3.11
+
diff --git a/src/main/java/com/commafeed/backend/model/UserSettings.java b/src/main/java/com/commafeed/backend/model/UserSettings.java
index aa823e3d..44fba463 100644
--- a/src/main/java/com/commafeed/backend/model/UserSettings.java
+++ b/src/main/java/com/commafeed/backend/model/UserSettings.java
@@ -5,6 +5,7 @@ import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@@ -25,6 +26,10 @@ public class UserSettings extends AbstractModel {
@Column(nullable = false)
private ReadingMode readingMode;
+ @Lob
+ @Column(length = Integer.MAX_VALUE)
+ private String customCss;
+
public ReadingMode getReadingMode() {
return readingMode;
}
@@ -41,4 +46,12 @@ public class UserSettings extends AbstractModel {
this.user = user;
}
+ public String getCustomCss() {
+ return customCss;
+ }
+
+ public void setCustomCss(String customCss) {
+ this.customCss = customCss;
+ }
+
}
diff --git a/src/main/java/com/commafeed/frontend/model/Settings.java b/src/main/java/com/commafeed/frontend/model/Settings.java
index 967ab276..c462483c 100644
--- a/src/main/java/com/commafeed/frontend/model/Settings.java
+++ b/src/main/java/com/commafeed/frontend/model/Settings.java
@@ -6,6 +6,7 @@ import java.io.Serializable;
public class Settings implements Serializable {
private String readingMode;
+ private String customCss;
public String getReadingMode() {
return readingMode;
@@ -15,4 +16,12 @@ public class Settings implements Serializable {
this.readingMode = readingMode;
}
+ public String getCustomCss() {
+ return customCss;
+ }
+
+ public void setCustomCss(String customCss) {
+ this.customCss = customCss;
+ }
+
}
diff --git a/src/main/java/com/commafeed/frontend/pages/HomePage.java b/src/main/java/com/commafeed/frontend/pages/HomePage.java
index 8771ea95..7128681d 100644
--- a/src/main/java/com/commafeed/frontend/pages/HomePage.java
+++ b/src/main/java/com/commafeed/frontend/pages/HomePage.java
@@ -1,17 +1,24 @@
package com.commafeed.frontend.pages;
+import javax.inject.Inject;
+
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import com.commafeed.backend.dao.UserSettingsService;
import com.commafeed.backend.model.UserRole.Role;
+import com.commafeed.backend.model.UserSettings;
+import com.commafeed.frontend.CommaFeedSession;
import com.commafeed.frontend.SecurityCheck;
+import com.commafeed.frontend.references.UserCustomCssReference;
import com.commafeed.frontend.references.angular.AngularReference;
import com.commafeed.frontend.references.angular.AngularResourceReference;
import com.commafeed.frontend.references.angular.AngularSanitizeReference;
import com.commafeed.frontend.references.angularui.AngularUIReference;
import com.commafeed.frontend.references.angularuibootstrap.AngularUIBootstrapReference;
import com.commafeed.frontend.references.angularuistate.AngularUIStateReference;
+import com.commafeed.frontend.references.codemirror.CodeMirrorCssReference;
import com.commafeed.frontend.references.csstreeview.CssTreeViewReference;
import com.commafeed.frontend.references.mousetrap.MouseTrapReference;
import com.commafeed.frontend.references.nggrid.NGGridReference;
@@ -24,6 +31,9 @@ import com.commafeed.frontend.references.spinjs.SpinJSReference;
@SecurityCheck(Role.USER)
public class HomePage extends BasePage {
+ @Inject
+ UserSettingsService settingsService;
+
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
@@ -39,6 +49,7 @@ public class HomePage extends BasePage {
SpinJSReference.renderHead(response);
MouseTrapReference.renderHead(response);
NGGridReference.renderHead(response);
+ CodeMirrorCssReference.renderHead(response);
CssTreeViewReference.renderHead(response);
@@ -48,5 +59,16 @@ public class HomePage extends BasePage {
response.render(JavaScriptHeaderItem.forUrl("js/services.js"));
response.render(CssHeaderItem.forUrl("css/app.css"));
+
+ response.render(CssHeaderItem
+ .forReference(new UserCustomCssReference() {
+ @Override
+ protected String getCss() {
+ UserSettings settings = settingsService
+ .findByUser(CommaFeedSession.get().getUser());
+ return settings == null ? null : settings
+ .getCustomCss();
+ }
+ }));
}
}
diff --git a/src/main/java/com/commafeed/frontend/references/UserCustomCssReference.java b/src/main/java/com/commafeed/frontend/references/UserCustomCssReference.java
new file mode 100644
index 00000000..eb5583ad
--- /dev/null
+++ b/src/main/java/com/commafeed/frontend/references/UserCustomCssReference.java
@@ -0,0 +1,39 @@
+package com.commafeed.frontend.references;
+
+import java.io.IOException;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+
+@SuppressWarnings("serial")
+public abstract class UserCustomCssReference extends ResourceReference {
+
+ public UserCustomCssReference() {
+ super(UserCustomCssReference.class, "custom.css");
+ }
+
+ @Override
+ public IResource getResource() {
+ return new AbstractResource() {
+ @Override
+ protected ResourceResponse newResourceResponse(Attributes attributes) {
+ ResourceResponse resourceResponse = new ResourceResponse();
+ resourceResponse.setContentType("text/css");
+ resourceResponse.setTextEncoding("UTF-8");
+ resourceResponse.setWriteCallback(new WriteCallback() {
+ @Override
+ public void writeData(Attributes attributes)
+ throws IOException {
+ attributes.getResponse().write(
+ StringUtils.trimToEmpty(getCss()));
+ }
+ });
+ return resourceResponse;
+ }
+ };
+ }
+
+ protected abstract String getCss();
+}
diff --git a/src/main/java/com/commafeed/frontend/references/angularui/AngularUIReference.java b/src/main/java/com/commafeed/frontend/references/angularui/AngularUIReference.java
index 757d81c6..54349770 100644
--- a/src/main/java/com/commafeed/frontend/references/angularui/AngularUIReference.java
+++ b/src/main/java/com/commafeed/frontend/references/angularui/AngularUIReference.java
@@ -2,28 +2,31 @@ package com.commafeed.frontend.references.angularui;
import java.util.Arrays;
+import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.HeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.request.resource.CssResourceReference;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
-import com.commafeed.frontend.utils.WicketUtils;
+import com.commafeed.frontend.references.angular.AngularReference;
-import de.agilecoders.wicket.webjars.request.resource.WebjarsJavaScriptResourceReference;
-
-public class AngularUIReference extends WebjarsJavaScriptResourceReference {
+public class AngularUIReference extends JavaScriptResourceReference {
private static final long serialVersionUID = 1L;
public static final AngularUIReference INSTANCE = new AngularUIReference();
private AngularUIReference() {
- super("/angular-ui/current/angular-ui.js");
+ super(AngularUIReference.class, "angular-ui.js");
}
+ @SuppressWarnings("unchecked")
@Override
public Iterable extends HeaderItem> getDependencies() {
- return Arrays
- .asList(WicketUtils
- .buildCssWebJarHeaderItem("/angular-ui/current/angular-ui.css"));
+ return Arrays.asList(JavaScriptHeaderItem
+ .forReference(AngularReference.INSTANCE), CssHeaderItem
+ .forReference(new CssResourceReference(
+ AngularUIReference.class, "angular-ui.css")));
}
public static void renderHead(final IHeaderResponse response) {
diff --git a/src/main/java/com/commafeed/frontend/references/angularui/angular-ui.css b/src/main/java/com/commafeed/frontend/references/angularui/angular-ui.css
new file mode 100644
index 00000000..eb02a8ed
--- /dev/null
+++ b/src/main/java/com/commafeed/frontend/references/angularui/angular-ui.css
@@ -0,0 +1,50 @@
+/**
+ * import components to builds angular-ui.css
+ */
+
+/* ui-reset */
+
+.ui-resetwrap {
+ position: relative;
+ display: inline-block;
+}
+
+.ui-reset {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 2;
+ display: none;
+ height: 100%;
+ cursor: pointer;
+}
+
+.ui-resetwrap:hover .ui-reset {
+ display: block;
+}
+
+/* ui-currency */
+
+.ui-currency-pos {
+ color: green;
+}
+
+.ui-currency-neg {
+ color: red;
+}
+
+.ui-currency-zero {
+ color: blue;
+}
+
+.ui-currency-pos.ui-bignum,
+.ui-currency-neg.ui-smallnum {
+ font-size: 110%;
+}
+
+/* highlight */
+
+.ui-match {
+ background: yellow;
+}
+
diff --git a/src/main/java/com/commafeed/frontend/references/angularui/angular-ui.js b/src/main/java/com/commafeed/frontend/references/angularui/angular-ui.js
new file mode 100644
index 00000000..e92b2cfc
--- /dev/null
+++ b/src/main/java/com/commafeed/frontend/references/angularui/angular-ui.js
@@ -0,0 +1,1461 @@
+/**
+ * AngularUI - The companion suite for AngularJS
+ * @version v0.4.0 - 2013-02-15
+ * @link http://angular-ui.github.com
+ * @license MIT License, http://www.opensource.org/licenses/MIT
+ */
+
+
+angular.module('ui.config', []).value('ui.config', {});
+angular.module('ui.filters', ['ui.config']);
+angular.module('ui.directives', ['ui.config']);
+angular.module('ui', ['ui.filters', 'ui.directives', 'ui.config']);
+
+/**
+ * Animates the injection of new DOM elements by simply creating the DOM with a class and then immediately removing it
+ * Animations must be done using CSS3 transitions, but provide excellent flexibility
+ *
+ * @todo Add proper support for animating out
+ * @param [options] {mixed} Can be an object with multiple options, or a string with the animation class
+ * class {string} the CSS class(es) to use. For example, 'ui-hide' might be an excellent alternative class.
+ * @example {{item}}
+ */
+angular.module('ui.directives').directive('uiAnimate', ['ui.config', '$timeout', function (uiConfig, $timeout) {
+ var options = {};
+ if (angular.isString(uiConfig.animate)) {
+ options['class'] = uiConfig.animate;
+ } else if (uiConfig.animate) {
+ options = uiConfig.animate;
+ }
+ return {
+ restrict: 'A', // supports using directive as element, attribute and class
+ link: function ($scope, element, attrs) {
+ var opts = {};
+ if (attrs.uiAnimate) {
+ opts = $scope.$eval(attrs.uiAnimate);
+ if (angular.isString(opts)) {
+ opts = {'class': opts};
+ }
+ }
+ opts = angular.extend({'class': 'ui-animate'}, options, opts);
+
+ element.addClass(opts['class']);
+ $timeout(function () {
+ element.removeClass(opts['class']);
+ }, 20, false);
+ }
+ };
+}]);
+
+
+/*
+* AngularJs Fullcalendar Wrapper for the JQuery FullCalendar
+* API @ http://arshaw.com/fullcalendar/
+*
+* Angular Calendar Directive that takes in the [eventSources] nested array object as the ng-model and watches (eventSources.length + eventSources[i].length) for changes.
+* Can also take in multiple event urls as a source object(s) and feed the events per view.
+* The calendar will watch any eventSource array and update itself when a delta is created
+* An equalsTracker attrs has been added for use cases that would render the overall length tracker the same even though the events have changed to force updates.
+*
+*/
+
+angular.module('ui.directives').directive('uiCalendar',['ui.config', '$parse', function (uiConfig,$parse) {
+ uiConfig.uiCalendar = uiConfig.uiCalendar || {};
+ //returns calendar
+ return {
+ require: 'ngModel',
+ restrict: 'A',
+ link: function(scope, elm, attrs, $timeout) {
+ var sources = scope.$eval(attrs.ngModel);
+ var tracker = 0;
+ /* returns the length of all source arrays plus the length of eventSource itself */
+ var getSources = function () {
+ var equalsTracker = scope.$eval(attrs.equalsTracker);
+ tracker = 0;
+ angular.forEach(sources,function(value,key){
+ if(angular.isArray(value)){
+ tracker += value.length;
+ }
+ });
+ if(angular.isNumber(equalsTracker)){
+ return tracker + sources.length + equalsTracker;
+ }else{
+ return tracker + sources.length;
+ }
+ };
+ /* update the calendar with the correct options */
+ function update() {
+ //calendar object exposed on scope
+ scope.calendar = elm.html('');
+ var view = scope.calendar.fullCalendar('getView');
+ if(view){
+ view = view.name; //setting the default view to be whatever the current view is. This can be overwritten.
+ }
+ /* If the calendar has options added then render them */
+ var expression,
+ options = {
+ defaultView : view,
+ eventSources: sources
+ };
+ if (attrs.uiCalendar) {
+ expression = scope.$eval(attrs.uiCalendar);
+ } else {
+ expression = {};
+ }
+ angular.extend(options, uiConfig.uiCalendar, expression);
+ scope.calendar.fullCalendar(options);
+ }
+ update();
+ /* watches all eventSources */
+ scope.$watch(getSources, function( newVal, oldVal )
+ {
+ update();
+ });
+ }
+ };
+}]);
+/*global angular, CodeMirror, Error*/
+/**
+ * Binds a CodeMirror widget to a