├── .gitignore ├── .openshift ├── action_hooks │ └── README.md ├── config │ ├── .gitkeep │ ├── catalina.policy │ ├── catalina.properties │ ├── context.xml │ ├── logging.properties │ ├── postgresql_module.xml │ ├── server.xml │ ├── settings.base.xml │ ├── settings.prod.xml │ ├── settings.rhcloud.xml │ ├── settings.stg.xml │ ├── tomcat-users.xml │ └── web.xml ├── cron │ ├── README.cron │ ├── daily │ │ └── .gitignore │ ├── hourly │ │ └── .gitignore │ ├── minutely │ │ └── .gitignore │ ├── monthly │ │ └── .gitignore │ └── weekly │ │ ├── README │ │ ├── chrono.dat │ │ ├── chronograph │ │ ├── jobs.allow │ │ └── jobs.deny └── markers │ ├── .gitkeep │ ├── hot_deploy │ └── java7 ├── README.md ├── pom.xml ├── src └── main │ ├── java │ ├── .gitkeep │ └── com │ │ └── getbookmarks │ │ ├── config │ │ ├── ApplicationConfig.java │ │ ├── GetBookmarksWebApplicationInitializer.java │ │ └── WebMvcConfig.java │ │ ├── domain │ │ └── Story.java │ │ ├── repository │ │ └── StoryRepository.java │ │ └── rest │ │ ├── PingResource.java │ │ ├── StoryNotFoundException.java │ │ └── StoryResource.java │ ├── resources │ ├── .gitkeep │ └── logback.xml │ └── webapp │ ├── css │ ├── bootstrap.css │ └── toastr.css │ ├── img │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png │ ├── index.html │ ├── js │ ├── angular-resource.js │ ├── angular.js │ ├── app.js │ ├── bootstrap.js │ ├── jquery-1.9.1.js │ └── toastr.js │ └── views │ └── stories │ ├── create.html │ ├── detail.html │ └── list.html └── webapps └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | target 3 | .settings/* 4 | !.settings/.jsdtscope 5 | .project 6 | .classpath 7 | -------------------------------------------------------------------------------- /.openshift/action_hooks/README.md: -------------------------------------------------------------------------------- 1 | For information about action hooks supported by OpenShift, consult the documentation: 2 | 3 | http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory 4 | -------------------------------------------------------------------------------- /.openshift/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/config/.gitkeep -------------------------------------------------------------------------------- /.openshift/config/catalina.policy: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one or more 2 | // contributor license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright ownership. 4 | // The ASF licenses this file to You under the Apache License, Version 2.0 5 | // (the "License"); you may not use this file except in compliance with 6 | // the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // ============================================================================ 17 | // catalina.policy - Security Policy Permissions for Tomcat 7 18 | // 19 | // This file contains a default set of security policies to be enforced (by the 20 | // JVM) when Catalina is executed with the "-security" option. In addition 21 | // to the permissions granted here, the following additional permissions are 22 | // granted to each web application: 23 | // 24 | // * Read access to the web application's document root directory 25 | // * Read, write and delete access to the web application's working directory 26 | // 27 | // $Id: catalina.policy 1347048 2012-06-06 18:47:00Z markt $ 28 | // ============================================================================ 29 | 30 | 31 | // ========== SYSTEM CODE PERMISSIONS ========================================= 32 | 33 | 34 | // These permissions apply to javac 35 | grant codeBase "file:${java.home}/lib/-" { 36 | permission java.security.AllPermission; 37 | }; 38 | 39 | // These permissions apply to all shared system extensions 40 | grant codeBase "file:${java.home}/jre/lib/ext/-" { 41 | permission java.security.AllPermission; 42 | }; 43 | 44 | // These permissions apply to javac when ${java.home] points at $JAVA_HOME/jre 45 | grant codeBase "file:${java.home}/../lib/-" { 46 | permission java.security.AllPermission; 47 | }; 48 | 49 | // These permissions apply to all shared system extensions when 50 | // ${java.home} points at $JAVA_HOME/jre 51 | grant codeBase "file:${java.home}/lib/ext/-" { 52 | permission java.security.AllPermission; 53 | }; 54 | 55 | 56 | // ========== CATALINA CODE PERMISSIONS ======================================= 57 | 58 | 59 | // These permissions apply to the daemon code 60 | grant codeBase "file:${catalina.home}/bin/commons-daemon.jar" { 61 | permission java.security.AllPermission; 62 | }; 63 | 64 | // These permissions apply to the logging API 65 | // Note: If tomcat-juli.jar is in ${catalina.base} and not in ${catalina.home}, 66 | // update this section accordingly. 67 | // grant codeBase "file:${catalina.base}/bin/tomcat-juli.jar" {..} 68 | grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" { 69 | permission java.io.FilePermission 70 | "${java.home}${file.separator}lib${file.separator}logging.properties", "read"; 71 | 72 | permission java.io.FilePermission 73 | "${catalina.base}${file.separator}conf${file.separator}logging.properties", "read"; 74 | permission java.io.FilePermission 75 | "${catalina.base}${file.separator}logs", "read, write"; 76 | permission java.io.FilePermission 77 | "${catalina.base}${file.separator}logs${file.separator}*", "read, write"; 78 | 79 | permission java.lang.RuntimePermission "shutdownHooks"; 80 | permission java.lang.RuntimePermission "getClassLoader"; 81 | permission java.lang.RuntimePermission "setContextClassLoader"; 82 | 83 | permission java.util.logging.LoggingPermission "control"; 84 | 85 | permission java.util.PropertyPermission "java.util.logging.config.class", "read"; 86 | permission java.util.PropertyPermission "java.util.logging.config.file", "read"; 87 | permission java.util.PropertyPermission "catalina.base", "read"; 88 | permission java.util.PropertyPermission 89 | "org.apache.juli.logging.UserDataHelper.CONFIG", "read"; 90 | permission java.util.PropertyPermission 91 | "org.apache.juli.logging.UserDataHelper.SUPPRESSION_TIME", "read"; 92 | 93 | // Note: To enable per context logging configuration, permit read access to 94 | // the appropriate file. Be sure that the logging configuration is 95 | // secure before enabling such access. 96 | // E.g. for the examples web application (uncomment and unwrap 97 | // the following to be on a single line): 98 | // permission java.io.FilePermission "${catalina.base}${file.separator} 99 | // webapps${file.separator}examples${file.separator}WEB-INF 100 | // ${file.separator}classes${file.separator}logging.properties", "read"; 101 | }; 102 | 103 | // These permissions apply to the server startup code 104 | grant codeBase "file:${catalina.home}/bin/bootstrap.jar" { 105 | permission java.security.AllPermission; 106 | }; 107 | 108 | // These permissions apply to the servlet API classes 109 | // and those that are shared across all class loaders 110 | // located in the "lib" directory 111 | grant codeBase "file:${catalina.home}/lib/-" { 112 | permission java.security.AllPermission; 113 | }; 114 | 115 | 116 | // If using a per instance lib directory, i.e. ${catalina.base}/lib, 117 | // then the following permission will need to be uncommented 118 | // grant codeBase "file:${catalina.base}/lib/-" { 119 | // permission java.security.AllPermission; 120 | // }; 121 | 122 | 123 | // ========== WEB APPLICATION PERMISSIONS ===================================== 124 | 125 | 126 | // These permissions are granted by default to all web applications 127 | // In addition, a web application will be given a read FilePermission 128 | // and JndiPermission for all files and directories in its document root. 129 | grant { 130 | // Required for JNDI lookup of named JDBC DataSource's and 131 | // javamail named MimePart DataSource used to send mail 132 | permission java.util.PropertyPermission "java.home", "read"; 133 | permission java.util.PropertyPermission "java.naming.*", "read"; 134 | permission java.util.PropertyPermission "javax.sql.*", "read"; 135 | 136 | // OS Specific properties to allow read access 137 | permission java.util.PropertyPermission "os.name", "read"; 138 | permission java.util.PropertyPermission "os.version", "read"; 139 | permission java.util.PropertyPermission "os.arch", "read"; 140 | permission java.util.PropertyPermission "file.separator", "read"; 141 | permission java.util.PropertyPermission "path.separator", "read"; 142 | permission java.util.PropertyPermission "line.separator", "read"; 143 | 144 | // JVM properties to allow read access 145 | permission java.util.PropertyPermission "java.version", "read"; 146 | permission java.util.PropertyPermission "java.vendor", "read"; 147 | permission java.util.PropertyPermission "java.vendor.url", "read"; 148 | permission java.util.PropertyPermission "java.class.version", "read"; 149 | permission java.util.PropertyPermission "java.specification.version", "read"; 150 | permission java.util.PropertyPermission "java.specification.vendor", "read"; 151 | permission java.util.PropertyPermission "java.specification.name", "read"; 152 | 153 | permission java.util.PropertyPermission "java.vm.specification.version", "read"; 154 | permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; 155 | permission java.util.PropertyPermission "java.vm.specification.name", "read"; 156 | permission java.util.PropertyPermission "java.vm.version", "read"; 157 | permission java.util.PropertyPermission "java.vm.vendor", "read"; 158 | permission java.util.PropertyPermission "java.vm.name", "read"; 159 | 160 | // Required for OpenJMX 161 | permission java.lang.RuntimePermission "getAttribute"; 162 | 163 | // Allow read of JAXP compliant XML parser debug 164 | permission java.util.PropertyPermission "jaxp.debug", "read"; 165 | 166 | // All JSPs need to be able to read this package 167 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat"; 168 | 169 | // Precompiled JSPs need access to these packages. 170 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.el"; 171 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime"; 172 | permission java.lang.RuntimePermission 173 | "accessClassInPackage.org.apache.jasper.runtime.*"; 174 | 175 | // Precompiled JSPs need access to these system properties. 176 | permission java.util.PropertyPermission 177 | "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER", "read"; 178 | permission java.util.PropertyPermission 179 | "org.apache.el.parser.COERCE_TO_ZERO", "read"; 180 | 181 | // The cookie code needs these. 182 | permission java.util.PropertyPermission 183 | "org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "read"; 184 | permission java.util.PropertyPermission 185 | "org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING", "read"; 186 | permission java.util.PropertyPermission 187 | "org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR", "read"; 188 | 189 | // Applications using Comet need to be able to access this package 190 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.comet"; 191 | 192 | // Applications using WebSocket need to be able to access this package 193 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.websocket"; 194 | }; 195 | 196 | 197 | // The Manager application needs access to the following packages to support the 198 | // session display functionality. These settings support the following 199 | // configurations: 200 | // - default CATALINA_HOME == CATALINA_BASE 201 | // - CATALINA_HOME != CATALINA_BASE, per instance Manager in CATALINA_BASE 202 | // - CATALINA_HOME != CATALINA_BASE, shared Manager in CATALINA_HOME 203 | grant codeBase "file:${catalina.base}/webapps/manager/-" { 204 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; 205 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; 206 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; 207 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; 208 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.util"; 209 | }; 210 | grant codeBase "file:${catalina.home}/webapps/manager/-" { 211 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; 212 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; 213 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; 214 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; 215 | permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.util"; 216 | }; 217 | 218 | // You can assign additional permissions to particular web applications by 219 | // adding additional "grant" entries here, based on the code base for that 220 | // application, /WEB-INF/classes/, or /WEB-INF/lib/ jar files. 221 | // 222 | // Different permissions can be granted to JSP pages, classes loaded from 223 | // the /WEB-INF/classes/ directory, all jar files in the /WEB-INF/lib/ 224 | // directory, or even to individual jar files in the /WEB-INF/lib/ directory. 225 | // 226 | // For instance, assume that the standard "examples" application 227 | // included a JDBC driver that needed to establish a network connection to the 228 | // corresponding database and used the scrape taglib to get the weather from 229 | // the NOAA web server. You might create a "grant" entries like this: 230 | // 231 | // The permissions granted to the context root directory apply to JSP pages. 232 | // grant codeBase "file:${catalina.base}/webapps/examples/-" { 233 | // permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect"; 234 | // permission java.net.SocketPermission "*.noaa.gov:80", "connect"; 235 | // }; 236 | // 237 | // The permissions granted to the context WEB-INF/classes directory 238 | // grant codeBase "file:${catalina.base}/webapps/examples/WEB-INF/classes/-" { 239 | // }; 240 | // 241 | // The permission granted to your JDBC driver 242 | // grant codeBase "jar:file:${catalina.base}/webapps/examples/WEB-INF/lib/driver.jar!/-" { 243 | // permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect"; 244 | // }; 245 | // The permission granted to the scrape taglib 246 | // grant codeBase "jar:file:${catalina.base}/webapps/examples/WEB-INF/lib/scrape.jar!/-" { 247 | // permission java.net.SocketPermission "*.noaa.gov:80", "connect"; 248 | // }; 249 | 250 | -------------------------------------------------------------------------------- /.openshift/config/catalina.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # 17 | # List of comma-separated packages that start with or equal this string 18 | # will cause a security exception to be thrown when 19 | # passed to checkPackageAccess unless the 20 | # corresponding RuntimePermission ("accessClassInPackage."+package) has 21 | # been granted. 22 | package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. 23 | # 24 | # List of comma-separated packages that start with or equal this string 25 | # will cause a security exception to be thrown when 26 | # passed to checkPackageDefinition unless the 27 | # corresponding RuntimePermission ("defineClassInPackage."+package) has 28 | # been granted. 29 | # 30 | # by default, no packages are restricted for definition, and none of 31 | # the class loaders supplied with the JDK call checkPackageDefinition. 32 | # 33 | package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. 34 | 35 | # 36 | # 37 | # List of comma-separated paths defining the contents of the "common" 38 | # classloader. Prefixes should be used to define what is the repository type. 39 | # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. 40 | # If left as blank,the JVM system loader will be used as Catalina's "common" 41 | # loader. 42 | # Examples: 43 | # "foo": Add this folder as a class repository 44 | # "foo/*.jar": Add all the JARs of the specified folder as class 45 | # repositories 46 | # "foo/bar.jar": Add bar.jar as a class repository 47 | common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar 48 | 49 | # 50 | # List of comma-separated paths defining the contents of the "server" 51 | # classloader. Prefixes should be used to define what is the repository type. 52 | # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. 53 | # If left as blank, the "common" loader will be used as Catalina's "server" 54 | # loader. 55 | # Examples: 56 | # "foo": Add this folder as a class repository 57 | # "foo/*.jar": Add all the JARs of the specified folder as class 58 | # repositories 59 | # "foo/bar.jar": Add bar.jar as a class repository 60 | server.loader= 61 | 62 | # 63 | # List of comma-separated paths defining the contents of the "shared" 64 | # classloader. Prefixes should be used to define what is the repository type. 65 | # Path may be relative to the CATALINA_BASE path or absolute. If left as blank, 66 | # the "common" loader will be used as Catalina's "shared" loader. 67 | # Examples: 68 | # "foo": Add this folder as a class repository 69 | # "foo/*.jar": Add all the JARs of the specified folder as class 70 | # repositories 71 | # "foo/bar.jar": Add bar.jar as a class repository 72 | # Please note that for single jars, e.g. bar.jar, you need the URL form 73 | # starting with file:. 74 | shared.loader= 75 | 76 | # List of JAR files that should not be scanned using the JarScanner 77 | # functionality. This is typically used to scan JARs for configuration 78 | # information. JARs that do not contain such information may be excluded from 79 | # the scan to speed up the scanning process. This is the default list. JARs on 80 | # this list are excluded from all scans. Scan specific lists (to exclude JARs 81 | # from individual scans) follow this. The list must be a comma separated list of 82 | # JAR file names. 83 | # The JARs listed below include: 84 | # - Tomcat Bootstrap JARs 85 | # - Tomcat API JARs 86 | # - Catalina JARs 87 | # - Jasper JARs 88 | # - Tomcat JARs 89 | # - Common non-Tomcat JARs 90 | # - Sun JDK JARs 91 | # - OpenJDK JARs 92 | # - Apple JDK JARs 93 | tomcat.util.scan.DefaultJarScanner.jarsToSkip=\ 94 | bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,\ 95 | annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,\ 96 | catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-tribes.jar,\ 97 | jasper.jar,jasper-el.jar,ecj-*.jar,\ 98 | tomcat-api.jar,tomcat-util.jar,tomcat-coyote.jar,tomcat-dbcp.jar,\ 99 | tomcat-jni.jar,tomcat-spdy.jar,\ 100 | tomcat-i18n-en.jar,tomcat-i18n-es.jar,tomcat-i18n-fr.jar,tomcat-i18n-ja.jar,\ 101 | tomcat-juli-adapters.jar,catalina-jmx-remote.jar,catalina-ws.jar,\ 102 | tomcat-jdbc.jar,\ 103 | commons-beanutils*.jar,commons-codec*.jar,commons-collections*.jar,\ 104 | commons-dbcp*.jar,commons-digester*.jar,commons-fileupload*.jar,\ 105 | commons-httpclient*.jar,commons-io*.jar,commons-lang*.jar,commons-logging*.jar,\ 106 | commons-math*.jar,commons-pool*.jar,\ 107 | jstl.jar,\ 108 | geronimo-spec-jaxrpc*.jar,wsdl4j*.jar,\ 109 | ant.jar,ant-junit*.jar,aspectj*.jar,jmx.jar,h2*.jar,hibernate*.jar,httpclient*.jar,\ 110 | jmx-tools.jar,jta*.jar,log4j*.jar,mail*.jar,slf4j*.jar,\ 111 | xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,\ 112 | access-bridge-64.jar,dnsns.jar,jaccess.jar,ldapsec.jar,localedata.jar,\ 113 | sunjce_provider.jar,sunmscapi.jar,sunpkcs11.jar,jhall.jar,tools.jar,\ 114 | sunec.jar,zipfs.jar,\ 115 | gnome-java-bridge.jar,pulse-java.jar,\ 116 | apple_provider.jar,AppleScriptEngine.jar,CoreAudio.jar,dns_sd.jar,\ 117 | j3daudio.jar,j3dcore.jar,j3dutils.jar,jai_core.jar,jai_codec.jar,\ 118 | mlibwrapper_jai.jar,MRJToolkit.jar,vecmath.jar,\ 119 | junit.jar,junit-*.jar,ant-launcher.jar 120 | 121 | # Additional JARs (over and above the default JARs listed above) to skip when 122 | # scanning for Servlet 3.0 pluggability features. These features include web 123 | # fragments, annotations, SCIs and classes that match @HandlesTypes. The list 124 | # must be a comma separated list of JAR file names. 125 | org.apache.catalina.startup.ContextConfig.jarsToSkip= 126 | 127 | # Additional JARs (over and above the default JARs listed above) to skip when 128 | # scanning for TLDs. The list must be a comma separated list of JAR file names. 129 | org.apache.catalina.startup.TldConfig.jarsToSkip= 130 | 131 | # 132 | # String cache configuration. 133 | tomcat.util.buf.StringCache.byte.enabled=true 134 | #tomcat.util.buf.StringCache.char.enabled=true 135 | #tomcat.util.buf.StringCache.trainThreshold=500000 136 | #tomcat.util.buf.StringCache.cacheSize=5000 137 | -------------------------------------------------------------------------------- /.openshift/config/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | WEB-INF/web.xml 22 | 23 | 24 | 27 | 28 | 30 | 33 | 34 | 45 | 46 | 56 | 57 | -------------------------------------------------------------------------------- /.openshift/config/logging.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler 17 | 18 | .handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler 19 | 20 | ############################################################ 21 | # Handler specific properties. 22 | # Describes specific configuration info for Handlers. 23 | ############################################################ 24 | 25 | 1catalina.org.apache.juli.FileHandler.level = FINE 26 | 1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 27 | 1catalina.org.apache.juli.FileHandler.prefix = catalina. 28 | 29 | 2localhost.org.apache.juli.FileHandler.level = FINE 30 | 2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 31 | 2localhost.org.apache.juli.FileHandler.prefix = localhost. 32 | 33 | 3manager.org.apache.juli.FileHandler.level = FINE 34 | 3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 35 | 3manager.org.apache.juli.FileHandler.prefix = manager. 36 | 37 | 4host-manager.org.apache.juli.FileHandler.level = FINE 38 | 4host-manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 39 | 4host-manager.org.apache.juli.FileHandler.prefix = host-manager. 40 | 41 | java.util.logging.ConsoleHandler.level = FINE 42 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 43 | 44 | 45 | ############################################################ 46 | # Facility specific properties. 47 | # Provides extra control for each logger. 48 | ############################################################ 49 | 50 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO 51 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.FileHandler 52 | 53 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO 54 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.FileHandler 55 | 56 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO 57 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.FileHandler 58 | 59 | # For example, set the org.apache.catalina.util.LifecycleBase logger to log 60 | # each component that extends LifecycleBase changing state: 61 | #org.apache.catalina.util.LifecycleBase.level = FINE 62 | 63 | # To see debug messages in TldLocationsCache, uncomment the following line: 64 | #org.apache.jasper.compiler.TldLocationsCache.level = FINE 65 | -------------------------------------------------------------------------------- /.openshift/config/postgresql_module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.openshift/config/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 42 | 47 | 48 | 49 | 54 | 55 | 56 | 57 | 61 | 62 | 63 | 70 | 75 | 76 | 77 | 83 | 87 | 92 | 93 | 94 | 95 | 96 | 97 | 102 | 103 | 106 | 107 | 108 | 111 | 114 | 115 | 117 | 118 | 122 | 124 | 125 | 126 | 128 | 129 | 131 | 134 | 135 | 138 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /.openshift/config/settings.base.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.openshift/config/settings.prod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | nexus 5 | central 6 | http://mirror1.ops.rhcloud.com/nexus/content/groups/public 7 | 8 | 9 | 10 | 11 | nexus 12 | 13 | 14 | eap 15 | http://maven.repository.redhat.com/techpreview/all 16 | true 17 | true 18 | 19 | 20 | central 21 | http://central 22 | true 23 | true 24 | 25 | 26 | 27 | 28 | eap 29 | http://maven.repository.redhat.com/techpreview/all 30 | true 31 | true 32 | 33 | 34 | central 35 | http://central 36 | true 37 | true 38 | 39 | 40 | 41 | 42 | 43 | nexus 44 | 45 | 46 | -------------------------------------------------------------------------------- /.openshift/config/settings.rhcloud.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | nexus 5 | central 6 | http://mirror1.ops.rhcloud.com/nexus/content/groups/public 7 | 8 | 9 | 10 | 11 | nexus 12 | 13 | 14 | eap 15 | http://maven.repository.redhat.com/techpreview/all 16 | true 17 | true 18 | 19 | 20 | central 21 | http://central 22 | true 23 | true 24 | 25 | 26 | 27 | 28 | eap 29 | http://maven.repository.redhat.com/techpreview/all 30 | true 31 | true 32 | 33 | 34 | central 35 | http://central 36 | true 37 | true 38 | 39 | 40 | 41 | 42 | 43 | nexus 44 | 45 | 46 | -------------------------------------------------------------------------------- /.openshift/config/settings.stg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | nexus 5 | central 6 | http://mirror1.ops.rhcloud.com/nexus/content/groups/public 7 | 8 | 9 | 10 | 11 | nexus 12 | 13 | 14 | eap 15 | http://maven.repository.redhat.com/techpreview/all 16 | true 17 | true 18 | 19 | 20 | central 21 | http://central 22 | true 23 | true 24 | 25 | 26 | 27 | 28 | eap 29 | http://maven.repository.redhat.com/techpreview/all 30 | true 31 | true 32 | 33 | 34 | central 35 | http://central 36 | true 37 | true 38 | 39 | 40 | 41 | 42 | 43 | nexus 44 | 45 | 46 | -------------------------------------------------------------------------------- /.openshift/config/tomcat-users.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 24 | 29 | 34 | 35 | 42 | 43 | -------------------------------------------------------------------------------- /.openshift/cron/README.cron: -------------------------------------------------------------------------------- 1 | Run scripts or jobs on a periodic basis 2 | ======================================= 3 | Any scripts or jobs added to the minutely, hourly, daily, weekly or monthly 4 | directories will be run on a scheduled basis (frequency is as indicated by the 5 | name of the directory) using run-parts. 6 | 7 | run-parts ignores any files that are hidden or dotfiles (.*) or backup 8 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} 9 | 10 | The presence of two specially named files jobs.deny and jobs.allow controls 11 | how run-parts executes your scripts/jobs. 12 | jobs.deny ===> Prevents specific scripts or jobs from being executed. 13 | jobs.allow ===> Only execute the named scripts or jobs (all other/non-named 14 | scripts that exist in this directory are ignored). 15 | 16 | The principles of jobs.deny and jobs.allow are the same as those of cron.deny 17 | and cron.allow and are described in detail at: 18 | http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Automating_System_Tasks.html#s2-autotasks-cron-access 19 | 20 | See: man crontab or above link for more details and see the the weekly/ 21 | directory for an example. 22 | 23 | PLEASE NOTE: The Cron cartridge must be installed in order to run the configured jobs. 24 | -------------------------------------------------------------------------------- /.openshift/cron/daily/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/cron/daily/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/hourly/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/cron/hourly/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/minutely/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/cron/minutely/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/monthly/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/cron/monthly/.gitignore -------------------------------------------------------------------------------- /.openshift/cron/weekly/README: -------------------------------------------------------------------------------- 1 | Run scripts or jobs on a weekly basis 2 | ===================================== 3 | Any scripts or jobs added to this directory will be run on a scheduled basis 4 | (weekly) using run-parts. 5 | 6 | run-parts ignores any files that are hidden or dotfiles (.*) or backup 7 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} and handles 8 | the files named jobs.deny and jobs.allow specially. 9 | 10 | In this specific example, the chronograph script is the only script or job file 11 | executed on a weekly basis (due to white-listing it in jobs.allow). And the 12 | README and chrono.dat file are ignored either as a result of being black-listed 13 | in jobs.deny or because they are NOT white-listed in the jobs.allow file. 14 | 15 | For more details, please see ../README.cron file. 16 | 17 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/chrono.dat: -------------------------------------------------------------------------------- 1 | Time And Relative D...n In Execution (Open)Shift! 2 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/chronograph: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "`date`: `cat $(dirname \"$0\")/chrono.dat`" 4 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/jobs.allow: -------------------------------------------------------------------------------- 1 | # 2 | # Script or job files listed in here (one entry per line) will be 3 | # executed on a weekly-basis. 4 | # 5 | # Example: The chronograph script will be executed weekly but the README 6 | # and chrono.dat files in this directory will be ignored. 7 | # 8 | # The README file is actually ignored due to the entry in the 9 | # jobs.deny which is checked before jobs.allow (this file). 10 | # 11 | chronograph 12 | 13 | -------------------------------------------------------------------------------- /.openshift/cron/weekly/jobs.deny: -------------------------------------------------------------------------------- 1 | # 2 | # Any script or job files listed in here (one entry per line) will NOT be 3 | # executed (read as ignored by run-parts). 4 | # 5 | 6 | README 7 | 8 | -------------------------------------------------------------------------------- /.openshift/markers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/markers/.gitkeep -------------------------------------------------------------------------------- /.openshift/markers/hot_deploy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/markers/hot_deploy -------------------------------------------------------------------------------- /.openshift/markers/java7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/.openshift/markers/java7 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Day 22 Demo Application## 2 | 3 | To run it on OpenShift, execute the following command. 4 | 5 | ``` 6 | $ rhc create-app getbookmarks tomcat-7 mongodb-2 --from-code https://github.com/shekhargulati/day22-spring-angularjs-demo-app.git 7 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | getbookmarks 5 | getbookmarks 6 | war 7 | 1.0 8 | getbookmarks 9 | 10 | UTF-8 11 | 1.7 12 | 1.7 13 | 14 | 15 | 16 | org.springframework 17 | spring-webmvc 18 | 3.2.5.RELEASE 19 | 20 | 21 | org.springframework 22 | spring-tx 23 | 3.2.5.RELEASE 24 | 25 | 26 | org.springframework.data 27 | spring-data-mongodb 28 | 1.3.2.RELEASE 29 | 30 | 31 | 32 | org.codehaus.jackson 33 | jackson-mapper-asl 34 | 1.9.13 35 | 36 | 37 | 38 | 39 | javax.servlet 40 | javax.servlet-api 41 | 3.1.0 42 | provided 43 | 44 | 45 | 46 | 47 | 48 | ch.qos.logback 49 | logback-classic 50 | 1.0.13 51 | 52 | 53 | 54 | 55 | 56 | openshift 57 | 58 | getbookmarks 59 | 60 | 61 | maven-war-plugin 62 | 2.4 63 | 64 | false 65 | webapps 66 | ROOT 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | getbookmarks 76 | 77 | 78 | maven-war-plugin 79 | 2.4 80 | 81 | false 82 | target 83 | ROOT 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/main/java/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/src/main/java/.gitkeep -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/config/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.data.authentication.UserCredentials; 6 | import org.springframework.data.mongodb.MongoDbFactory; 7 | import org.springframework.data.mongodb.core.MongoTemplate; 8 | import org.springframework.data.mongodb.core.SimpleMongoDbFactory; 9 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 10 | 11 | import com.getbookmarks.repository.StoryRepository; 12 | import com.mongodb.Mongo; 13 | 14 | @Configuration 15 | @EnableMongoRepositories(basePackageClasses = StoryRepository.class) 16 | public class ApplicationConfig { 17 | 18 | @Bean 19 | public MongoTemplate mongoTemplate() throws Exception { 20 | String openshiftMongoDbHost = System.getenv("OPENSHIFT_MONGODB_DB_HOST"); 21 | int openshiftMongoDbPort = Integer.parseInt(System.getenv("OPENSHIFT_MONGODB_DB_PORT")); 22 | String username = System.getenv("OPENSHIFT_MONGODB_DB_USERNAME"); 23 | String password = System.getenv("OPENSHIFT_MONGODB_DB_PASSWORD"); 24 | Mongo mongo = new Mongo(openshiftMongoDbHost, openshiftMongoDbPort); 25 | UserCredentials userCredentials = new UserCredentials(username, password); 26 | String databaseName = System.getenv("OPENSHIFT_APP_NAME"); 27 | MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongo, databaseName, userCredentials); 28 | MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory); 29 | return mongoTemplate; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/config/GetBookmarksWebApplicationInitializer.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.config; 2 | 3 | import javax.servlet.ServletContext; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.ServletRegistration.Dynamic; 6 | 7 | import org.springframework.web.WebApplicationInitializer; 8 | import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; 9 | import org.springframework.web.servlet.DispatcherServlet; 10 | 11 | public class GetBookmarksWebApplicationInitializer implements WebApplicationInitializer { 12 | 13 | @Override 14 | public void onStartup(ServletContext servletContext) throws ServletException { 15 | AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext(); 16 | webApplicationContext.register(ApplicationConfig.class, WebMvcConfig.class); 17 | 18 | Dynamic dynamc = servletContext.addServlet("dispatcherServlet", new DispatcherServlet(webApplicationContext)); 19 | dynamc.addMapping("/api/v1/*"); 20 | dynamc.setLoadOnStartup(1); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 8 | import org.springframework.web.servlet.view.json.MappingJacksonJsonView; 9 | 10 | import com.getbookmarks.rest.PingResource; 11 | 12 | @EnableWebMvc 13 | @ComponentScan(basePackageClasses = PingResource.class) 14 | @Configuration 15 | public class WebMvcConfig extends WebMvcConfigurerAdapter { 16 | 17 | @Bean 18 | public MappingJacksonJsonView jsonView() { 19 | MappingJacksonJsonView jsonView = new MappingJacksonJsonView(); 20 | jsonView.setPrefixJson(true); 21 | return jsonView; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/domain/Story.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.domain; 2 | 3 | import java.util.Date; 4 | 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.mongodb.core.mapping.Document; 7 | 8 | @Document(collection = "stories") 9 | public class Story { 10 | 11 | @Id 12 | private String id; 13 | 14 | private String title; 15 | 16 | private String text; 17 | 18 | private String url; 19 | 20 | private String fullname; 21 | 22 | private final Date submittedOn = new Date(); 23 | 24 | private String image; 25 | 26 | public Story() { 27 | } 28 | 29 | public Story(String url, String fullname) { 30 | this.url = url; 31 | this.fullname = fullname; 32 | } 33 | 34 | public Story(Story input, Story extraInformation) { 35 | this.url = input.getUrl(); 36 | this.fullname = input.getFullname(); 37 | this.title = extraInformation.getTitle(); 38 | this.text = extraInformation.getText(); 39 | this.image = extraInformation.getImage(); 40 | } 41 | 42 | public String getId() { 43 | return id; 44 | } 45 | 46 | public String getTitle() { 47 | return title; 48 | } 49 | 50 | public String getText() { 51 | return text; 52 | } 53 | 54 | public String getUrl() { 55 | return url; 56 | } 57 | 58 | 59 | public String getFullname() { 60 | return fullname; 61 | } 62 | 63 | public Date getSubmittedOn() { 64 | return submittedOn; 65 | } 66 | 67 | public String getImage() { 68 | return image; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/repository/StoryRepository.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.repository; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import com.getbookmarks.domain.Story; 9 | 10 | @Repository 11 | public interface StoryRepository extends CrudRepository { 12 | 13 | public List findAll(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/rest/PingResource.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.rest; 2 | 3 | import org.springframework.http.MediaType; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.ResponseBody; 7 | 8 | @RequestMapping("/ping") 9 | @Controller 10 | public class PingResource { 11 | 12 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 13 | @ResponseBody 14 | public String ping() { 15 | return "{'ping':'pong'}"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/rest/StoryNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.rest; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class StoryNotFoundException extends RuntimeException { 8 | 9 | public StoryNotFoundException(String storyId) { 10 | super(String.format("Story with id %s not found", storyId)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/getbookmarks/rest/StoryResource.java: -------------------------------------------------------------------------------- 1 | package com.getbookmarks.rest; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | import org.springframework.web.client.RestTemplate; 15 | 16 | import com.getbookmarks.domain.Story; 17 | import com.getbookmarks.repository.StoryRepository; 18 | 19 | @Controller 20 | @RequestMapping("/stories") 21 | public class StoryResource { 22 | 23 | private StoryRepository storyRepository; 24 | 25 | @Autowired 26 | public StoryResource(StoryRepository storyRepository) { 27 | this.storyRepository = storyRepository; 28 | } 29 | 30 | @RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE) 31 | @ResponseBody 32 | public ResponseEntity submitStory(@RequestBody Story story) { 33 | Story storyWithExtractedInformation = decorateWithInformation(story); 34 | storyRepository.save(storyWithExtractedInformation); 35 | ResponseEntity responseEntity = new ResponseEntity<>(HttpStatus.CREATED); 36 | return responseEntity; 37 | } 38 | 39 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 40 | @ResponseBody 41 | public List allStories() { 42 | return storyRepository.findAll(); 43 | } 44 | 45 | @RequestMapping(value = "/{storyId}", produces = MediaType.APPLICATION_JSON_VALUE) 46 | @ResponseBody 47 | public Story showStory(@PathVariable("storyId") String storyId) { 48 | Story story = storyRepository.findOne(storyId); 49 | if (story == null) { 50 | throw new StoryNotFoundException(storyId); 51 | } 52 | return story; 53 | } 54 | 55 | private Story decorateWithInformation(Story story) { 56 | String url = story.getUrl(); 57 | RestTemplate restTemplate = new RestTemplate(); 58 | ResponseEntity forEntity = restTemplate.getForEntity( 59 | "http://gooseextractor-t20.rhcloud.com/api/v1/extract?url=" + url, Story.class); 60 | if (forEntity.hasBody()) { 61 | return new Story(story, forEntity.getBody()); 62 | } 63 | return story; 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/src/main/resources/.gitkeep -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d %5p %40.40c:%4L - %m%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/webapp/css/toastr.css: -------------------------------------------------------------------------------- 1 | .toast-title { 2 | font-weight: bold; 3 | } 4 | .toast-message { 5 | -ms-word-wrap: break-word; 6 | word-wrap: break-word; 7 | } 8 | .toast-message a, 9 | .toast-message label { 10 | color: #ffffff; 11 | } 12 | .toast-message a:hover { 13 | color: #cccccc; 14 | text-decoration: none; 15 | } 16 | .toast-top-full-width { 17 | top: 0; 18 | right: 0; 19 | width: 100%; 20 | } 21 | .toast-bottom-full-width { 22 | bottom: 0; 23 | right: 0; 24 | width: 100%; 25 | } 26 | .toast-top-left { 27 | top: 12px; 28 | left: 12px; 29 | } 30 | .toast-top-right { 31 | top: 12px; 32 | right: 12px; 33 | } 34 | .toast-bottom-right { 35 | right: 12px; 36 | bottom: 12px; 37 | } 38 | .toast-bottom-left { 39 | bottom: 12px; 40 | left: 12px; 41 | } 42 | #toast-container { 43 | position: fixed; 44 | z-index: 999999; 45 | } 46 | #toast-container * { 47 | -moz-box-sizing: border-box; 48 | -webkit-box-sizing: border-box; 49 | box-sizing: border-box; 50 | } 51 | #toast-container > div { 52 | margin: 0 0 6px; 53 | padding: 15px 15px 15px 50px; 54 | width: 300px; 55 | -moz-border-radius: 3px 3px 3px 3px; 56 | -webkit-border-radius: 3px 3px 3px 3px; 57 | border-radius: 3px 3px 3px 3px; 58 | background-position: 15px center; 59 | background-repeat: no-repeat; 60 | -moz-box-shadow: 0 0 12px #999999; 61 | -webkit-box-shadow: 0 0 12px #999999; 62 | box-shadow: 0 0 12px #999999; 63 | color: #ffffff; 64 | opacity: 0.8; 65 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); 66 | filter: alpha(opacity=80); 67 | } 68 | #toast-container > :hover { 69 | -moz-box-shadow: 0 0 12px #000000; 70 | -webkit-box-shadow: 0 0 12px #000000; 71 | box-shadow: 0 0 12px #000000; 72 | opacity: 1; 73 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 74 | filter: alpha(opacity=100); 75 | cursor: pointer; 76 | } 77 | #toast-container > .toast-info { 78 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important; 79 | } 80 | #toast-container > .toast-error { 81 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important; 82 | } 83 | #toast-container > .toast-success { 84 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important; 85 | } 86 | #toast-container > .toast-warning { 87 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important; 88 | } 89 | #toast-container.toast-top-full-width > div, 90 | #toast-container.toast-bottom-full-width > div { 91 | width: 96%; 92 | margin: auto; 93 | } 94 | .toast { 95 | background-color: #030303; 96 | } 97 | .toast-success { 98 | background-color: #51a351; 99 | } 100 | .toast-error { 101 | background-color: #bd362f; 102 | } 103 | .toast-info { 104 | background-color: #2f96b4; 105 | } 106 | .toast-warning { 107 | background-color: #f89406; 108 | } 109 | /*Responsive Design*/ 110 | @media all and (max-width: 240px) { 111 | #toast-container > div { 112 | padding: 8px 8px 8px 50px; 113 | width: 108px; 114 | } 115 | } 116 | @media all and (min-width: 241px) and (max-width: 320px) { 117 | #toast-container > div { 118 | padding: 8px 8px 8px 50px; 119 | width: 128px; 120 | } 121 | } 122 | @media all and (min-width: 321px) and (max-width: 480px) { 123 | #toast-container > div { 124 | padding: 8px 8px 8px 50px; 125 | width: 192px; 126 | } 127 | } 128 | @media all and (min-width: 481px) and (max-width: 768px) { 129 | #toast-container > div { 130 | padding: 15px 15px 15px 50px; 131 | width: 300px; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/webapp/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/src/main/webapp/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/main/webapp/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shekhargulati/day22-spring-angularjs-demo-app/1eeae70758fda574bf11d2e620186fcfef258423/src/main/webapp/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | GetBookmarks : Submit Story 13 | 14 | 15 | 36 | 37 |
38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/webapp/js/angular-resource.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.6 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) { 7 | 'use strict'; 8 | 9 | /** 10 | * @ngdoc overview 11 | * @name ngResource 12 | * @description 13 | */ 14 | 15 | /** 16 | * @ngdoc object 17 | * @name ngResource.$resource 18 | * @requires $http 19 | * 20 | * @description 21 | * A factory which creates a resource object that lets you interact with 22 | * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. 23 | * 24 | * The returned resource object has action methods which provide high-level behaviors without 25 | * the need to interact with the low level {@link ng.$http $http} service. 26 | * 27 | * # Installation 28 | * To use $resource make sure you have included the `angular-resource.js` that comes in Angular 29 | * package. You can also find this file on Google CDN, bower as well as at 30 | * {@link http://code.angularjs.org/ code.angularjs.org}. 31 | * 32 | * Finally load the module in your application: 33 | * 34 | * angular.module('app', ['ngResource']); 35 | * 36 | * and you are ready to get started! 37 | * 38 | * @param {string} url A parameterized URL template with parameters prefixed by `:` as in 39 | * `/user/:username`. If you are using a URL with a port number (e.g. 40 | * `http://example.com:8080/api`), you'll need to escape the colon character before the port 41 | * number, like this: `$resource('http://example.com\\:8080/api')`. 42 | * 43 | * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in 44 | * `actions` methods. 45 | * 46 | * Each key value in the parameter object is first bound to url template if present and then any 47 | * excess keys are appended to the url search query after the `?`. 48 | * 49 | * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in 50 | * URL `/path/greet?salutation=Hello`. 51 | * 52 | * If the parameter value is prefixed with `@` then the value of that parameter is extracted from 53 | * the data object (useful for non-GET operations). 54 | * 55 | * @param {Object.=} actions Hash with declaration of custom action that should extend the 56 | * default set of resource actions. The declaration should be created in the following format: 57 | * 58 | * {action1: {method:?, params:?, isArray:?}, 59 | * action2: {method:?, params:?, isArray:?}, 60 | * ...} 61 | * 62 | * Where: 63 | * 64 | * - `action` – {string} – The name of action. This name becomes the name of the method on your 65 | * resource object. 66 | * - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`, 67 | * and `JSONP` 68 | * - `params` – {object=} – Optional set of pre-bound parameters for this action. 69 | * - isArray – {boolean=} – If true then the returned object for this action is an array, see 70 | * `returns` section. 71 | * 72 | * @returns {Object} A resource "class" object with methods for the default set of resource actions 73 | * optionally extended with custom `actions`. The default set contains these actions: 74 | * 75 | * { 'get': {method:'GET'}, 76 | * 'save': {method:'POST'}, 77 | * 'query': {method:'GET', isArray:true}, 78 | * 'remove': {method:'DELETE'}, 79 | * 'delete': {method:'DELETE'} }; 80 | * 81 | * Calling these methods invoke an {@link ng.$http} with the specified http method, 82 | * destination and parameters. When the data is returned from the server then the object is an 83 | * instance of the resource class. The actions `save`, `remove` and `delete` are available on it 84 | * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create, 85 | * read, update, delete) on server-side data like this: 86 | *
 87 |         var User = $resource('/user/:userId', {userId:'@id'});
 88 |         var user = User.get({userId:123}, function() {
 89 |           user.abc = true;
 90 |           user.$save();
 91 |         });
 92 |      
93 | * 94 | * It is important to realize that invoking a $resource object method immediately returns an 95 | * empty reference (object or array depending on `isArray`). Once the data is returned from the 96 | * server the existing reference is populated with the actual data. This is a useful trick since 97 | * usually the resource is assigned to a model which is then rendered by the view. Having an empty 98 | * object results in no rendering, once the data arrives from the server then the object is 99 | * populated with the data and the view automatically re-renders itself showing the new data. This 100 | * means that in most case one never has to write a callback function for the action methods. 101 | * 102 | * The action methods on the class object or instance object can be invoked with the following 103 | * parameters: 104 | * 105 | * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` 106 | * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` 107 | * - non-GET instance actions: `instance.$action([parameters], [success], [error])` 108 | * 109 | * 110 | * @example 111 | * 112 | * # Credit card resource 113 | * 114 | *
115 |      // Define CreditCard class
116 |      var CreditCard = $resource('/user/:userId/card/:cardId',
117 |       {userId:123, cardId:'@id'}, {
118 |        charge: {method:'POST', params:{charge:true}}
119 |       });
120 | 
121 |      // We can retrieve a collection from the server
122 |      var cards = CreditCard.query(function() {
123 |        // GET: /user/123/card
124 |        // server returns: [ {id:456, number:'1234', name:'Smith'} ];
125 | 
126 |        var card = cards[0];
127 |        // each item is an instance of CreditCard
128 |        expect(card instanceof CreditCard).toEqual(true);
129 |        card.name = "J. Smith";
130 |        // non GET methods are mapped onto the instances
131 |        card.$save();
132 |        // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
133 |        // server returns: {id:456, number:'1234', name: 'J. Smith'};
134 | 
135 |        // our custom method is mapped as well.
136 |        card.$charge({amount:9.99});
137 |        // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
138 |      });
139 | 
140 |      // we can create an instance as well
141 |      var newCard = new CreditCard({number:'0123'});
142 |      newCard.name = "Mike Smith";
143 |      newCard.$save();
144 |      // POST: /user/123/card {number:'0123', name:'Mike Smith'}
145 |      // server returns: {id:789, number:'01234', name: 'Mike Smith'};
146 |      expect(newCard.id).toEqual(789);
147 |  * 
148 | * 149 | * The object returned from this function execution is a resource "class" which has "static" method 150 | * for each action in the definition. 151 | * 152 | * Calling these methods invoke `$http` on the `url` template with the given `method` and `params`. 153 | * When the data is returned from the server then the object is an instance of the resource type and 154 | * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD 155 | * operations (create, read, update, delete) on server-side data. 156 | 157 |
158 |      var User = $resource('/user/:userId', {userId:'@id'});
159 |      var user = User.get({userId:123}, function() {
160 |        user.abc = true;
161 |        user.$save();
162 |      });
163 |    
164 | * 165 | * It's worth noting that the success callback for `get`, `query` and other method gets passed 166 | * in the response that came from the server as well as $http header getter function, so one 167 | * could rewrite the above example and get access to http headers as: 168 | * 169 |
170 |      var User = $resource('/user/:userId', {userId:'@id'});
171 |      User.get({userId:123}, function(u, getResponseHeaders){
172 |        u.abc = true;
173 |        u.$save(function(u, putResponseHeaders) {
174 |          //u => saved user object
175 |          //putResponseHeaders => $http header getter
176 |        });
177 |      });
178 |    
179 | 180 | * # Buzz client 181 | 182 | Let's look at what a buzz client created with the `$resource` service looks like: 183 | 184 | 185 | 205 | 206 |
207 | 208 | 209 |
210 |
211 |

212 | 213 | {{item.actor.name}} 214 | Expand replies: {{item.links.replies[0].count}} 215 |

216 | {{item.object.content | html}} 217 |
218 | 219 | {{reply.actor.name}}: {{reply.content | html}} 220 |
221 |
222 |
223 |
224 | 225 | 226 |
227 | */ 228 | angular.module('ngResource', ['ng']). 229 | factory('$resource', ['$http', '$parse', function($http, $parse) { 230 | var DEFAULT_ACTIONS = { 231 | 'get': {method:'GET'}, 232 | 'save': {method:'POST'}, 233 | 'query': {method:'GET', isArray:true}, 234 | 'remove': {method:'DELETE'}, 235 | 'delete': {method:'DELETE'} 236 | }; 237 | var noop = angular.noop, 238 | forEach = angular.forEach, 239 | extend = angular.extend, 240 | copy = angular.copy, 241 | isFunction = angular.isFunction, 242 | getter = function(obj, path) { 243 | return $parse(path)(obj); 244 | }; 245 | 246 | /** 247 | * We need our custom method because encodeURIComponent is too aggressive and doesn't follow 248 | * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path 249 | * segments: 250 | * segment = *pchar 251 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 252 | * pct-encoded = "%" HEXDIG HEXDIG 253 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 254 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 255 | * / "*" / "+" / "," / ";" / "=" 256 | */ 257 | function encodeUriSegment(val) { 258 | return encodeUriQuery(val, true). 259 | replace(/%26/gi, '&'). 260 | replace(/%3D/gi, '='). 261 | replace(/%2B/gi, '+'); 262 | } 263 | 264 | 265 | /** 266 | * This method is intended for encoding *key* or *value* parts of query component. We need a custom 267 | * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be 268 | * encoded per http://tools.ietf.org/html/rfc3986: 269 | * query = *( pchar / "/" / "?" ) 270 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 271 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 272 | * pct-encoded = "%" HEXDIG HEXDIG 273 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 274 | * / "*" / "+" / "," / ";" / "=" 275 | */ 276 | function encodeUriQuery(val, pctEncodeSpaces) { 277 | return encodeURIComponent(val). 278 | replace(/%40/gi, '@'). 279 | replace(/%3A/gi, ':'). 280 | replace(/%24/g, '$'). 281 | replace(/%2C/gi, ','). 282 | replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); 283 | } 284 | 285 | function Route(template, defaults) { 286 | this.template = template = template + '#'; 287 | this.defaults = defaults || {}; 288 | var urlParams = this.urlParams = {}; 289 | forEach(template.split(/\W/), function(param){ 290 | if (param && (new RegExp("(^|[^\\\\]):" + param + "\\W").test(template))) { 291 | urlParams[param] = true; 292 | } 293 | }); 294 | this.template = template.replace(/\\:/g, ':'); 295 | } 296 | 297 | Route.prototype = { 298 | url: function(params) { 299 | var self = this, 300 | url = this.template, 301 | val, 302 | encodedVal; 303 | 304 | params = params || {}; 305 | forEach(this.urlParams, function(_, urlParam){ 306 | val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; 307 | if (angular.isDefined(val) && val !== null) { 308 | encodedVal = encodeUriSegment(val); 309 | url = url.replace(new RegExp(":" + urlParam + "(\\W)", "g"), encodedVal + "$1"); 310 | } else { 311 | url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W)", "g"), function(match, 312 | leadingSlashes, tail) { 313 | if (tail.charAt(0) == '/') { 314 | return tail; 315 | } else { 316 | return leadingSlashes + tail; 317 | } 318 | }); 319 | } 320 | }); 321 | url = url.replace(/\/?#$/, ''); 322 | var query = []; 323 | forEach(params, function(value, key){ 324 | if (!self.urlParams[key]) { 325 | query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value)); 326 | } 327 | }); 328 | query.sort(); 329 | url = url.replace(/\/*$/, ''); 330 | return url + (query.length ? '?' + query.join('&') : ''); 331 | } 332 | }; 333 | 334 | 335 | function ResourceFactory(url, paramDefaults, actions) { 336 | var route = new Route(url); 337 | 338 | actions = extend({}, DEFAULT_ACTIONS, actions); 339 | 340 | function extractParams(data, actionParams){ 341 | var ids = {}; 342 | actionParams = extend({}, paramDefaults, actionParams); 343 | forEach(actionParams, function(value, key){ 344 | ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value; 345 | }); 346 | return ids; 347 | } 348 | 349 | function Resource(value){ 350 | copy(value || {}, this); 351 | } 352 | 353 | forEach(actions, function(action, name) { 354 | action.method = angular.uppercase(action.method); 355 | var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH'; 356 | Resource[name] = function(a1, a2, a3, a4) { 357 | var params = {}; 358 | var data; 359 | var success = noop; 360 | var error = null; 361 | switch(arguments.length) { 362 | case 4: 363 | error = a4; 364 | success = a3; 365 | //fallthrough 366 | case 3: 367 | case 2: 368 | if (isFunction(a2)) { 369 | if (isFunction(a1)) { 370 | success = a1; 371 | error = a2; 372 | break; 373 | } 374 | 375 | success = a2; 376 | error = a3; 377 | //fallthrough 378 | } else { 379 | params = a1; 380 | data = a2; 381 | success = a3; 382 | break; 383 | } 384 | case 1: 385 | if (isFunction(a1)) success = a1; 386 | else if (hasBody) data = a1; 387 | else params = a1; 388 | break; 389 | case 0: break; 390 | default: 391 | throw "Expected between 0-4 arguments [params, data, success, error], got " + 392 | arguments.length + " arguments."; 393 | } 394 | 395 | var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data)); 396 | $http({ 397 | method: action.method, 398 | url: route.url(extend({}, extractParams(data, action.params || {}), params)), 399 | data: data 400 | }).then(function(response) { 401 | var data = response.data; 402 | 403 | if (data) { 404 | if (action.isArray) { 405 | value.length = 0; 406 | forEach(data, function(item) { 407 | value.push(new Resource(item)); 408 | }); 409 | } else { 410 | copy(data, value); 411 | } 412 | } 413 | (success||noop)(value, response.headers); 414 | }, error); 415 | 416 | return value; 417 | }; 418 | 419 | 420 | Resource.prototype['$' + name] = function(a1, a2, a3) { 421 | var params = extractParams(this), 422 | success = noop, 423 | error; 424 | 425 | switch(arguments.length) { 426 | case 3: params = a1; success = a2; error = a3; break; 427 | case 2: 428 | case 1: 429 | if (isFunction(a1)) { 430 | success = a1; 431 | error = a2; 432 | } else { 433 | params = a1; 434 | success = a2 || noop; 435 | } 436 | case 0: break; 437 | default: 438 | throw "Expected between 1-3 arguments [params, success, error], got " + 439 | arguments.length + " arguments."; 440 | } 441 | var data = hasBody ? this : undefined; 442 | Resource[name].call(this, params, data, success, error); 443 | }; 444 | }); 445 | 446 | Resource.bind = function(additionalParamDefaults){ 447 | return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); 448 | }; 449 | 450 | return Resource; 451 | } 452 | 453 | return ResourceFactory; 454 | }]); 455 | 456 | 457 | })(window, window.angular); 458 | -------------------------------------------------------------------------------- /src/main/webapp/js/app.js: -------------------------------------------------------------------------------- 1 | angular.module("getbookmarks.services", ["ngResource"]). 2 | factory('Story', function ($resource) { 3 | var Story = $resource('/api/v1/stories/:storyId', {storyId: '@id'}); 4 | Story.prototype.isNew = function(){ 5 | return (typeof(this.id) === 'undefined'); 6 | } 7 | return Story; 8 | }); 9 | 10 | angular.module("getbookmarks", ["getbookmarks.services"]). 11 | config(function ($routeProvider) { 12 | $routeProvider 13 | .when('/', {templateUrl: 'views/stories/list.html', controller: StoryListController}) 14 | .when('/stories/new', {templateUrl: 'views/stories/create.html', controller: StoryCreateController}) 15 | .when('/stories/:storyId', {templateUrl: 'views/stories/detail.html', controller: StoryDetailController}); 16 | }); 17 | 18 | function StoryListController($scope, Story) { 19 | $scope.stories = Story.query(); 20 | 21 | } 22 | 23 | function StoryCreateController($scope, $routeParams, $location, Story) { 24 | 25 | $scope.story = new Story(); 26 | 27 | $scope.save = function () { 28 | $scope.story.$save(function (story, headers) { 29 | toastr.success("Submitted New Story"); 30 | $location.path('/'); 31 | }); 32 | }; 33 | } 34 | 35 | 36 | function StoryDetailController($scope, $routeParams, $location, Story) { 37 | var storyId = $routeParams.storyId; 38 | 39 | $scope.story = Story.get({storyId: storyId}); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/webapp/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.3.1 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 27 | * ======================================================= */ 28 | 29 | $(function () { 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd otransitionend' 40 | , 'transition' : 'transitionend' 41 | } 42 | , name 43 | 44 | for (name in transEndEventNames){ 45 | if (el.style[name] !== undefined) { 46 | return transEndEventNames[name] 47 | } 48 | } 49 | 50 | }()) 51 | 52 | return transitionEnd && { 53 | end: transitionEnd 54 | } 55 | 56 | })() 57 | 58 | }) 59 | 60 | }(window.jQuery);/* ========================================================== 61 | * bootstrap-alert.js v2.3.1 62 | * http://twitter.github.com/bootstrap/javascript.html#alerts 63 | * ========================================================== 64 | * Copyright 2012 Twitter, Inc. 65 | * 66 | * Licensed under the Apache License, Version 2.0 (the "License"); 67 | * you may not use this file except in compliance with the License. 68 | * You may obtain a copy of the License at 69 | * 70 | * http://www.apache.org/licenses/LICENSE-2.0 71 | * 72 | * Unless required by applicable law or agreed to in writing, software 73 | * distributed under the License is distributed on an "AS IS" BASIS, 74 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 75 | * See the License for the specific language governing permissions and 76 | * limitations under the License. 77 | * ========================================================== */ 78 | 79 | 80 | !function ($) { 81 | 82 | "use strict"; // jshint ;_; 83 | 84 | 85 | /* ALERT CLASS DEFINITION 86 | * ====================== */ 87 | 88 | var dismiss = '[data-dismiss="alert"]' 89 | , Alert = function (el) { 90 | $(el).on('click', dismiss, this.close) 91 | } 92 | 93 | Alert.prototype.close = function (e) { 94 | var $this = $(this) 95 | , selector = $this.attr('data-target') 96 | , $parent 97 | 98 | if (!selector) { 99 | selector = $this.attr('href') 100 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 101 | } 102 | 103 | $parent = $(selector) 104 | 105 | e && e.preventDefault() 106 | 107 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 108 | 109 | $parent.trigger(e = $.Event('close')) 110 | 111 | if (e.isDefaultPrevented()) return 112 | 113 | $parent.removeClass('in') 114 | 115 | function removeElement() { 116 | $parent 117 | .trigger('closed') 118 | .remove() 119 | } 120 | 121 | $.support.transition && $parent.hasClass('fade') ? 122 | $parent.on($.support.transition.end, removeElement) : 123 | removeElement() 124 | } 125 | 126 | 127 | /* ALERT PLUGIN DEFINITION 128 | * ======================= */ 129 | 130 | var old = $.fn.alert 131 | 132 | $.fn.alert = function (option) { 133 | return this.each(function () { 134 | var $this = $(this) 135 | , data = $this.data('alert') 136 | if (!data) $this.data('alert', (data = new Alert(this))) 137 | if (typeof option == 'string') data[option].call($this) 138 | }) 139 | } 140 | 141 | $.fn.alert.Constructor = Alert 142 | 143 | 144 | /* ALERT NO CONFLICT 145 | * ================= */ 146 | 147 | $.fn.alert.noConflict = function () { 148 | $.fn.alert = old 149 | return this 150 | } 151 | 152 | 153 | /* ALERT DATA-API 154 | * ============== */ 155 | 156 | $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) 157 | 158 | }(window.jQuery);/* ============================================================ 159 | * bootstrap-button.js v2.3.1 160 | * http://twitter.github.com/bootstrap/javascript.html#buttons 161 | * ============================================================ 162 | * Copyright 2012 Twitter, Inc. 163 | * 164 | * Licensed under the Apache License, Version 2.0 (the "License"); 165 | * you may not use this file except in compliance with the License. 166 | * You may obtain a copy of the License at 167 | * 168 | * http://www.apache.org/licenses/LICENSE-2.0 169 | * 170 | * Unless required by applicable law or agreed to in writing, software 171 | * distributed under the License is distributed on an "AS IS" BASIS, 172 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 173 | * See the License for the specific language governing permissions and 174 | * limitations under the License. 175 | * ============================================================ */ 176 | 177 | 178 | !function ($) { 179 | 180 | "use strict"; // jshint ;_; 181 | 182 | 183 | /* BUTTON PUBLIC CLASS DEFINITION 184 | * ============================== */ 185 | 186 | var Button = function (element, options) { 187 | this.$element = $(element) 188 | this.options = $.extend({}, $.fn.button.defaults, options) 189 | } 190 | 191 | Button.prototype.setState = function (state) { 192 | var d = 'disabled' 193 | , $el = this.$element 194 | , data = $el.data() 195 | , val = $el.is('input') ? 'val' : 'html' 196 | 197 | state = state + 'Text' 198 | data.resetText || $el.data('resetText', $el[val]()) 199 | 200 | $el[val](data[state] || this.options[state]) 201 | 202 | // push to event loop to allow forms to submit 203 | setTimeout(function () { 204 | state == 'loadingText' ? 205 | $el.addClass(d).attr(d, d) : 206 | $el.removeClass(d).removeAttr(d) 207 | }, 0) 208 | } 209 | 210 | Button.prototype.toggle = function () { 211 | var $parent = this.$element.closest('[data-toggle="buttons-radio"]') 212 | 213 | $parent && $parent 214 | .find('.active') 215 | .removeClass('active') 216 | 217 | this.$element.toggleClass('active') 218 | } 219 | 220 | 221 | /* BUTTON PLUGIN DEFINITION 222 | * ======================== */ 223 | 224 | var old = $.fn.button 225 | 226 | $.fn.button = function (option) { 227 | return this.each(function () { 228 | var $this = $(this) 229 | , data = $this.data('button') 230 | , options = typeof option == 'object' && option 231 | if (!data) $this.data('button', (data = new Button(this, options))) 232 | if (option == 'toggle') data.toggle() 233 | else if (option) data.setState(option) 234 | }) 235 | } 236 | 237 | $.fn.button.defaults = { 238 | loadingText: 'loading...' 239 | } 240 | 241 | $.fn.button.Constructor = Button 242 | 243 | 244 | /* BUTTON NO CONFLICT 245 | * ================== */ 246 | 247 | $.fn.button.noConflict = function () { 248 | $.fn.button = old 249 | return this 250 | } 251 | 252 | 253 | /* BUTTON DATA-API 254 | * =============== */ 255 | 256 | $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { 257 | var $btn = $(e.target) 258 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 259 | $btn.button('toggle') 260 | }) 261 | 262 | }(window.jQuery);/* ========================================================== 263 | * bootstrap-carousel.js v2.3.1 264 | * http://twitter.github.com/bootstrap/javascript.html#carousel 265 | * ========================================================== 266 | * Copyright 2012 Twitter, Inc. 267 | * 268 | * Licensed under the Apache License, Version 2.0 (the "License"); 269 | * you may not use this file except in compliance with the License. 270 | * You may obtain a copy of the License at 271 | * 272 | * http://www.apache.org/licenses/LICENSE-2.0 273 | * 274 | * Unless required by applicable law or agreed to in writing, software 275 | * distributed under the License is distributed on an "AS IS" BASIS, 276 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 277 | * See the License for the specific language governing permissions and 278 | * limitations under the License. 279 | * ========================================================== */ 280 | 281 | 282 | !function ($) { 283 | 284 | "use strict"; // jshint ;_; 285 | 286 | 287 | /* CAROUSEL CLASS DEFINITION 288 | * ========================= */ 289 | 290 | var Carousel = function (element, options) { 291 | this.$element = $(element) 292 | this.$indicators = this.$element.find('.carousel-indicators') 293 | this.options = options 294 | this.options.pause == 'hover' && this.$element 295 | .on('mouseenter', $.proxy(this.pause, this)) 296 | .on('mouseleave', $.proxy(this.cycle, this)) 297 | } 298 | 299 | Carousel.prototype = { 300 | 301 | cycle: function (e) { 302 | if (!e) this.paused = false 303 | if (this.interval) clearInterval(this.interval); 304 | this.options.interval 305 | && !this.paused 306 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 307 | return this 308 | } 309 | 310 | , getActiveIndex: function () { 311 | this.$active = this.$element.find('.item.active') 312 | this.$items = this.$active.parent().children() 313 | return this.$items.index(this.$active) 314 | } 315 | 316 | , to: function (pos) { 317 | var activeIndex = this.getActiveIndex() 318 | , that = this 319 | 320 | if (pos > (this.$items.length - 1) || pos < 0) return 321 | 322 | if (this.sliding) { 323 | return this.$element.one('slid', function () { 324 | that.to(pos) 325 | }) 326 | } 327 | 328 | if (activeIndex == pos) { 329 | return this.pause().cycle() 330 | } 331 | 332 | return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) 333 | } 334 | 335 | , pause: function (e) { 336 | if (!e) this.paused = true 337 | if (this.$element.find('.next, .prev').length && $.support.transition.end) { 338 | this.$element.trigger($.support.transition.end) 339 | this.cycle(true) 340 | } 341 | clearInterval(this.interval) 342 | this.interval = null 343 | return this 344 | } 345 | 346 | , next: function () { 347 | if (this.sliding) return 348 | return this.slide('next') 349 | } 350 | 351 | , prev: function () { 352 | if (this.sliding) return 353 | return this.slide('prev') 354 | } 355 | 356 | , slide: function (type, next) { 357 | var $active = this.$element.find('.item.active') 358 | , $next = next || $active[type]() 359 | , isCycling = this.interval 360 | , direction = type == 'next' ? 'left' : 'right' 361 | , fallback = type == 'next' ? 'first' : 'last' 362 | , that = this 363 | , e 364 | 365 | this.sliding = true 366 | 367 | isCycling && this.pause() 368 | 369 | $next = $next.length ? $next : this.$element.find('.item')[fallback]() 370 | 371 | e = $.Event('slide', { 372 | relatedTarget: $next[0] 373 | , direction: direction 374 | }) 375 | 376 | if ($next.hasClass('active')) return 377 | 378 | if (this.$indicators.length) { 379 | this.$indicators.find('.active').removeClass('active') 380 | this.$element.one('slid', function () { 381 | var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) 382 | $nextIndicator && $nextIndicator.addClass('active') 383 | }) 384 | } 385 | 386 | if ($.support.transition && this.$element.hasClass('slide')) { 387 | this.$element.trigger(e) 388 | if (e.isDefaultPrevented()) return 389 | $next.addClass(type) 390 | $next[0].offsetWidth // force reflow 391 | $active.addClass(direction) 392 | $next.addClass(direction) 393 | this.$element.one($.support.transition.end, function () { 394 | $next.removeClass([type, direction].join(' ')).addClass('active') 395 | $active.removeClass(['active', direction].join(' ')) 396 | that.sliding = false 397 | setTimeout(function () { that.$element.trigger('slid') }, 0) 398 | }) 399 | } else { 400 | this.$element.trigger(e) 401 | if (e.isDefaultPrevented()) return 402 | $active.removeClass('active') 403 | $next.addClass('active') 404 | this.sliding = false 405 | this.$element.trigger('slid') 406 | } 407 | 408 | isCycling && this.cycle() 409 | 410 | return this 411 | } 412 | 413 | } 414 | 415 | 416 | /* CAROUSEL PLUGIN DEFINITION 417 | * ========================== */ 418 | 419 | var old = $.fn.carousel 420 | 421 | $.fn.carousel = function (option) { 422 | return this.each(function () { 423 | var $this = $(this) 424 | , data = $this.data('carousel') 425 | , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) 426 | , action = typeof option == 'string' ? option : options.slide 427 | if (!data) $this.data('carousel', (data = new Carousel(this, options))) 428 | if (typeof option == 'number') data.to(option) 429 | else if (action) data[action]() 430 | else if (options.interval) data.pause().cycle() 431 | }) 432 | } 433 | 434 | $.fn.carousel.defaults = { 435 | interval: 5000 436 | , pause: 'hover' 437 | } 438 | 439 | $.fn.carousel.Constructor = Carousel 440 | 441 | 442 | /* CAROUSEL NO CONFLICT 443 | * ==================== */ 444 | 445 | $.fn.carousel.noConflict = function () { 446 | $.fn.carousel = old 447 | return this 448 | } 449 | 450 | /* CAROUSEL DATA-API 451 | * ================= */ 452 | 453 | $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { 454 | var $this = $(this), href 455 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 456 | , options = $.extend({}, $target.data(), $this.data()) 457 | , slideIndex 458 | 459 | $target.carousel(options) 460 | 461 | if (slideIndex = $this.attr('data-slide-to')) { 462 | $target.data('carousel').pause().to(slideIndex).cycle() 463 | } 464 | 465 | e.preventDefault() 466 | }) 467 | 468 | }(window.jQuery);/* ============================================================= 469 | * bootstrap-collapse.js v2.3.1 470 | * http://twitter.github.com/bootstrap/javascript.html#collapse 471 | * ============================================================= 472 | * Copyright 2012 Twitter, Inc. 473 | * 474 | * Licensed under the Apache License, Version 2.0 (the "License"); 475 | * you may not use this file except in compliance with the License. 476 | * You may obtain a copy of the License at 477 | * 478 | * http://www.apache.org/licenses/LICENSE-2.0 479 | * 480 | * Unless required by applicable law or agreed to in writing, software 481 | * distributed under the License is distributed on an "AS IS" BASIS, 482 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 483 | * See the License for the specific language governing permissions and 484 | * limitations under the License. 485 | * ============================================================ */ 486 | 487 | 488 | !function ($) { 489 | 490 | "use strict"; // jshint ;_; 491 | 492 | 493 | /* COLLAPSE PUBLIC CLASS DEFINITION 494 | * ================================ */ 495 | 496 | var Collapse = function (element, options) { 497 | this.$element = $(element) 498 | this.options = $.extend({}, $.fn.collapse.defaults, options) 499 | 500 | if (this.options.parent) { 501 | this.$parent = $(this.options.parent) 502 | } 503 | 504 | this.options.toggle && this.toggle() 505 | } 506 | 507 | Collapse.prototype = { 508 | 509 | constructor: Collapse 510 | 511 | , dimension: function () { 512 | var hasWidth = this.$element.hasClass('width') 513 | return hasWidth ? 'width' : 'height' 514 | } 515 | 516 | , show: function () { 517 | var dimension 518 | , scroll 519 | , actives 520 | , hasData 521 | 522 | if (this.transitioning || this.$element.hasClass('in')) return 523 | 524 | dimension = this.dimension() 525 | scroll = $.camelCase(['scroll', dimension].join('-')) 526 | actives = this.$parent && this.$parent.find('> .accordion-group > .in') 527 | 528 | if (actives && actives.length) { 529 | hasData = actives.data('collapse') 530 | if (hasData && hasData.transitioning) return 531 | actives.collapse('hide') 532 | hasData || actives.data('collapse', null) 533 | } 534 | 535 | this.$element[dimension](0) 536 | this.transition('addClass', $.Event('show'), 'shown') 537 | $.support.transition && this.$element[dimension](this.$element[0][scroll]) 538 | } 539 | 540 | , hide: function () { 541 | var dimension 542 | if (this.transitioning || !this.$element.hasClass('in')) return 543 | dimension = this.dimension() 544 | this.reset(this.$element[dimension]()) 545 | this.transition('removeClass', $.Event('hide'), 'hidden') 546 | this.$element[dimension](0) 547 | } 548 | 549 | , reset: function (size) { 550 | var dimension = this.dimension() 551 | 552 | this.$element 553 | .removeClass('collapse') 554 | [dimension](size || 'auto') 555 | [0].offsetWidth 556 | 557 | this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') 558 | 559 | return this 560 | } 561 | 562 | , transition: function (method, startEvent, completeEvent) { 563 | var that = this 564 | , complete = function () { 565 | if (startEvent.type == 'show') that.reset() 566 | that.transitioning = 0 567 | that.$element.trigger(completeEvent) 568 | } 569 | 570 | this.$element.trigger(startEvent) 571 | 572 | if (startEvent.isDefaultPrevented()) return 573 | 574 | this.transitioning = 1 575 | 576 | this.$element[method]('in') 577 | 578 | $.support.transition && this.$element.hasClass('collapse') ? 579 | this.$element.one($.support.transition.end, complete) : 580 | complete() 581 | } 582 | 583 | , toggle: function () { 584 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 585 | } 586 | 587 | } 588 | 589 | 590 | /* COLLAPSE PLUGIN DEFINITION 591 | * ========================== */ 592 | 593 | var old = $.fn.collapse 594 | 595 | $.fn.collapse = function (option) { 596 | return this.each(function () { 597 | var $this = $(this) 598 | , data = $this.data('collapse') 599 | , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) 600 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 601 | if (typeof option == 'string') data[option]() 602 | }) 603 | } 604 | 605 | $.fn.collapse.defaults = { 606 | toggle: true 607 | } 608 | 609 | $.fn.collapse.Constructor = Collapse 610 | 611 | 612 | /* COLLAPSE NO CONFLICT 613 | * ==================== */ 614 | 615 | $.fn.collapse.noConflict = function () { 616 | $.fn.collapse = old 617 | return this 618 | } 619 | 620 | 621 | /* COLLAPSE DATA-API 622 | * ================= */ 623 | 624 | $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { 625 | var $this = $(this), href 626 | , target = $this.attr('data-target') 627 | || e.preventDefault() 628 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 629 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 630 | $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') 631 | $(target).collapse(option) 632 | }) 633 | 634 | }(window.jQuery);/* ============================================================ 635 | * bootstrap-dropdown.js v2.3.1 636 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 637 | * ============================================================ 638 | * Copyright 2012 Twitter, Inc. 639 | * 640 | * Licensed under the Apache License, Version 2.0 (the "License"); 641 | * you may not use this file except in compliance with the License. 642 | * You may obtain a copy of the License at 643 | * 644 | * http://www.apache.org/licenses/LICENSE-2.0 645 | * 646 | * Unless required by applicable law or agreed to in writing, software 647 | * distributed under the License is distributed on an "AS IS" BASIS, 648 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 649 | * See the License for the specific language governing permissions and 650 | * limitations under the License. 651 | * ============================================================ */ 652 | 653 | 654 | !function ($) { 655 | 656 | "use strict"; // jshint ;_; 657 | 658 | 659 | /* DROPDOWN CLASS DEFINITION 660 | * ========================= */ 661 | 662 | var toggle = '[data-toggle=dropdown]' 663 | , Dropdown = function (element) { 664 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 665 | $('html').on('click.dropdown.data-api', function () { 666 | $el.parent().removeClass('open') 667 | }) 668 | } 669 | 670 | Dropdown.prototype = { 671 | 672 | constructor: Dropdown 673 | 674 | , toggle: function (e) { 675 | var $this = $(this) 676 | , $parent 677 | , isActive 678 | 679 | if ($this.is('.disabled, :disabled')) return 680 | 681 | $parent = getParent($this) 682 | 683 | isActive = $parent.hasClass('open') 684 | 685 | clearMenus() 686 | 687 | if (!isActive) { 688 | $parent.toggleClass('open') 689 | } 690 | 691 | $this.focus() 692 | 693 | return false 694 | } 695 | 696 | , keydown: function (e) { 697 | var $this 698 | , $items 699 | , $active 700 | , $parent 701 | , isActive 702 | , index 703 | 704 | if (!/(38|40|27)/.test(e.keyCode)) return 705 | 706 | $this = $(this) 707 | 708 | e.preventDefault() 709 | e.stopPropagation() 710 | 711 | if ($this.is('.disabled, :disabled')) return 712 | 713 | $parent = getParent($this) 714 | 715 | isActive = $parent.hasClass('open') 716 | 717 | if (!isActive || (isActive && e.keyCode == 27)) { 718 | if (e.which == 27) $parent.find(toggle).focus() 719 | return $this.click() 720 | } 721 | 722 | $items = $('[role=menu] li:not(.divider):visible a', $parent) 723 | 724 | if (!$items.length) return 725 | 726 | index = $items.index($items.filter(':focus')) 727 | 728 | if (e.keyCode == 38 && index > 0) index-- // up 729 | if (e.keyCode == 40 && index < $items.length - 1) index++ // down 730 | if (!~index) index = 0 731 | 732 | $items 733 | .eq(index) 734 | .focus() 735 | } 736 | 737 | } 738 | 739 | function clearMenus() { 740 | $(toggle).each(function () { 741 | getParent($(this)).removeClass('open') 742 | }) 743 | } 744 | 745 | function getParent($this) { 746 | var selector = $this.attr('data-target') 747 | , $parent 748 | 749 | if (!selector) { 750 | selector = $this.attr('href') 751 | selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 752 | } 753 | 754 | $parent = selector && $(selector) 755 | 756 | if (!$parent || !$parent.length) $parent = $this.parent() 757 | 758 | return $parent 759 | } 760 | 761 | 762 | /* DROPDOWN PLUGIN DEFINITION 763 | * ========================== */ 764 | 765 | var old = $.fn.dropdown 766 | 767 | $.fn.dropdown = function (option) { 768 | return this.each(function () { 769 | var $this = $(this) 770 | , data = $this.data('dropdown') 771 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 772 | if (typeof option == 'string') data[option].call($this) 773 | }) 774 | } 775 | 776 | $.fn.dropdown.Constructor = Dropdown 777 | 778 | 779 | /* DROPDOWN NO CONFLICT 780 | * ==================== */ 781 | 782 | $.fn.dropdown.noConflict = function () { 783 | $.fn.dropdown = old 784 | return this 785 | } 786 | 787 | 788 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 789 | * =================================== */ 790 | 791 | $(document) 792 | .on('click.dropdown.data-api', clearMenus) 793 | .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) 794 | .on('click.dropdown-menu', function (e) { e.stopPropagation() }) 795 | .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle) 796 | .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown) 797 | 798 | }(window.jQuery); 799 | /* ========================================================= 800 | * bootstrap-modal.js v2.3.1 801 | * http://twitter.github.com/bootstrap/javascript.html#modals 802 | * ========================================================= 803 | * Copyright 2012 Twitter, Inc. 804 | * 805 | * Licensed under the Apache License, Version 2.0 (the "License"); 806 | * you may not use this file except in compliance with the License. 807 | * You may obtain a copy of the License at 808 | * 809 | * http://www.apache.org/licenses/LICENSE-2.0 810 | * 811 | * Unless required by applicable law or agreed to in writing, software 812 | * distributed under the License is distributed on an "AS IS" BASIS, 813 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 814 | * See the License for the specific language governing permissions and 815 | * limitations under the License. 816 | * ========================================================= */ 817 | 818 | 819 | !function ($) { 820 | 821 | "use strict"; // jshint ;_; 822 | 823 | 824 | /* MODAL CLASS DEFINITION 825 | * ====================== */ 826 | 827 | var Modal = function (element, options) { 828 | this.options = options 829 | this.$element = $(element) 830 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 831 | this.options.remote && this.$element.find('.modal-body').load(this.options.remote) 832 | } 833 | 834 | Modal.prototype = { 835 | 836 | constructor: Modal 837 | 838 | , toggle: function () { 839 | return this[!this.isShown ? 'show' : 'hide']() 840 | } 841 | 842 | , show: function () { 843 | var that = this 844 | , e = $.Event('show') 845 | 846 | this.$element.trigger(e) 847 | 848 | if (this.isShown || e.isDefaultPrevented()) return 849 | 850 | this.isShown = true 851 | 852 | this.escape() 853 | 854 | this.backdrop(function () { 855 | var transition = $.support.transition && that.$element.hasClass('fade') 856 | 857 | if (!that.$element.parent().length) { 858 | that.$element.appendTo(document.body) //don't move modals dom position 859 | } 860 | 861 | that.$element.show() 862 | 863 | if (transition) { 864 | that.$element[0].offsetWidth // force reflow 865 | } 866 | 867 | that.$element 868 | .addClass('in') 869 | .attr('aria-hidden', false) 870 | 871 | that.enforceFocus() 872 | 873 | transition ? 874 | that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) : 875 | that.$element.focus().trigger('shown') 876 | 877 | }) 878 | } 879 | 880 | , hide: function (e) { 881 | e && e.preventDefault() 882 | 883 | var that = this 884 | 885 | e = $.Event('hide') 886 | 887 | this.$element.trigger(e) 888 | 889 | if (!this.isShown || e.isDefaultPrevented()) return 890 | 891 | this.isShown = false 892 | 893 | this.escape() 894 | 895 | $(document).off('focusin.modal') 896 | 897 | this.$element 898 | .removeClass('in') 899 | .attr('aria-hidden', true) 900 | 901 | $.support.transition && this.$element.hasClass('fade') ? 902 | this.hideWithTransition() : 903 | this.hideModal() 904 | } 905 | 906 | , enforceFocus: function () { 907 | var that = this 908 | $(document).on('focusin.modal', function (e) { 909 | if (that.$element[0] !== e.target && !that.$element.has(e.target).length) { 910 | that.$element.focus() 911 | } 912 | }) 913 | } 914 | 915 | , escape: function () { 916 | var that = this 917 | if (this.isShown && this.options.keyboard) { 918 | this.$element.on('keyup.dismiss.modal', function ( e ) { 919 | e.which == 27 && that.hide() 920 | }) 921 | } else if (!this.isShown) { 922 | this.$element.off('keyup.dismiss.modal') 923 | } 924 | } 925 | 926 | , hideWithTransition: function () { 927 | var that = this 928 | , timeout = setTimeout(function () { 929 | that.$element.off($.support.transition.end) 930 | that.hideModal() 931 | }, 500) 932 | 933 | this.$element.one($.support.transition.end, function () { 934 | clearTimeout(timeout) 935 | that.hideModal() 936 | }) 937 | } 938 | 939 | , hideModal: function () { 940 | var that = this 941 | this.$element.hide() 942 | this.backdrop(function () { 943 | that.removeBackdrop() 944 | that.$element.trigger('hidden') 945 | }) 946 | } 947 | 948 | , removeBackdrop: function () { 949 | this.$backdrop && this.$backdrop.remove() 950 | this.$backdrop = null 951 | } 952 | 953 | , backdrop: function (callback) { 954 | var that = this 955 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 956 | 957 | if (this.isShown && this.options.backdrop) { 958 | var doAnimate = $.support.transition && animate 959 | 960 | this.$backdrop = $('