├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── ResourcesGrailsPlugin.groovy
├── application.properties
├── docs
└── Core Concepts.gdoc
├── grails-app
├── conf
│ ├── BlueprintResources.groovy
│ ├── BuildConfig.groovy
│ ├── Config.groovy
│ ├── DataSource.groovy
│ ├── JQueryResources.groovy
│ ├── META-INF
│ │ └── mime.types
│ ├── OverrideResources.groovy
│ ├── ResourcesBootStrap.groovy
│ ├── TestOnlyResources.groovy
│ └── UrlMappings.groovy
├── i18n
│ └── messages.properties
├── resourceMappers
│ └── org
│ │ └── grails
│ │ └── plugin
│ │ └── resource
│ │ ├── BaseUrlResourceMapper.groovy
│ │ ├── BundleResourceMapper.groovy
│ │ ├── CSSPreprocessorResourceMapper.groovy
│ │ ├── CSSRewriterResourceMapper.groovy
│ │ └── test
│ │ └── TestResourceMapper.groovy
├── taglib
│ └── org
│ │ └── grails
│ │ └── plugin
│ │ └── resource
│ │ └── ResourceTagLib.groovy
└── views
│ └── index.gsp
├── grailsw
├── grailsw.bat
├── src
├── docs
│ ├── guide
│ │ ├── 1. Overview.gdoc
│ │ ├── 1.1 Quick Start.gdoc
│ │ ├── 1.1.1 Make sure jQuery plugin is installed.gdoc
│ │ ├── 1.1.2 Install jQuery UI and Blueprint plugins.gdoc
│ │ ├── 1.1.3 Edit your Sitemesh layout.gdoc
│ │ ├── 1.1.4 Edit your GSP page to include jQuery.gdoc
│ │ ├── 1.1.5 View the page source.gdoc
│ │ ├── 1.1.6 Now optimize your application.gdoc
│ │ ├── 10. Security.gdoc
│ │ ├── 2. Concepts.gdoc
│ │ ├── 3. Declaring resources.gdoc
│ │ ├── 3.1 The resource DSL.gdoc
│ │ ├── 3.1.1 The dependsOn method.gdoc
│ │ ├── 3.1.2 The resource method.gdoc
│ │ ├── 3.1.3 The defaultBundle method.gdoc
│ │ ├── 3.2 Resource artefacts.gdoc
│ │ ├── 3.3 Config.groovy.gdoc
│ │ ├── 3.4 Bundling.gdoc
│ │ ├── 4. Using resources.gdoc
│ │ ├── 4.1 Linking to CSS, JavaScript etc..gdoc
│ │ ├── 4.2 Linking to images.gdoc
│ │ ├── 4.3 Linking to resources explicitly, bypassing modules.gdoc
│ │ ├── 4.4 Including pieces of JavaScript code generated at runtime.gdoc
│ │ ├── 5. Overriding resources.gdoc
│ │ ├── 6. Creating custom mappers.gdoc
│ │ ├── 6.1 Defining a mapper.gdoc
│ │ ├── 6.2 Mapper phases and priority.gdoc
│ │ ├── 6.3 Operation.gdoc
│ │ ├── 6.4 Processing only the right types of files.gdoc
│ │ ├── 6.5 Adding response headers and intercepting requests.gdoc
│ │ ├── 7. Writing plugins that use Resources.gdoc
│ │ ├── 8. Debugging.gdoc
│ │ └── 9. Configuration.gdoc
│ └── ref
│ │ ├── Mappers
│ │ ├── baseurl.gdoc
│ │ ├── bundle.gdoc
│ │ ├── csspreprocessor.gdoc
│ │ └── cssrewriter.gdoc
│ │ └── Tags
│ │ ├── external.gdoc
│ │ ├── img.gdoc
│ │ ├── layoutResources.gdoc
│ │ ├── require.gdoc
│ │ ├── resource.gdoc
│ │ ├── resourceLink.gdoc
│ │ ├── script.gdoc
│ │ ├── stash.gdoc
│ │ ├── style.gdoc
│ │ └── use.gdoc
├── groovy
│ └── org
│ │ └── grails
│ │ └── plugin
│ │ └── resource
│ │ ├── AggregatedResourceMeta.groovy
│ │ ├── CSSBundleResourceMeta.groovy
│ │ ├── CSSLinkProcessor.groovy
│ │ ├── DevModeSanityFilter.groovy
│ │ ├── JavaScriptBundleResourceMeta.groovy
│ │ ├── ProcessingFilter.groovy
│ │ ├── RequestUtil.java
│ │ ├── ResourceMeta.groovy
│ │ ├── ResourceModule.groovy
│ │ ├── ResourceProcessor.groovy
│ │ ├── ResourceProcessorBatch.groovy
│ │ ├── URLUtils.java
│ │ ├── mapper
│ │ ├── MapperPhase.groovy
│ │ ├── ResourceMapper.groovy
│ │ └── ResourceMappersFactory.groovy
│ │ ├── module
│ │ ├── ModuleBuilder.groovy
│ │ ├── ModuleDeclarationsFactory.groovy
│ │ └── ModulesBuilder.groovy
│ │ └── util
│ │ ├── DispositionsUtils.groovy
│ │ ├── HalfBakedLegacyLinkGenerator.groovy
│ │ ├── ResourceMetaStore.groovy
│ │ └── StatsManager.groovy
└── java
│ └── org
│ └── grails
│ └── plugin
│ └── resources
│ ├── artefacts
│ ├── AbstractResourcesArtefactHandler.java
│ ├── DefaultResourceMapperClass.java
│ ├── DefaultResourcesClass.java
│ ├── ResourceMapperArtefactHandler.java
│ ├── ResourceMapperClass.java
│ ├── ResourcesArtefactHandler.java
│ └── ResourcesClass.java
│ └── stash
│ ├── ScriptStashWriter.java
│ ├── StashManager.java
│ ├── StashWriter.java
│ └── StyleStashWriter.java
├── test
├── integration
│ ├── InitialisationSmokeTests.groovy
│ └── org
│ │ └── grails
│ │ └── plugin
│ │ ├── resource
│ │ ├── LegacyResourceIntegrationSpec.groovy
│ │ ├── ResourceProcessorIntegTests.groovy
│ │ ├── ResourceTagLibIntegTests.groovy
│ │ └── module
│ │ │ └── ModulesIntegTests.groovy
│ │ └── resources
│ │ └── stash
│ │ └── StashManagerIntegrationTests.groovy
├── test-files
│ ├── image.png
│ └── somehack.xml
└── unit
│ └── org
│ └── grails
│ └── plugin
│ ├── resource
│ ├── AggregatedResourceMetaTests.groovy
│ ├── BaseUrlResourceMapperSpec.groovy
│ ├── BundleResourceMapperTests.groovy
│ ├── CSSLinkProcessorTests.groovy
│ ├── CSSPreprocessorResourceMapperTests.groovy
│ ├── CSSRewriterResourceMapperTests.groovy
│ ├── PathMatcherTests.groovy
│ ├── ProcessingFilterTests.groovy
│ ├── ResourceMapperTests.groovy
│ ├── ResourceMetaStoreTests.groovy
│ ├── ResourceMetaTests.groovy
│ ├── ResourceModuleTests.groovy
│ ├── ResourceModulesBuilderTests.groovy
│ ├── ResourceProcessorSpec.groovy
│ ├── ResourceProcessorTests.groovy
│ ├── ResourceTagLibTests.groovy
│ ├── URLUtilsSpec.groovy
│ ├── URLUtilsTests.groovy
│ └── util
│ │ └── DispositionsUtilsTests.groovy
│ └── resources
│ └── stash
│ ├── ScriptStashWriterUnitTests.groovy
│ └── StyleStashWriterUnitTests.groovy
├── travis-build.sh
├── web-app
├── GPRESOURCES-207
│ ├── file1.js
│ ├── file2.js
│ ├── file3.js
│ ├── file4.js
│ └── file5.js
├── GPRESOURCES-210
│ ├── file1.js
│ └── file2.js
├── WEB-INF
│ ├── applicationContext.xml
│ ├── sitemesh.xml
│ └── tld
│ │ ├── c.tld
│ │ ├── fmt.tld
│ │ ├── grails.tld
│ │ └── spring.tld
├── css
│ ├── blueprint
│ │ ├── ie.css
│ │ ├── screen.css
│ │ └── src
│ │ │ └── grid.png
│ ├── errors.css
│ ├── legacy.css
│ ├── main.css
│ └── mobile.css
├── images
│ ├── apple-touch-icon-retina.png
│ ├── apple-touch-icon.png
│ ├── favicon.ico
│ ├── grails_logo.jpg
│ ├── grails_logo.png
│ ├── leftnav_btm.png
│ ├── leftnav_midstretch.png
│ ├── leftnav_top.png
│ ├── skin
│ │ ├── database_add.png
│ │ ├── database_delete.png
│ │ ├── database_edit.png
│ │ ├── database_save.png
│ │ ├── database_table.png
│ │ ├── exclamation.png
│ │ ├── house.png
│ │ ├── information.png
│ │ ├── shadow.jpg
│ │ ├── sorted_asc.gif
│ │ └── sorted_desc.gif
│ ├── spinner.gif
│ └── springsource.png
└── js
│ ├── adhoc.js
│ ├── application.js
│ ├── core.js
│ ├── jquery-ui
│ ├── jquery-ui-1.8.5.custom.min.js
│ └── themes
│ │ └── custom-theme
│ │ ├── images
│ │ ├── ui-bg_diagonals-thick_90_eeeeee_40x40.png
│ │ ├── ui-bg_flat_15_cd0a0a_40x100.png
│ │ ├── ui-bg_glass_100_e4f1fb_1x400.png
│ │ ├── ui-bg_glass_50_3baae3_1x400.png
│ │ ├── ui-bg_glass_80_d7ebf9_1x400.png
│ │ ├── ui-bg_highlight-hard_100_f2f5f7_1x100.png
│ │ ├── ui-bg_highlight-hard_70_000000_1x100.png
│ │ ├── ui-bg_highlight-soft_100_deedf7_1x100.png
│ │ ├── ui-bg_highlight-soft_25_ffef8f_1x100.png
│ │ ├── ui-icons_2694e8_256x240.png
│ │ ├── ui-icons_2e83ff_256x240.png
│ │ ├── ui-icons_3d80b3_256x240.png
│ │ ├── ui-icons_72a7cf_256x240.png
│ │ └── ui-icons_ffffff_256x240.png
│ │ └── jquery-ui-1.8.5.custom.css
│ └── jquery
│ ├── jquery-1.4.2-b.min.js
│ └── jquery-1.4.2.min.js
└── wrapper
├── grails-wrapper-runtime-2.3.11.jar
├── grails-wrapper.properties
└── springloaded-1.2.0.RELEASE.jar
/.gitignore:
--------------------------------------------------------------------------------
1 | #IDEA
2 | .idea/
3 | *.iml
4 | *.ipr
5 | *.iws
6 |
7 | #Eclipse
8 | .settings/
9 | .classpath
10 | .project
11 |
12 | #compiled sources
13 | target/*
14 | target-eclipse/*
15 | out/*
16 | *.zip
17 |
18 | #logs
19 | *.log
20 |
21 | #other
22 | plugin.xml
23 | dependencies.groovy
24 | /test-tmp/
25 | /src/docs/ref/Resources/
26 | *.swp
27 |
28 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: groovy
2 | jdk:
3 | - openjdk7
4 | - openjdk6
5 | - oraclejdk7
6 | before_script:
7 | - rm -rf target
8 | script: ./travis-build.sh
9 | env:
10 | global:
11 | - GIT_NAME="Graeme Rocher"
12 | - GIT_EMAIL="graeme.rocher@gmail.com"
13 | - secure: LrVonKDVyNsS3GrErpKeqicnRlwiXQi/LylPL0now266SM1E3clBGyZxA89g7ItqFpqQivwANysTpRL0nO8gd+EkyVG5B19P9Fq0eELLglhinU4T/T5MmWl0a2GFQSyewZDjJDrQQl1zMG3ejbl9QrFauwM4NfPcVMpyON3wjOY=
14 | - secure: eqMOkrSZOS44IZj6cK5i8/JCneWyMRCuhq7Gq+AMSIJFiw6c9ZMkooco/LLL2OV7D9f6yYmZXSDXWLv1scy9/YqrQftYrI7VyVA4Zwb4+MGag2kJ/T7jRtQ/FkYmSBex6VRuzGPN+ZyTdjuaJm3TJoCZwWfGE3Sw9OPYRRLEZAA=
15 | - secure: Ieprqq+UrMPivvu4cZXK4h+Vwl6QTTv/RYKFJddoN7FxHYoSqxXNNz3MBB0OS3DDM3jyHPLapvolWh8/4zis9hKl8R3C2Al00hiNBRtH0urx+IamvUcki6VEl/NSd99QQGCxqZn+zCH//N4nO5bS/u4fVnP5LfC+KBshdbPFrkU=
16 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Licensed under the Apache License, Version 2.0 (the "License");
3 | you may not use this file except in compliance with the License.
4 | You may obtain a copy of the License at
5 |
6 | http://www.apache.org/licenses/LICENSE-2.0
7 |
8 | Unless required by applicable law or agreed to in writing, software
9 | distributed under the License is distributed on an "AS IS" BASIS,
10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | See the License for the specific language governing permissions and
12 | limitations under the License.
13 |
14 | (c) 2009 Marc Palmer / AnyWare Ltd. www.grailsrocks.com
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](http://travis-ci.org/grails-plugins/grails-resources)
4 |
5 | Grails Resources framework.
6 | ===========================
7 |
8 | Grails resources provides a DSL for declaring static resources in plugins and in apps, and a mapper artefact that allows other plugins to provide processing of static resources to e.g. minify / zip them.
9 |
10 | All processing is performed at runtime once, against files in the server filesystem.
11 |
12 | Built-in mappers included in the plugin:
13 |
14 | * CSS rewriter (two mappers required, happens automatically)
15 | * Bundler (combines multiple css or js files into one)
16 |
17 | See docs at http://grails-plugins.github.io/grails-resources/
18 |
--------------------------------------------------------------------------------
/application.properties:
--------------------------------------------------------------------------------
1 | #Grails Metadata file
2 | #Sat Sep 14 14:20:38 MST 2013
3 | app.grails.version=2.3.11
4 | app.name=resources
5 |
--------------------------------------------------------------------------------
/docs/Core Concepts.gdoc:
--------------------------------------------------------------------------------
1 | h1. Core Concepts
2 |
3 | h2. Declared resources
4 |
5 | Normally you will declare most of your resources (or plugins that provide
6 | libraries will do this for you), and the Resources framework will know about
7 | your application's core resources in advance.
8 |
9 | These resources will be processed at startup up and be ready to go from the
10 | moment your application is able to receive requests.
11 |
12 | You reference these resources in your views using the resource tags e.g.
13 | r:require, r:img, r:resource, r:resourceLink.
14 |
15 | h2. Ad-hoc resources
16 |
17 | Some resources are not declared in advance. These are typically images that
18 | you use throughout your HTML but do not wish to declare in advance, as well as
19 | those referenced by CSS files you have declared.
20 |
21 | These are called "ad-hoc" resources because you have provided no formal
22 | resource definition for them. As such they assume default behaviour for their
23 | resource type, and if you need to alter this behaviour (for example provide
24 | extra HTML attributes for them), you should declare them explicitly.
25 |
26 | You link to ad-hoc resources either implicitly by including, for example, a
27 | CSS file that references other image resources, or by using the r:img,
28 | r:resource or r:resourceLink tags.
29 |
30 | h2. Legacy resources
31 |
32 | With existing applications and plugins that do not use the Resources
33 | framework, the r:xxx tags are not used and few if any resources are declared.
34 |
35 | In this eventuality, Resources framework intercepts these requests and issues
36 | redirects to the processed versions of these resources. This means that
37 | regular
tags, CSS,
10 |
11 | {code}
12 |
13 | This would output the ");
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/java/org/grails/plugin/resources/stash/StashManager.java:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resources.stash;
2 |
3 | import org.grails.plugin.resource.util.DispositionsUtils;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import java.io.IOException;
7 | import java.io.Writer;
8 | import java.util.*;
9 |
10 | /**
11 | * Manages the stashing and unstashing of page fragments.
12 | *
13 | * @author Patrick Jungermann
14 | */
15 | public class StashManager {
16 |
17 | /**
18 | * Prefix used for storing page fragment stashes.
19 | */
20 | public static final String REQ_ATTR_PREFIX_PAGE_FRAGMENTS = "resources.plugin.page.fragments";
21 |
22 | /**
23 | * Registered, usable stash writers.
24 | */
25 | public static final Map STASH_WRITERS = new HashMap();
26 | static {
27 | // register the basic stash writers
28 | STASH_WRITERS.put("script", new ScriptStashWriter());
29 | STASH_WRITERS.put("style", new StyleStashWriter());
30 | }
31 |
32 | /**
33 | * Stashes a page fragment.
34 | *
35 | * @param request The current request, at which the page fragment has to be stashed.
36 | * @param type The stash's (writer) type.
37 | * @param disposition The disposition, at which the page fragment has to be unstashed.
38 | * @param fragment The fragment, which has to be stashed.
39 | */
40 | @SuppressWarnings("unchecked")
41 | public static void stashPageFragment(final HttpServletRequest request, final String type, final String disposition, final String fragment) {
42 | final String resourceTrackerName = makePageFragmentKey(type, disposition);
43 | DispositionsUtils.addDispositionToRequest(request, disposition, "-page-fragments-");
44 |
45 | List resourceTracker = (List) request.getAttribute(resourceTrackerName);
46 | if (resourceTracker == null) {
47 | resourceTracker = new ArrayList();
48 | request.setAttribute(resourceTrackerName, resourceTracker);
49 | }
50 | resourceTracker.add(fragment);
51 | }
52 |
53 | /**
54 | * Unstashes the disposition's page fragments.
55 | *
56 | * @param out The target, to which all fragments have to be written to.
57 | * @param request The request, at which the fragments have been stashed.
58 | * @param disposition The disposition, for which all fragments have to be rendered.
59 | * @throws IOException if there was any problem with writing the fragments.
60 | */
61 | public static void unstashPageFragments(final Writer out, final HttpServletRequest request, final String disposition) throws IOException {
62 | for (final String type : STASH_WRITERS.keySet()) {
63 | List stash = consumePageFragments(request, type, disposition);
64 | if (!stash.isEmpty()) {
65 | STASH_WRITERS.get(type).write(out, stash);
66 | }
67 | }
68 | }
69 |
70 | /**
71 | * Returns the stash (all page fragments) of the requested type and disposition.
72 | *
73 | * @param request The request, at which the fragments have been stashed.
74 | * @param type The fragments' type.
75 | * @param disposition The disposition, for which the fragments have to be returned.
76 | * @return All fragments of the requested type and disposition.
77 | */
78 | @SuppressWarnings("unchecked")
79 | private static List consumePageFragments(final HttpServletRequest request, final String type, final String disposition) {
80 | List stash = (List) request.getAttribute(makePageFragmentKey(type, disposition));
81 | return stash != null ? stash : Collections.EMPTY_LIST;
82 | }
83 |
84 | /**
85 | * Returns the page fragment key, used to store the page fragment stashes.
86 | *
87 | * @param type The fragments' type.
88 | * @param disposition The fragments' disposition.
89 | * @return The page fragment key.
90 | */
91 | private static String makePageFragmentKey(final String type, final String disposition) {
92 | return REQ_ATTR_PREFIX_PAGE_FRAGMENTS + ":" + type + ":" + disposition;
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/java/org/grails/plugin/resources/stash/StashWriter.java:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resources.stash;
2 |
3 | import java.io.IOException;
4 | import java.io.Writer;
5 | import java.util.List;
6 |
7 | /**
8 | * Writes a stash to the output.
9 | *
10 | * @author Patrick Jungermann
11 | */
12 | public interface StashWriter {
13 |
14 | /**
15 | * Writes the stash's content to the writer.
16 | *
17 | * @param out The output writer.
18 | * @param stash The stash.
19 | * @throws IOException if there was a problem with writing the content to the writer.
20 | */
21 | void write(Writer out, List stash) throws IOException;
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/java/org/grails/plugin/resources/stash/StyleStashWriter.java:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resources.stash;
2 |
3 | import java.io.IOException;
4 | import java.io.Writer;
5 | import java.util.List;
6 |
7 | /**
8 | * Writes stashed styles to the output. All stashed fragments will be written into one style tag, in order.
9 | *
10 | * @author Patrick Jungermann
11 | */
12 | public class StyleStashWriter implements StashWriter {
13 |
14 | @Override
15 | public void write(final Writer out, final List stash) throws IOException {
16 | if (stash.isEmpty()) {
17 | return;
18 | }
19 |
20 | out.write("");
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/test/integration/InitialisationSmokeTests.groovy:
--------------------------------------------------------------------------------
1 | import grails.test.mixin.integration.IntegrationTestMixin
2 | import grails.test.mixin.TestMixin
3 |
4 | @TestMixin(IntegrationTestMixin)
5 | class InitialisationSmokeTests {
6 |
7 | def grailsResourceProcessor
8 |
9 | /**
10 | * We are testing that the resources plugin operates correctly in an integration testing environment.
11 | * That is, it does not cause issues for users when installed and they are running integration tests.
12 | *
13 | * @see TestOnlyResources
14 | */
15 | void testInitialisedOk() {
16 | //@todo this temporarily removed until Grails fixes the problems with servletContext resource loading/another workaround found
17 | //assert grailsResourceProcessor.getModule("jquery") != null
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/test/integration/org/grails/plugin/resource/LegacyResourceIntegrationSpec.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.spock.IntegrationSpec
4 | import org.codehaus.groovy.grails.commons.GrailsApplication
5 | import org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletRequest
6 | import org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletResponse
7 |
8 | class LegacyResourceIntegrationSpec extends IntegrationSpec {
9 |
10 | ResourceProcessor grailsResourceProcessor
11 | GrailsApplication grailsApplication
12 |
13 | // GPRESOURCES-214
14 | def 'legacy resource with baseurl'() {
15 | grailsApplication.config.grails.resources.mappers.baseurl.enabled = true
16 | grailsApplication.config.grails.resources.mappers.baseurl.default = "http://cdn.domain.com/static"
17 |
18 | GrailsMockHttpServletRequest request = new GrailsMockHttpServletRequest()
19 | GrailsMockHttpServletResponse response = new GrailsMockHttpServletResponse()
20 |
21 | request.requestURI = "/images/springsource.png"
22 |
23 | when:
24 | grailsResourceProcessor.processLegacyResource(request, response)
25 |
26 | then:
27 | response.redirectedUrl == "http://cdn.domain.com/static/images/_springsource.png"
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/test/integration/org/grails/plugin/resource/ResourceProcessorIntegTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.integration.IntegrationTestMixin
4 | import grails.test.mixin.TestMixin
5 |
6 | @TestMixin(IntegrationTestMixin)
7 | class ResourceProcessorIntegTests {
8 |
9 | def grailsResourceProcessor
10 |
11 | protected makeMockResource(uri) {
12 | [
13 | uri:uri,
14 | disposition:'head',
15 | exists: { -> true }
16 | ]
17 | }
18 |
19 | void testGettingModulesInDependencyOrder() {
20 | def testModules = [
21 | a: [name:'a', resources: [ makeMockResource('a.css') ] ],
22 | b: [name:'b', dependsOn:['a'], resources: [ makeMockResource('b.css') ] ],
23 | c: [name:'c', dependsOn:['a', 'b'], resources: [ makeMockResource('a.css') ] ],
24 | d: [name:'d', dependsOn:['b'], resources: [ makeMockResource('a.css') ] ],
25 | e: [name:'e', dependsOn:['d'], resources: [ makeMockResource('a.css') ] ]
26 | ]
27 |
28 | def modsNeeded = [
29 | e: true,
30 | c: true
31 | ]
32 |
33 | grailsResourceProcessor.modulesByName.putAll(testModules)
34 | grailsResourceProcessor.updateDependencyOrder()
35 |
36 | def moduleNames = grailsResourceProcessor.getAllModuleNamesRequired(modsNeeded)
37 | println "Module names: ${moduleNames}"
38 | def moduleNameResults = grailsResourceProcessor.getModulesInDependencyOrder(moduleNames)
39 | println "Modules: ${moduleNameResults}"
40 |
41 | assert moduleNameResults.indexOf('a') < moduleNameResults.indexOf('b')
42 | assert moduleNameResults.indexOf('b') < moduleNameResults.indexOf('c')
43 | assert moduleNameResults.indexOf('b') < moduleNameResults.indexOf('d')
44 | assert moduleNameResults.indexOf('b') < moduleNameResults.indexOf('e')
45 | assert moduleNameResults.indexOf('d') < moduleNameResults.indexOf('e')
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/integration/org/grails/plugin/resource/module/ModulesIntegTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource.module
2 |
3 | import grails.test.GroovyPagesTestCase
4 | import grails.test.mixin.integration.IntegrationTestMixin
5 | import grails.test.mixin.TestMixin
6 |
7 | /**
8 | * Integration tests of constructing modules.
9 | *
10 | * @author peter
11 | */
12 | @TestMixin(IntegrationTestMixin)
13 | class ModulesIntegTests extends GroovyPagesTestCase {
14 |
15 | def grailsResourceProcessor
16 | def grailsApplication
17 |
18 | protected makeMockResource(uri) {
19 | [
20 | uri:uri,
21 | disposition:'head',
22 | exists: { -> true }
23 | ]
24 | }
25 |
26 | void testGrailsApplicationAccessInClosure() {
27 |
28 | def template = '''
29 |
30 |
31 |
32 |
33 |
34 | Hi
35 |
36 | '''
37 | def result = applyTemplate(template, [:])
38 |
39 | assertTrue result.contains("")
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/test/test-files/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/test/test-files/image.png
--------------------------------------------------------------------------------
/test/test-files/somehack.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/AggregatedResourceMetaTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 | import grails.test.mixin.TestMixin
3 | import grails.test.mixin.support.GrailsUnitTestMixin
4 | import org.junit.Rule
5 | import org.junit.rules.TemporaryFolder
6 |
7 | @TestMixin(GrailsUnitTestMixin)
8 | class AggregatedResourceMetaTests {
9 | @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder()
10 | File temporarySubfolder
11 |
12 | def mockResSvc
13 | def module
14 |
15 | @org.junit.Before
16 | void setupTest() {
17 | temporarySubfolder = temporaryFolder.newFolder('test-tmp')
18 |
19 | module = new ResourceModule()
20 | module.name = 'aggmodule'
21 |
22 | mockResSvc = [
23 | config : [ ],
24 | updateDependencyOrder: { -> },
25 | modulesInDependencyOrder: [module.name],
26 | getMimeType: { String str -> 'text/plain' },
27 | makeFileForURI: { uri -> new File(temporarySubfolder, uri)}
28 | ]
29 |
30 | }
31 |
32 | protected ResourceMeta makeRes(String reluri, String contents) {
33 | def base = new File('./test-tmp/')
34 | base.mkdirs()
35 |
36 | def r = new ResourceMeta(sourceUrl:'/'+reluri)
37 | r.workDir = base
38 | r.actualUrl = r.sourceUrl
39 | r.disposition = 'head'
40 | r.contentType = "text/css"
41 | r.processedFile = new File(base, reluri)
42 | r.processedFile.parentFile.mkdirs()
43 | r.processedFile.delete()
44 | r.module = module
45 |
46 | r.processedFile << new ByteArrayInputStream(contents.bytes)
47 | return r
48 | }
49 |
50 | /**
51 | * Ensure that bundle mapper updates content length and exists()
52 | */
53 | void testUpdatesMetadata() {
54 | def r = new AggregatedResourceMeta()
55 |
56 | def r1 = makeRes('/aggtest/file1.css', "/* file 1 */")
57 | def r2 = makeRes('/aggtest/file2.css', "/* file 2 */")
58 |
59 | r.add(r1)
60 | r.add(r2)
61 |
62 | r.sourceUrl = '/aggtest1.css'
63 | assertFalse r.exists()
64 |
65 | r.beginPrepare(mockResSvc)
66 |
67 | r.endPrepare(mockResSvc)
68 |
69 | assertTrue r.exists()
70 | assertTrue r.contentLength >= r1.contentLength + r2.contentLength
71 | }
72 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/BaseUrlResourceMapperSpec.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import spock.lang.Specification
4 |
5 | class BaseUrlResourceMapperSpec extends Specification {
6 |
7 | def mapper
8 |
9 | def setup(){
10 | mapper = new BaseUrlResourceMapper()
11 | }
12 |
13 | def "test that mappers are configured correctly"(){
14 | setup:
15 | def resource = [ linkUrl : '/images.jpg' ]
16 | def config = [ enabled: true, default: 'http://www.google.com/' ]
17 | when:
18 | mapper.map( resource, config )
19 | then:
20 | resource.linkOverride == 'http://www.google.com/images.jpg'
21 | }
22 |
23 | def "when mappers are disabled, links are not processed"(){
24 | setup:
25 | def resource = [ linkUrl : '/images.jpg' ]
26 | def config = [ enabled: false, default: 'http://www.google.com/' ]
27 | when:
28 | mapper.map( resource, config )
29 | then:
30 | resource.linkOverride == null
31 | }
32 |
33 | def "a resource can set a unique url based on module name"(){
34 | setup:
35 | def resource = [ linkUrl : '/images.jpg', module: [ name: 'uno'] ]
36 | def config = [ enabled: true, default:'http://www.google.com/', modules : [ uno: 'http://uno.com/' ] ]
37 | when:
38 | mapper.map( resource, config )
39 | then:
40 | resource.linkOverride == 'http://uno.com/images.jpg'
41 | }
42 |
43 | def "a resource with no modules default to base url"(){
44 | setup:
45 | def resource = [ linkUrl : '/images.jpg', module: [ name: 'uno'] ]
46 | def config = [ enabled: true, default:'http://www.google.com/', modules : [ dos: 'http://dos.com/' ] ]
47 | when:
48 | mapper.map( resource, config )
49 | then:
50 | resource.linkOverride == 'http://www.google.com/images.jpg'
51 | }
52 |
53 | //GPRESOURCES-184
54 | def "mapper uses delegate-resource's name for aggreagated resources"() {
55 | setup:
56 | def resourceBundle = [getLinkUrl: { '/bundle.js' }] as AggregatedResourceMeta
57 | resourceBundle.resources = [bundledResource('uno')]
58 | def config = [ enabled: true, default:'http://www.google.com/', modules : [ uno: 'http://uno.com/' ] ]
59 |
60 | when:
61 | mapper.map(resourceBundle, config)
62 |
63 | then:
64 | resourceBundle.linkOverride == 'http://uno.com/bundle.js'
65 | }
66 |
67 | //GPRESOURCES-184
68 | def "mapper throws an exception when configured to map modules bundled together to different urls"() {
69 | setup:
70 | def resourceBundle = [getLinkUrl: { '/bundle.js' }] as AggregatedResourceMeta
71 | resourceBundle.resources = [bundledResource('uno'), bundledResource('dos')]
72 | def config = [ enabled: true, default:'http://www.google.com/', modules : [ uno: 'http://uno.com/' ] ]
73 |
74 | when:
75 | mapper.map(resourceBundle, config)
76 |
77 | then:
78 | def exception = thrown(IllegalArgumentException)
79 | exception.message.contains('All modules bundled together must have the same baseUrl override')
80 | exception.message.contains(resourceBundle.resources.first().bundle)
81 | }
82 |
83 | //GPRESOURCES-16
84 | def "mapper uses rotating baseUrl but only when config.default configured as a list"() {
85 | setup:
86 | def resource = [ linkUrl : '/images.jpg' ]
87 | def config = [ enabled: true, default: 'http://www.google.com/' ]
88 |
89 | when:
90 | mapper.map( resource, config )
91 |
92 | then:
93 | resource.linkOverride == 'http://www.google.com/images.jpg'
94 | }
95 |
96 | def "mapper uses rotating baseUrl but only when config.default configured as a non-empty list"() {
97 | setup:
98 | def resource = [ linkUrl : '/images.jpg' ]
99 | def config = [ enabled: true, default: [] ]
100 |
101 | when:
102 | mapper.map( resource, config )
103 |
104 | then:
105 | resource.linkOverride == null
106 | }
107 |
108 | def "mapper uses rotating baseUrl when baseUrl configured as a list"() {
109 | setup:
110 | def resourceOne = [ linkUrl : '/images.jpg' ]
111 | def resourceTwo = [ linkUrl : '/images.png' ]
112 | def resourceThree = [ linkUrl : '/images.gif' ]
113 | def config = [ enabled: true, default: ['http://cdn1.google.com/', 'http://cdn2.google.com/', 'http://cdn3.google.com/']]
114 |
115 | when:
116 | mapper.map( resourceOne, config )
117 | mapper.map( resourceTwo, config )
118 | mapper.map( resourceThree, config )
119 |
120 | then:
121 | resourceOne.linkOverride == 'http://cdn3.google.com/images.jpg'
122 | resourceTwo.linkOverride == 'http://cdn3.google.com/images.png'
123 | resourceThree.linkOverride == 'http://cdn2.google.com/images.gif'
124 | }
125 | //GPRESOURCES-16
126 |
127 | private ResourceMeta bundledResource(String moduleName) {
128 | def module = [name: moduleName] as ResourceModule
129 | [module: module, bundle: 'bundle_head'] as ResourceMeta
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/BundleResourceMapperTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin;
5 |
6 | public @TestMixin(GrailsUnitTestMixin)
7 | class BundleResourceMapperTests {
8 |
9 | BundleResourceMapper mapper = new BundleResourceMapper()
10 |
11 | void testRecognisedArtifactsAreBundledIfRequested() {
12 |
13 | Map expectedBundling = [
14 | 'text/css': true,
15 | 'text/javascript': true,
16 | 'application/javascript': true,
17 | 'application/x-javascript': true,
18 | 'everything/nothing': false
19 | ]
20 |
21 | List resultingBundle
22 | Map grailsResourceProcessor = [
23 | findSyntheticResourceById: { String bundleId -> return resultingBundle },
24 | ]
25 |
26 | mapper.grailsResourceProcessor = grailsResourceProcessor
27 |
28 | expectedBundling.each { String resourceType, Boolean shouldBundle ->
29 | resultingBundle = [[existing:'bundle']]
30 | Map resource = [identifier:UUID.randomUUID(), contentType: resourceType, bundle:'myBundle', sourceUrlExtension:'js']
31 |
32 | mapper.map(resource, null)
33 |
34 | assert (resultingBundle[1]?.identifier == resource.identifier) == shouldBundle
35 | }
36 |
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/CSSLinkProcessorTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 | import grails.test.mixin.TestMixin
3 | import grails.test.mixin.support.GrailsUnitTestMixin
4 | import org.junit.Rule
5 | import org.junit.rules.TemporaryFolder
6 |
7 | @TestMixin(GrailsUnitTestMixin)
8 | class CSSLinkProcessorTests {
9 | @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder()
10 | File temporarySubfolder
11 | def mockResSvc
12 |
13 | @org.junit.Before
14 | void setupTest() {
15 | temporarySubfolder = temporaryFolder.newFolder('test-tmp')
16 | mockResSvc = [
17 | config : [ rewrite: [css: true] ]
18 | ]
19 | }
20 |
21 | protected ResourceMeta makeRes(String reluri, String contents) {
22 | def r = new ResourceMeta(sourceUrl:'/'+reluri)
23 | r.workDir = temporarySubfolder
24 | r.actualUrl = r.sourceUrl
25 | r.contentType = "text/css"
26 | r.processedFile = new File(temporarySubfolder, reluri)
27 | r.processedFile.parentFile.mkdirs()
28 | r.processedFile.delete()
29 |
30 | r.processedFile << new ByteArrayInputStream(contents.bytes)
31 | return r
32 | }
33 |
34 | /**
35 | * This simulates a test where the image resources are moved to a new flat dir
36 | * but the CSS is *not* moved, to force recalculation of paths
37 | */
38 | void testCSSPreprocessing() {
39 |
40 | def res = makeRes('css/urltests1.css', """
41 | @import '/css/style1.css';
42 | @import "/css/style2.css";
43 | @import '/css/style3.css' screen;
44 | @import "/css/style4.css" screen, print;
45 | @import url(/css/style5.css);
46 | @import url('/css/style6.css');
47 | @import url("/css/style7.css");
48 | @import url( '/css/style8.css' );
49 | @import url( "/css/style9.css" );
50 | @import url("/css/style10.css") screen ;
51 | @import url( '/css/style11.css') print, screen;
52 | .bg1 { background: url(/images/theme/bg1.png) }
53 | .bg2 { background: url("/css/images/bg2.png") }
54 | .bg3 { background: url( /images/bg3.png ) }
55 | .bg4 { background: url( '/css/bg4.png' ) }
56 | .bg5 { background: url(http://google.com/images/bg5.png) }
57 | .bg6 { background: url(https://google.com/images/bg5.png) }
58 | .bg7 { background: url(####BULL) }
59 | .bg8 { background: url(data:font/opentype;base64,ABCDEF123456789ABCDEF123456789) }
60 | .bg9 { background: url(//mydomain.com/protocol-relative-url) }
61 | """)
62 | def expectedLinks = [
63 | '/css/style1.css',
64 | '/css/style2.css',
65 | '/css/style3.css',
66 | '/css/style4.css',
67 | '/css/style5.css',
68 | '/css/style6.css',
69 | '/css/style7.css',
70 | '/css/style8.css',
71 | '/css/style9.css',
72 | '/css/style10.css',
73 | '/css/style11.css',
74 | '/images/theme/bg1.png',
75 | '/css/images/bg2.png',
76 | '/images/bg3.png',
77 | '/css/bg4.png',
78 | 'http://google.com/images/bg5.png',
79 | 'https://google.com/images/bg5.png',
80 | '####BULL',
81 | 'data:font/opentype;base64,ABCDEF123456789ABCDEF123456789',
82 | '//mydomain.com/protocol-relative-url'
83 | ]
84 | def cursor = 0
85 |
86 | def processor = new CSSLinkProcessor()
87 | processor.process(res, mockResSvc) { prefix, original, suffix ->
88 | assertEquals expectedLinks[cursor++], original
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/PathMatcherTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import org.springframework.util.AntPathMatcher
4 | import grails.test.mixin.TestMixin
5 | import grails.test.mixin.support.GrailsUnitTestMixin
6 |
7 | @TestMixin(GrailsUnitTestMixin)
8 | class PathMatcherTests {
9 | static final PATH_MATCHER = new AntPathMatcher()
10 |
11 | void testDeepMatching() {
12 | assertTrue PATH_MATCHER.match('**/.svn', 'web-app/images/.svn')
13 | assertFalse PATH_MATCHER.match('**/.svn', 'web-app/images/.svn/test.jpg')
14 | assertTrue PATH_MATCHER.match('**/.svn/**/*.jpg', 'web-app/images/.svn/test.jpg')
15 | assertTrue PATH_MATCHER.match('**/.svn/**/*.jpg', 'web-app/images/.svn/images/logos/test.jpg')
16 | assertFalse PATH_MATCHER.match('**/.svn/**/*.jpg', 'web-app/images/.svn/images/logos/test.png')
17 | assertTrue PATH_MATCHER.match('**/.svn/**/*.*', 'web-app/images/.svn/images/logos/test.png')
18 | assertTrue PATH_MATCHER.match('**/.svn/**/*.*', 'web-app/images/.svn/css/test.css')
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ProcessingFilterTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.*
4 | import javax.servlet.FilterChain
5 |
6 | import org.springframework.mock.web.MockHttpServletResponse
7 | import org.springframework.mock.web.MockHttpServletRequest
8 |
9 | class ProcessingFilterTests {
10 | @org.junit.Test
11 | void testResourceIsNotProcessedByBothFiltersIfHandledByFirst() {
12 | def filter = new ProcessingFilter()
13 | filter.adhoc = false
14 | filter.grailsResourceProcessor = [
15 | isDebugMode: { req -> false },
16 | processModernResource: { req, resp -> resp.committed = true }
17 | ]
18 |
19 | def rq = new MockHttpServletRequest()
20 | def rp = new MockHttpServletResponse()
21 |
22 | def fakeChain = [
23 | doFilter: { req, resp -> fail('Second filter instance was called') }
24 | ] as FilterChain
25 |
26 | filter.doFilter(rq, rp, fakeChain)
27 | }
28 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ResourceMapperTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 | import org.grails.plugin.resource.mapper.ResourceMapper
7 |
8 | @TestMixin(GrailsUnitTestMixin)
9 | class ResourceMapperTests {
10 | void testDefaultIncludesExcludes() {
11 | def artefact = new DummyMapper()
12 | artefact.defaultExcludes = ['**/*.jpg', '**/*.png']
13 | artefact.defaultIncludes = ['**/*.*']
14 | artefact.name = 'foobar'
15 | artefact.map = { res, config ->
16 | }
17 |
18 | def m = new ResourceMapper(artefact, [foobar:[:]])
19 |
20 | def testMeta = new ResourceMeta()
21 | testMeta.sourceUrl = '/images/test.png'
22 | testMeta.actualUrl = '/images/test.png'
23 | testMeta.contentType = "image/png"
24 |
25 | assertFalse m.invokeIfNotExcluded(testMeta)
26 |
27 | def testMetaB = new ResourceMeta()
28 | testMetaB.sourceUrl = '/images/test.jpg'
29 | testMetaB.actualUrl = '/images/test.jpg'
30 | testMetaB.contentType = "image/jpeg"
31 |
32 | assertFalse m.invokeIfNotExcluded(testMetaB)
33 |
34 | def testMeta2 = new ResourceMeta()
35 | testMeta2.sourceUrl = '/images/test.zip'
36 | testMeta2.actualUrl = '/images/test.zip'
37 | testMeta2.contentType = "application/zip"
38 |
39 | assertTrue m.invokeIfNotExcluded(testMeta2)
40 |
41 | }
42 |
43 | void testResourceExclusionOfMapper() {
44 | def artefact = new DummyMapper()
45 | artefact.defaultIncludes = ['**/*.*']
46 | artefact.name = 'minify'
47 | artefact.map = { res, config ->
48 | }
49 |
50 | def m = new ResourceMapper(artefact, [minify:[:]])
51 |
52 | def artefact2 = new DummyMapper()
53 | artefact2.defaultIncludes = ['**/*.*']
54 | artefact2.name = 'other'
55 | artefact2.map = { res, config ->
56 | }
57 |
58 | def m2 = new ResourceMapper(artefact2, [other:[:]])
59 |
60 | def testMeta = new ResourceMeta()
61 | testMeta.sourceUrl = '/images/test.png'
62 | testMeta.actualUrl = '/images/test.png'
63 | testMeta.contentType = "image/png"
64 | testMeta.excludedMappers = ['minify'] as Set
65 |
66 | assertFalse m.invokeIfNotExcluded(testMeta)
67 | assertTrue m2.invokeIfNotExcluded(testMeta)
68 | }
69 |
70 | void testResourceExclusionOfOperation() {
71 | def artefact = new DummyMapper()
72 | artefact.defaultIncludes = ['**/*.*']
73 | artefact.name = 'yuicssminifier'
74 | artefact.operation = 'minify'
75 | artefact.map = { res, config ->
76 | }
77 |
78 | def m = new ResourceMapper(artefact, [minify:[:]])
79 |
80 | def artefact2 = new DummyMapper()
81 | artefact2.defaultIncludes = ['**/*.*']
82 | artefact2.name = 'googlecssminifier'
83 | artefact2.operation = 'minify'
84 | artefact2.map = { res, config ->
85 | }
86 |
87 | def m2 = new ResourceMapper(artefact2, [other:[:]])
88 |
89 | def testMeta = new ResourceMeta()
90 | testMeta.sourceUrl = '/images/test.css'
91 | testMeta.actualUrl = '/images/test.css'
92 | testMeta.contentType = "text/css"
93 | testMeta.excludedMappers = ['minify'] as Set
94 |
95 | assertFalse m.invokeIfNotExcluded(testMeta)
96 | assertFalse m2.invokeIfNotExcluded(testMeta)
97 |
98 | testMeta.excludedMappers = null
99 | assertTrue m.invokeIfNotExcluded(testMeta)
100 | assertTrue m2.invokeIfNotExcluded(testMeta)
101 | }
102 | }
103 |
104 | class DummyMapper {
105 | def defaultExcludes
106 | def defaultIncludes
107 | def name
108 | def map
109 | def operation
110 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ResourceMetaStoreTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 | import org.grails.plugin.resource.util.ResourceMetaStore
7 |
8 | @TestMixin(GrailsUnitTestMixin)
9 | class ResourceMetaStoreTests {
10 | void testAddingDeclaredResourceAddsBothProcessedAndSourceUrls() {
11 | def r = new ResourceMeta()
12 | r.sourceUrl = "/jquery/images/bg.png"
13 | r.workDir = new File('/tmp/test')
14 | r.processedFile = new File('/tmp/test/123456789.png')
15 | r.updateActualUrlFromProcessedFile()
16 |
17 | def store = new ResourceMetaStore()
18 | store.addDeclaredResource( {
19 | r.actualUrl = "/jquery/images/_bg.png"
20 | return r
21 | } )
22 |
23 | assertEquals ResourceMetaStore.CLOSED_LATCH, store.latches["/jquery/images/bg.png"]
24 | assertEquals ResourceMetaStore.CLOSED_LATCH, store.latches["/jquery/images/_bg.png"]
25 | }
26 |
27 | void testRequestingResourceThatDoesNotExist() {
28 | def store = new ResourceMetaStore()
29 | def resURI = '/images/idonotexist.jpg'
30 | def res = store.getOrCreateAdHocResource(resURI, { throw new FileNotFoundException('Where my file?') } )
31 | assertNull "Resource should not have existed", res
32 |
33 | assertNull store.latches[resURI]
34 | }
35 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ResourceMetaTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 | @TestMixin(GrailsUnitTestMixin)
7 | class ResourceMetaTests {
8 | void testMovingFileUpdatesActualUrlCorrectly() {
9 | def r = new ResourceMeta()
10 | r.sourceUrl = "/jquery/images/bg.png"
11 | r.workDir = new File('/tmp/test')
12 | r.processedFile = new File('/tmp/test/123456789.png')
13 | r.updateActualUrlFromProcessedFile()
14 |
15 | assertEquals "/jquery/images/bg.png", r.sourceUrl
16 | assertEquals "/123456789.png", r.actualUrl
17 | assertEquals "/123456789.png", r.linkUrl
18 | }
19 |
20 | void testRenamingFileUpdatesActualUrlCorrectly() {
21 | def r = new ResourceMeta()
22 | r.sourceUrl = "/jquery/images/bg.png"
23 | r.workDir = new File('/tmp/test')
24 | r.processedFile = new File('/tmp/test/jquery/images/bg.png.gz')
25 | r.updateActualUrlFromProcessedFile()
26 |
27 | // All results must be abs to the work dir, with leading /
28 | assertEquals "/jquery/images/bg.png", r.sourceUrl
29 | assertEquals "/jquery/images/bg.png.gz", r.actualUrl
30 | assertEquals "/jquery/images/bg.png.gz", r.linkUrl
31 | }
32 |
33 | void testCSSURLWithHackyMozillaAnchorCrapStripsAnchor() {
34 | def r = new ResourceMeta()
35 | r.workDir = new File('/tmp/test')
36 | r.sourceUrl = "/jquery/images/bg.png#crackaddicts"
37 | r.processedFile = new File('/tmp/test/jquery/images/bg.png')
38 | r.updateActualUrlFromProcessedFile()
39 |
40 | // All results must be abs to the work dir, with leading /
41 | assertEquals "Source url should have anchor stripped from it", "/jquery/images/bg.png", r.sourceUrl
42 | assertEquals "/jquery/images/bg.png", r.actualUrl
43 | assertEquals "#crackaddicts", r.sourceUrlParamsAndFragment
44 | assertEquals "/jquery/images/bg.png#crackaddicts", r.linkUrl
45 | }
46 |
47 | void testCSSURLWithAnchorAndQueryParamsMaintained() {
48 | def r = new ResourceMeta()
49 | r.workDir = new File('/tmp/test')
50 | r.sourceUrl = "/jquery/images/bg.png?you=got&to=be&kidding=true#crackaddicts"
51 | r.processedFile = new File('/tmp/test/jquery/images/bg.png')
52 | r.updateActualUrlFromProcessedFile()
53 |
54 | // All results must be abs to the work dir, with leading /
55 | assertEquals "Source url should have anchor stripped from it", "/jquery/images/bg.png", r.sourceUrl
56 | assertEquals "/jquery/images/bg.png", r.actualUrl
57 | assertEquals "?you=got&to=be&kidding=true#crackaddicts", r.sourceUrlParamsAndFragment
58 | assertEquals "/jquery/images/bg.png?you=got&to=be&kidding=true#crackaddicts", r.linkUrl
59 | }
60 |
61 | void testAbsoluteURLWithAnchorAndQueryParamsMaintained() {
62 | def r = new ResourceMeta()
63 | r.workDir = new File('/tmp/test')
64 | r.sourceUrl = "http://crackhouse.ck/jquery/images/bg.png?you=got&to=be&kidding=true#crackaddicts"
65 | r.updateActualUrlFromProcessedFile()
66 |
67 | // All results must be abs to the work dir, with leading /
68 | assertEquals "Source url should have anchor stripped from it", "http://crackhouse.ck/jquery/images/bg.png", r.sourceUrl
69 | assertEquals "http://crackhouse.ck/jquery/images/bg.png", r.actualUrl
70 | assertEquals "?you=got&to=be&kidding=true#crackaddicts", r.sourceUrlParamsAndFragment
71 | assertEquals "http://crackhouse.ck/jquery/images/bg.png?you=got&to=be&kidding=true#crackaddicts", r.linkUrl
72 | }
73 |
74 | void testRelativePathCalculations() {
75 | def data = [
76 | // Expected, base, target
77 | ["../images/logo.png", '/css/main.css', '/images/logo.png'],
78 | ["../logo.png", '/css/main.css', '/logo.png'],
79 | [ 'images/ui-bg_fine-grain_10_eceadf_60x60.png', '/css/xx/jquery-ui-1.8.16.custom.css', "/css/xx/images/ui-bg_fine-grain_10_eceadf_60x60.png"],
80 | ["_yyyyyy.png", '/_xxxxxx.css', '/_yyyyyy.png'],
81 | ["notgonnahappen/_yyyyyy.png", '/_xxxxxx.css', '/notgonnahappen/_yyyyyy.png'],
82 | ["../notgonnahappen/really/_yyyyyy.png", '/css/_xxxxxx.css', '/notgonnahappen/really/_yyyyyy.png'],
83 | ["../../notgonnahappen/really/_yyyyyy.png", '/css/deep/_xxxxxx.css', '/notgonnahappen/really/_yyyyyy.png'],
84 | ["../../_yyyyyy.png", '/css/deep/_xxxxxx.css', '/_yyyyyy.png'],
85 | ["_xxxx.png", '/css/_zzzzz.css', '/css/_xxxx.png'],
86 | ["../css/_xxxx.png", '/css2/_zzzzz.css', '/css/_xxxx.png'],
87 | ["../css2/_xxxx.png", '/css/_zzzzz.css', '/css2/_xxxx.png']
88 | ]
89 |
90 | data.each { d ->
91 | println "Trying: ${d}"
92 | def r = new ResourceMeta(actualUrl:d[2])
93 | assertEquals d[0], r.relativeTo(new ResourceMeta(actualUrl:d[1]) )
94 | }
95 | }
96 | }
97 |
98 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ResourceModuleTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 | import org.grails.plugin.resource.module.*
7 |
8 | @TestMixin(GrailsUnitTestMixin)
9 | class ResourceModuleTests {
10 | def svc
11 |
12 | @org.junit.Before
13 | void setupTest() {
14 | svc = new Expando()
15 | svc.getDefaultSettingsForURI = { uri, type ->
16 | [:]
17 | }
18 | }
19 |
20 | void testDefaultBundleFalse() {
21 | def resources = [
22 | [url:'simile/simile.css'],
23 | [url:'simile/simile.js']
24 | ]
25 |
26 | def m = new ResourceModule('testModule', resources, false, svc)
27 |
28 | assertEquals 2, m.resources.size()
29 | assertTrue m.resources.every { it.bundle == null }
30 | }
31 |
32 | void testDefaultBundling() {
33 | def resources = [
34 | [url:'simile/simile.css', disposition:'head'],
35 | [url:'simile/simile.js', disposition:'head']
36 | ]
37 |
38 | def m = new ResourceModule('testModule', resources, null, svc)
39 |
40 | assertEquals 2, m.resources.size()
41 | m.resources.each { r ->
42 | assertEquals 'bundle_testModule_head', r.bundle
43 | }
44 | }
45 |
46 | void testDefaultBundleWithName() {
47 | def resources = [
48 | [url:'simile/simile.css', disposition:'defer'],
49 | [url:'simile/simile.js', disposition:'defer']
50 | ]
51 |
52 | def m = new ResourceModule('testModule', resources, "frank-and-beans", svc)
53 |
54 | assertEquals 2, m.resources.size()
55 | m.resources.each { r ->
56 | assertEquals 'frank-and-beans_defer', r.bundle
57 | }
58 | }
59 |
60 | void testExcludedMapperString() {
61 | def resources = [
62 | [url:'simile/simile.js', disposition:'head', exclude:'minify']
63 | ]
64 |
65 | def m = new ResourceModule('testModule', resources, null, svc)
66 |
67 | assertEquals 1, m.resources.size()
68 | assertTrue m.resources[0].excludedMappers.contains('minify')
69 | }
70 |
71 | void testExcludedMapperSet() {
72 | def resources = [
73 | [url:'simile/simile.js', disposition:'head', exclude:['minify']]
74 | ]
75 |
76 | def m = new ResourceModule('testModule', resources, null, svc)
77 |
78 | assertEquals 1, m.resources.size()
79 | assertTrue m.resources[0].excludedMappers.contains('minify')
80 | }
81 |
82 | void testStringOnlyResource() {
83 | def resources = [
84 | 'js/test.js'
85 | ]
86 |
87 | def m = new ResourceModule('testModule', resources, null, svc)
88 |
89 | assertEquals 1, m.resources.size()
90 | assertEquals "/js/test.js", m.resources[0].sourceUrl
91 | }
92 |
93 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ResourceModulesBuilderTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 | import org.grails.plugin.resource.module.*
7 |
8 | @TestMixin(GrailsUnitTestMixin)
9 | class ResourceModulesBuilderTests {
10 | def svc
11 |
12 | @org.junit.Before
13 | void setupTest() {
14 |
15 | svc = new Expando()
16 | svc.getDefaultSettingsForURI = { uri, type ->
17 | [:]
18 | }
19 | }
20 |
21 | void testModuleOverrides() {
22 | def modules = []
23 | def bld = new ModulesBuilder(modules)
24 |
25 | bld.'jquery' {
26 | }
27 |
28 | bld.'horn-smutils' {
29 | dependsOn(['jquery'])
30 | }
31 |
32 | bld.horn {
33 | defaultBundle false
34 | dependsOn(['horn-smutils', 'jquery'])
35 | }
36 |
37 | // knock out the smutils dep and replace
38 | bld.'smutils' {
39 | dependsOn(['jquery'])
40 | }
41 |
42 | bld.overrides {
43 | horn {
44 | defaultBundle true
45 | dependsOn(['smutils', 'jquery'])
46 | }
47 | }
48 |
49 | assert 4 == modules.size()
50 | assert 1 == bld._moduleOverrides.size()
51 | assert 'horn' == bld._moduleOverrides[0].name
52 | assert true == bld._moduleOverrides[0].defaultBundle
53 | assert ['smutils', 'jquery'] == bld._moduleOverrides[0].dependencies
54 | }
55 |
56 | void testModuleDependsOnSyntaxes() {
57 | def modules = []
58 | def bld = new ModulesBuilder(modules)
59 |
60 | bld.'moduleA' {
61 | dependsOn(['jquery', 'jquery-ui'])
62 | }
63 |
64 | bld.'moduleB' {
65 | dependsOn 'jquery, jquery-ui'
66 | }
67 |
68 | bld.'moduleC' {
69 | dependsOn(['jquery', 'jquery-ui'] as String[])
70 | }
71 |
72 | shouldFail {
73 | bld.'moduleD' {
74 | // This is bad groovy syntaxt, parens are needed, translates to getProperty('dependsOn')
75 | dependsOn ['jquery', 'jquery-ui']
76 | }
77 | }
78 |
79 | assert 3 == modules.size()
80 | assert ['jquery', 'jquery-ui'] == modules.find({it.name == 'moduleA'}).dependencies
81 | assert ['jquery', 'jquery-ui'] == modules.find({it.name == 'moduleB'}).dependencies
82 | assert ['jquery', 'jquery-ui'] == modules.find({it.name == 'moduleC'}).dependencies
83 | }
84 |
85 | void testDefaultBundleFalse() {
86 | def modules = []
87 | def bld = new ModulesBuilder(modules)
88 |
89 | bld.testModule {
90 | defaultBundle false
91 | resource url:'simile/simile.css'
92 | resource url:'simile/simile.js'
93 | }
94 |
95 | assertEquals 1, modules.size()
96 | assertEquals 'testModule', modules[0].name
97 | assertEquals false, modules[0].defaultBundle
98 | }
99 |
100 | void testDefaultBundling() {
101 | def modules = []
102 | def bld = new ModulesBuilder(modules)
103 |
104 | bld.testModule {
105 | resource url:'simile/simile.css'
106 | resource url:'simile/simile.js'
107 | }
108 |
109 | assertEquals 1, modules.size()
110 | assertEquals 'testModule', modules[0].name
111 | assertNull modules[0].defaultBundle
112 | }
113 |
114 | void testDefaultBundleWithName() {
115 | def modules = []
116 | def bld = new ModulesBuilder(modules)
117 |
118 | bld.testModule {
119 | defaultBundle "frank-and-beans"
120 | resource url:'simile/simile.css'
121 | resource url:'simile/simile.js'
122 | }
123 |
124 | assertEquals 1, modules.size()
125 | assertEquals 'testModule', modules[0].name
126 | assertEquals 'frank-and-beans', modules[0].defaultBundle
127 | }
128 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/ResourceProcessorSpec.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import org.junit.Rule
4 | import org.junit.rules.TemporaryFolder
5 | import org.springframework.mock.web.MockHttpServletRequest
6 | import org.springframework.mock.web.MockHttpServletResponse
7 | import org.springframework.mock.web.MockServletContext;
8 |
9 | import spock.lang.Specification
10 | import spock.lang.Unroll
11 |
12 | class ResourceProcessorSpec extends Specification {
13 | @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder()
14 | ResourceProcessor resourceProcessor
15 | MockHttpServletRequest request
16 | MockHttpServletResponse response
17 | ConfigObject config
18 |
19 | def setup() {
20 | resourceProcessor = new ResourceProcessor()
21 | resourceProcessor.staticUrlPrefix = 'static'
22 | config = new ConfigObject()
23 | File temporarySubfolder = temporaryFolder.newFolder('test-tmp')
24 | config.grails.resources.work.dir = temporarySubfolder.getAbsolutePath()
25 | def servletContext = new MockServletContext()
26 | resourceProcessor.grailsApplication = [
27 | config : config,
28 | mainContext : [servletContext: servletContext]
29 | ]
30 | resourceProcessor.servletContext = servletContext
31 | resourceProcessor.afterPropertiesSet()
32 | request = new MockHttpServletRequest(servletContext)
33 | request.contextPath = '/'
34 | response = new MockHttpServletResponse()
35 | }
36 |
37 | @Unroll
38 | def "check default adhoc.includes/excludes settings - access to url #url should be #accepted"() {
39 | given:
40 | request.requestURI = url
41 | when: 'default adhoc.includes and adhoc.excludes are used'
42 | def uri = null
43 | def exception=null
44 | def result=false
45 | try {
46 | uri=resourceProcessor.removeQueryParams(resourceProcessor.extractURI(request, false))
47 | result=resourceProcessor.canProcessLegacyResource(uri)
48 | } catch (Exception e) {
49 | result=false
50 | exception=e
51 | }
52 | then:
53 | result == accepted
54 | when: 'adhoc.includes setting is accepting all resources'
55 | resourceProcessor.adHocIncludes = ['/**/*']
56 | try {
57 | result=resourceProcessor.canProcessLegacyResource(uri)
58 | } catch (Exception e) {
59 | result=false
60 | exception=e
61 | }
62 | then:
63 | result == accepted
64 | where:
65 | url | accepted
66 | '/static/images/image.png' | true
67 | '/static/js/file.js' | true
68 | '/static/WEB-INF/web.xml' | false
69 | '/static/wEb-iNf/web.xml' | false
70 | '/static/web-inf/web.xml' | false
71 | '/static/META-INF/MANIFEST.MF' | false
72 | '/static/meta-inf/MANIFEST.MF' | false
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/URLUtilsSpec.groovy:
--------------------------------------------------------------------------------
1 |
2 | package org.grails.plugin.resource
3 |
4 | import spock.lang.Specification
5 | import spock.lang.Unroll;
6 | import static URLUtils.normalizeUri
7 |
8 | class URLUtilsSpec extends Specification {
9 | def './ should get normalized'() {
10 | expect:
11 | normalizeUri('/parentdir/./some-dir/file.xml') == '/parentdir/some-dir/file.xml'
12 | }
13 |
14 | def '../ should get normalized'() {
15 | expect:
16 | normalizeUri('/parentdir/something/../some-dir/file.xml') == '/parentdir/some-dir/file.xml'
17 | }
18 |
19 | def 'fail if ../ goes beyond root'() {
20 | when:
21 | normalizeUri('../../test')
22 | then:
23 | thrown IllegalArgumentException
24 | }
25 |
26 | def 'allow spaces in path'() {
27 | expect:
28 | normalizeUri('/parentdir/a b c.xml') == '/parentdir/a b c.xml'
29 | normalizeUri('/parentdir/a%20b%20c.xml') == '/parentdir/a b c.xml'
30 | }
31 |
32 | def 'fail if contains .. path traversal after decoding'() {
33 | when:
34 | normalizeUri('/some/path/%2e%2e/some-dir/file.xml')
35 | then:
36 | thrown IllegalArgumentException
37 | }
38 |
39 | def 'fail if contains backslash after decoding'() {
40 | when:
41 | normalizeUri('/some/path/%2e%2e%5c%2e%2e/some-dir/file.xml')
42 | then:
43 | thrown IllegalArgumentException
44 | }
45 |
46 | def 'fail if contains . path traversal after decoding'() {
47 | when:
48 | normalizeUri('/some/path/%2e/some-dir/file.xml')
49 | then:
50 | thrown IllegalArgumentException
51 | }
52 |
53 | @Unroll
54 | def 'fail if contains double encoded path traversal going beyond root - #uri'() {
55 | when:
56 | normalizeUri(uri)
57 | then:
58 | thrown IllegalArgumentException
59 | where:
60 | uri|_
61 | '/static/css/..%252f..%252f..%252fsecrets.txt'|_
62 | '/static/css/some..%252fa..%252fb..%252fsecrets.txt'|_
63 | '/static/css/..a%252f..b%252f..c%252fsecrets.txt'|_
64 | }
65 |
66 | def 'double url encoded should get normalized'() {
67 | expect:
68 | normalizeUri('/parentdir/%25%37%33%25%36%66%25%36%64%25%36%35%25%32%64%25%36%34%25%36%39%25%37%32/file.xml') == '/parentdir/some-dir/file.xml'
69 | }
70 |
71 | def 'triple url encoded should get normalized'() {
72 | expect:
73 | normalizeUri('/parentdir/%25%32%35%25%33%37%25%33%33%25%32%35%25%33%36%25%36%36%25%32%35%25%33%36%25%36%34%25%32%35%25%33%36%25%33%35%25%32%35%25%33%32%25%36%34%25%32%35%25%33%36%25%33%34%25%32%35%25%33%36%25%33%39%25%32%35%25%33%37%25%33%32/file.xml') == '/parentdir/some-dir/file.xml'
74 | }
75 |
76 | def 'fail if normalization limit exceeds'() {
77 | when:
78 | def uri=normalizeUri('/parentdir/%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%37%25%32%35%25%33%33%25%33%33%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%36%25%32%35%25%33%36%25%33%36%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%36%25%32%35%25%33%36%25%33%34%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%36%25%32%35%25%33%33%25%33%35%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%32%25%32%35%25%33%36%25%33%34%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%36%25%32%35%25%33%33%25%33%34%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%36%25%32%35%25%33%33%25%33%39%25%32%35%25%33%32%25%33%35%25%32%35%25%33%33%25%33%37%25%32%35%25%33%33%25%33%32/file.xml')
79 | then:
80 | thrown IllegalArgumentException
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/URLUtilsTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 | @TestMixin(GrailsUnitTestMixin)
7 | class URLUtilsTests {
8 |
9 | void testRelativeCSSUris() {
10 | assertEquals "images/bg_fade.png", URLUtils.relativeURI('css/main.css', '../images/bg_fade.png')
11 | assertEquals "/images/bg_fade.png", URLUtils.relativeURI('/css/main.css', '../images/bg_fade.png')
12 | assertEquals "/css/images/bg_fade.png", URLUtils.relativeURI('/css/main.css', './images/bg_fade.png')
13 | assertEquals "css/images/bg_fade.png", URLUtils.relativeURI('css/main.css', './images/bg_fade.png')
14 | assertEquals "bg_fade.png", URLUtils.relativeURI('main.css', 'bg_fade.png')
15 | assertEquals "/bg_fade.png", URLUtils.relativeURI('/main.css', 'bg_fade.png')
16 | assertEquals "css/bg_fade.png", URLUtils.relativeURI('css/main.css', 'bg_fade.png')
17 | assertEquals "/css/bg_fade.png", URLUtils.relativeURI('/css/main.css', 'bg_fade.png')
18 | assertEquals "/bg_fade.png", URLUtils.relativeURI('/main.css', '/bg_fade.png')
19 | assertEquals "/bg_fade.png", URLUtils.relativeURI('css/main.css', '/bg_fade.png')
20 | assertEquals "/bg_fade.png", URLUtils.relativeURI('/css/main.css', '/bg_fade.png')
21 | assertEquals "http://somewhere.com/images/x.png", URLUtils.relativeURI('css/main.css', 'http://somewhere.com/images/x.png')
22 | }
23 |
24 | void testIsRelativeForServerRelativeUrls() {
25 | assertTrue URLUtils.isRelativeURL("/server/relative")
26 | }
27 |
28 | void testIsRelativeForRelativeToCurrentPath() {
29 | assertTrue URLUtils.isRelativeURL("relative/to/current/path")
30 | }
31 |
32 | void testIsRelativeForRelativeToCurrentPathViaParent() {
33 | assertTrue URLUtils.isRelativeURL("../relative/to/current/path")
34 | }
35 |
36 | void testIsRelativeForDataUrls() {
37 | assertFalse URLUtils.isRelativeURL("data:xyz")
38 | }
39 |
40 | void testIsRelativeForPageFragments() {
41 | assertFalse URLUtils.isRelativeURL("#fragment_only")
42 | }
43 |
44 | void testIsRelativeForAbsoluteUrls() {
45 | assertFalse URLUtils.isRelativeURL("http://www.example.org/absolute/path")
46 | }
47 |
48 | void testIsExternalUrl() {
49 | assertTrue URLUtils.isExternalURL('http://images.examples.com')
50 | assertTrue URLUtils.isExternalURL('https://images.examples.com')
51 | assertTrue URLUtils.isExternalURL('//images.examples.com')
52 |
53 | assertFalse URLUtils.isExternalURL('/images/exapmles.com')
54 | assertFalse URLUtils.isExternalURL('://images.examples.com')
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resource/util/DispositionsUtilsTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resource.util
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 |
6 |
7 | @TestMixin(GrailsUnitTestMixin)
8 | class DispositionsUtilsTests {
9 |
10 | void testAddingDispositionToRequest() {
11 | def request = [:]
12 | assertTrue DispositionsUtils.getRequestDispositionsRemaining(request).empty
13 |
14 | DispositionsUtils.addDispositionToRequest(request, 'head', 'dummy')
15 | assertTrue((['head'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
16 |
17 | // Let's just make sure its a set
18 | DispositionsUtils.addDispositionToRequest(request, 'head', 'dummy')
19 | assertTrue((['head'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
20 |
21 | DispositionsUtils.addDispositionToRequest(request, 'defer', 'dummy')
22 | assertTrue((['head', 'defer'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
23 |
24 | DispositionsUtils.addDispositionToRequest(request, 'image', 'dummy')
25 | assertTrue((['head', 'image', 'defer'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
26 | }
27 |
28 | void testRemovingDispositionFromRequest() {
29 | def request = [(DispositionsUtils.REQ_ATTR_DISPOSITIONS_REMAINING):(['head', 'image', 'defer'] as Set)]
30 |
31 | assertTrue((['head', 'image', 'defer'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
32 |
33 | DispositionsUtils.removeDispositionFromRequest(request, 'head')
34 | assertTrue((['defer', 'image'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
35 |
36 | DispositionsUtils.removeDispositionFromRequest(request, 'defer')
37 | assertTrue((['image'] as Set) == DispositionsUtils.getRequestDispositionsRemaining(request))
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resources/stash/ScriptStashWriterUnitTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resources.stash
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 | import org.junit.Before
6 | import org.junit.Test
7 |
8 | /**
9 | * Unit tests for {@link ScriptStashWriter}.
10 | *
11 | * @author Patrick Jungermann
12 | */
13 | @TestMixin(GrailsUnitTestMixin)
14 | class ScriptStashWriterUnitTests {
15 |
16 | ScriptStashWriter writer
17 |
18 | @Before
19 | void setUp() {
20 | writer = new ScriptStashWriter()
21 | }
22 |
23 | @Test(expected = NullPointerException)
24 | void writeButNoOutputTarget() {
25 | writer.write(null, ["fragment"])
26 | }
27 |
28 | @Test(expected = NullPointerException)
29 | void writeButNoStash() {
30 | writer.write(new StringWriter(), null)
31 | }
32 |
33 | @Test
34 | void writeEmptyStash() {
35 | StringWriter out = new StringWriter()
36 |
37 | writer.write(out, [])
38 |
39 | assertEquals "", out.toString()
40 | }
41 |
42 | @Test
43 | void writeStashWithOneFragment() {
44 | StringWriter out = new StringWriter()
45 |
46 | writer.write(out, ["fragment;"])
47 |
48 | String expected = ""
49 | assertEquals expected, out.toString()
50 | }
51 |
52 | @Test
53 | void writeStashWithMultipleFragments() {
54 | StringWriter out = new StringWriter()
55 |
56 | writer.write(out, ["fragment1;", "fragment2;"])
57 |
58 | String expected = ""
59 | assertEquals expected, out.toString()
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/test/unit/org/grails/plugin/resources/stash/StyleStashWriterUnitTests.groovy:
--------------------------------------------------------------------------------
1 | package org.grails.plugin.resources.stash
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 | import org.junit.Before
6 | import org.junit.Test
7 |
8 | /**
9 | * Unit tests for {@link StyleStashWriter}.
10 | *
11 | * @author Patrick Jungermann
12 | */
13 | @TestMixin(GrailsUnitTestMixin)
14 | class StyleStashWriterUnitTests {
15 |
16 | StyleStashWriter writer
17 |
18 | @Before
19 | void setUp() {
20 | writer = new StyleStashWriter()
21 | }
22 |
23 | @Test(expected = NullPointerException)
24 | void writeButNoOutputTarget() {
25 | writer.write(null, ["fragment"])
26 | }
27 |
28 | @Test(expected = NullPointerException)
29 | void writeButNoStash() {
30 | writer.write(new StringWriter(), null)
31 | }
32 |
33 | @Test
34 | void writeEmptyStash() {
35 | StringWriter out = new StringWriter()
36 |
37 | writer.write(out, [])
38 |
39 | assertEquals "", out.toString()
40 | }
41 |
42 | @Test
43 | void writeStashWithOneFragment() {
44 | StringWriter out = new StringWriter()
45 |
46 | writer.write(out, ["fragment;"])
47 |
48 | String expected = ""
49 | assertEquals expected, out.toString()
50 | }
51 |
52 | @Test
53 | void writeStashWithMultipleFragments() {
54 | StringWriter out = new StringWriter()
55 |
56 | writer.write(out, ["fragment1;", "fragment2;"])
57 |
58 | String expected = ""
59 | assertEquals expected, out.toString()
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/travis-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | rm -rf *.zip
4 | ./grailsw refresh-dependencies --non-interactive
5 | ./grailsw test-app --non-interactive
6 | ./grailsw package-plugin --non-interactive
7 | ./grailsw doc --pdf --non-interactive
8 |
9 | filename=$(find . -name "grails-*.zip" | head -1)
10 | filename=$(basename $filename)
11 | plugin=${filename:7}
12 | plugin=${plugin/.zip/}
13 | plugin=${plugin/-SNAPSHOT/}
14 | version="${plugin#*-}";
15 | plugin=${plugin/"-$version"/}
16 |
17 | echo "Publishing plugin $plugin with version $version"
18 |
19 | if [[ $TRAVIS_BRANCH == 'master' && $TRAVIS_REPO_SLUG == "grails-plugins/grails-$plugin" && $TRAVIS_PULL_REQUEST == 'false' && $TRAVIS_JOB_NUMBER =~ ^[0-9]+(\.1)?$ ]]; then
20 | git config --global user.name "$GIT_NAME"
21 | git config --global user.email "$GIT_EMAIL"
22 | git config --global credential.helper "store --file=~/.git-credentials"
23 | echo "https://$GH_TOKEN:@github.com" > ~/.git-credentials
24 |
25 |
26 | if [[ $filename != *-SNAPSHOT* ]]
27 | then
28 | git clone https://${GH_TOKEN}@github.com/$TRAVIS_REPO_SLUG.git -b gh-pages gh-pages --single-branch > /dev/null
29 | cd gh-pages
30 | git rm -rf .
31 | cp -r ../target/docs/. ./
32 | git add *
33 | git commit -a -m "Updating docs for Travis build: https://travis-ci.org/$TRAVIS_REPO_SLUG/builds/$TRAVIS_BUILD_ID"
34 | git push origin HEAD
35 | cd ..
36 | rm -rf gh-pages
37 | else
38 | echo "SNAPSHOT version, not publishing docs"
39 | fi
40 |
41 |
42 | ./grailsw publish-plugin --no-scm --allow-overwrite --non-interactive
43 | else
44 | echo "Not on master branch, so not publishing"
45 | echo "TRAVIS_BRANCH: $TRAVIS_BRANCH"
46 | echo "TRAVIS_REPO_SLUG: $TRAVIS_REPO_SLUG"
47 | echo "TRAVIS_PULL_REQUEST: $TRAVIS_PULL_REQUEST"
48 | fi
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-207/file1.js:
--------------------------------------------------------------------------------
1 | // empty file
2 |
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-207/file2.js:
--------------------------------------------------------------------------------
1 | // empty file
2 |
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-207/file3.js:
--------------------------------------------------------------------------------
1 | // empty file
2 |
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-207/file4.js:
--------------------------------------------------------------------------------
1 | // empty file
2 |
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-207/file5.js:
--------------------------------------------------------------------------------
1 | // empty file
2 |
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-210/file1.js:
--------------------------------------------------------------------------------
1 | // comment
--------------------------------------------------------------------------------
/web-app/GPRESOURCES-210/file2.js:
--------------------------------------------------------------------------------
1 | // comment
--------------------------------------------------------------------------------
/web-app/WEB-INF/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Grails application factory bean
9 |
10 |
11 |
12 |
13 |
14 | A bean that manages Grails plugins
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | utf-8
31 |
32 |
33 |
--------------------------------------------------------------------------------
/web-app/WEB-INF/sitemesh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/web-app/css/blueprint/ie.css:
--------------------------------------------------------------------------------
1 | /* -----------------------------------------------------------------------
2 |
3 |
4 | Blueprint CSS Framework 0.9
5 | http://blueprintcss.org
6 |
7 | * Copyright (c) 2007-Present. See LICENSE for more info.
8 | * See README for instructions on how to use Blueprint.
9 | * For credits and origins, see AUTHORS.
10 | * This is a compressed file. See the sources in the 'src' directory.
11 |
12 | ----------------------------------------------------------------------- */
13 |
14 | /* ie.css */
15 | body {text-align:center;}
16 | .container {text-align:left;}
17 | * html .column, * html div.span-1, * html div.span-2, * html div.span-3, * html div.span-4, * html div.span-5, * html div.span-6, * html div.span-7, * html div.span-8, * html div.span-9, * html div.span-10, * html div.span-11, * html div.span-12, * html div.span-13, * html div.span-14, * html div.span-15, * html div.span-16, * html div.span-17, * html div.span-18, * html div.span-19, * html div.span-20, * html div.span-21, * html div.span-22, * html div.span-23, * html div.span-24 {overflow-x:hidden;}
18 | * html legend {margin:0px -8px 16px 0;padding:0;}
19 | ol {margin-left:2em;}
20 | sup {vertical-align:text-top;}
21 | sub {vertical-align:text-bottom;}
22 | html>body p code {*white-space:normal;}
23 | hr {margin:-8px auto 11px;}
24 | img {-ms-interpolation-mode:bicubic;}
25 | .clearfix, .container {display:inline-block;}
26 | * html .clearfix, * html .container {height:1%;}
27 | fieldset {padding-top:0;}
28 | input.text, input.title {background-color:#fff;border:1px solid #bbb;}
29 | input.text:focus, input.title:focus {border-color:#666;}
30 | input.text, input.title, textarea, select {margin:0.5em 0;}
31 | input.checkbox, input.radio {position:relative;top:.25em;}
32 | form.inline div, form.inline p {vertical-align:middle;}
33 | form.inline label {position:relative;top:-0.25em;}
34 | form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;}
35 | button, input.button {position:relative;top:0.25em;}
--------------------------------------------------------------------------------
/web-app/css/blueprint/src/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/css/blueprint/src/grid.png
--------------------------------------------------------------------------------
/web-app/css/errors.css:
--------------------------------------------------------------------------------
1 | h1, h2 {
2 | margin: 10px 25px 5px;
3 | }
4 |
5 | h2 {
6 | font-size: 1.1em;
7 | }
8 |
9 | .filename {
10 | font-style: italic;
11 | }
12 |
13 | .exceptionMessage {
14 | margin: 10px;
15 | border: 1px solid #000;
16 | padding: 5px;
17 | background-color: #E9E9E9;
18 | }
19 |
20 | .stack,
21 | .snippet {
22 | margin: 0 25px 10px;
23 | }
24 |
25 | .stack,
26 | .snippet {
27 | border: 1px solid #ccc;
28 | -mox-box-shadow: 0 0 2px rgba(0,0,0,0.2);
29 | -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2);
30 | box-shadow: 0 0 2px rgba(0,0,0,0.2);
31 | }
32 |
33 | /* error details */
34 | .error-details {
35 | border-top: 1px solid #FFAAAA;
36 | -mox-box-shadow: 0 0 2px rgba(0,0,0,0.2);
37 | -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2);
38 | box-shadow: 0 0 2px rgba(0,0,0,0.2);
39 | border-bottom: 1px solid #FFAAAA;
40 | -mox-box-shadow: 0 0 2px rgba(0,0,0,0.2);
41 | -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2);
42 | box-shadow: 0 0 2px rgba(0,0,0,0.2);
43 | background-color:#FFF3F3;
44 | line-height: 1.5;
45 | overflow: hidden;
46 | padding: 5px;
47 | padding-left:25px;
48 | }
49 |
50 | .error-details dt {
51 | clear: left;
52 | float: left;
53 | font-weight: bold;
54 | margin-right: 5px;
55 | }
56 |
57 | .error-details dt:after {
58 | content: ":";
59 | }
60 |
61 | .error-details dd {
62 | display: block;
63 | }
64 |
65 | /* stack trace */
66 | .stack {
67 | padding: 5px;
68 | overflow: auto;
69 | height: 150px;
70 | }
71 |
72 | /* code snippet */
73 | .snippet {
74 | background-color: #fff;
75 | font-family: monospace;
76 | }
77 |
78 | .snippet .line {
79 | display: block;
80 | }
81 |
82 | .snippet .lineNumber {
83 | background-color: #ddd;
84 | color: #999;
85 | display: inline-block;
86 | margin-right: 5px;
87 | padding: 0 3px;
88 | text-align: right;
89 | width: 3em;
90 | }
91 |
92 | .snippet .error {
93 | background-color: #fff3f3;
94 | font-weight: bold;
95 | }
96 |
97 | .snippet .error .lineNumber {
98 | background-color: #faa;
99 | color: #333;
100 | font-weight: bold;
101 | }
102 |
103 | .snippet .line:first-child .lineNumber {
104 | padding-top: 5px;
105 | }
106 |
107 | .snippet .line:last-child .lineNumber {
108 | padding-bottom: 5px;
109 | }
--------------------------------------------------------------------------------
/web-app/css/legacy.css:
--------------------------------------------------------------------------------
1 | /* Hello world */
2 | body {
3 | color: #222;
4 | }
5 |
--------------------------------------------------------------------------------
/web-app/css/mobile.css:
--------------------------------------------------------------------------------
1 | /* Styles for mobile devices */
2 |
3 | @media screen and (max-width: 480px) {
4 | .nav {
5 | padding: 0.5em;
6 | }
7 |
8 | .nav li {
9 | margin: 0 0.5em 0 0;
10 | padding: 0.25em;
11 | }
12 |
13 | /* Hide individual steps in pagination, just have next & previous */
14 | .pagination .step, .pagination .currentStep {
15 | display: none;
16 | }
17 |
18 | .pagination .prevLink {
19 | float: left;
20 | }
21 |
22 | .pagination .nextLink {
23 | float: right;
24 | }
25 |
26 | /* pagination needs to wrap around floated buttons */
27 | .pagination {
28 | overflow: hidden;
29 | }
30 |
31 | /* slightly smaller margin around content body */
32 | fieldset,
33 | .property-list {
34 | padding: 0.3em 1em 1em;
35 | }
36 |
37 | input, textarea {
38 | width: 100%;
39 | -moz-box-sizing: border-box;
40 | -webkit-box-sizing: border-box;
41 | -ms-box-sizing: border-box;
42 | box-sizing: border-box;
43 | }
44 |
45 | select, input[type=checkbox], input[type=radio], input[type=submit], input[type=button], input[type=reset] {
46 | width: auto;
47 | }
48 |
49 | /* hide all but the first column of list tables */
50 | .scaffold-list td:not(:first-child),
51 | .scaffold-list th:not(:first-child) {
52 | display: none;
53 | }
54 |
55 | .scaffold-list thead th {
56 | text-align: center;
57 | }
58 |
59 | /* stack form elements */
60 | .fieldcontain {
61 | margin-top: 0.6em;
62 | }
63 |
64 | .fieldcontain label,
65 | .fieldcontain .property-label,
66 | .fieldcontain .property-value {
67 | display: block;
68 | float: none;
69 | margin: 0 0 0.25em 0;
70 | text-align: left;
71 | width: auto;
72 | }
73 |
74 | .errors ul,
75 | .message p {
76 | margin: 0.5em;
77 | }
78 |
79 | .error ul {
80 | margin-left: 0;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/web-app/images/apple-touch-icon-retina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/apple-touch-icon-retina.png
--------------------------------------------------------------------------------
/web-app/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/web-app/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/favicon.ico
--------------------------------------------------------------------------------
/web-app/images/grails_logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/grails_logo.jpg
--------------------------------------------------------------------------------
/web-app/images/grails_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/grails_logo.png
--------------------------------------------------------------------------------
/web-app/images/leftnav_btm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/leftnav_btm.png
--------------------------------------------------------------------------------
/web-app/images/leftnav_midstretch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/leftnav_midstretch.png
--------------------------------------------------------------------------------
/web-app/images/leftnav_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/leftnav_top.png
--------------------------------------------------------------------------------
/web-app/images/skin/database_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/database_add.png
--------------------------------------------------------------------------------
/web-app/images/skin/database_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/database_delete.png
--------------------------------------------------------------------------------
/web-app/images/skin/database_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/database_edit.png
--------------------------------------------------------------------------------
/web-app/images/skin/database_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/database_save.png
--------------------------------------------------------------------------------
/web-app/images/skin/database_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/database_table.png
--------------------------------------------------------------------------------
/web-app/images/skin/exclamation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/exclamation.png
--------------------------------------------------------------------------------
/web-app/images/skin/house.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/house.png
--------------------------------------------------------------------------------
/web-app/images/skin/information.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/information.png
--------------------------------------------------------------------------------
/web-app/images/skin/shadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/shadow.jpg
--------------------------------------------------------------------------------
/web-app/images/skin/sorted_asc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/sorted_asc.gif
--------------------------------------------------------------------------------
/web-app/images/skin/sorted_desc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/skin/sorted_desc.gif
--------------------------------------------------------------------------------
/web-app/images/spinner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/spinner.gif
--------------------------------------------------------------------------------
/web-app/images/springsource.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/images/springsource.png
--------------------------------------------------------------------------------
/web-app/js/adhoc.js:
--------------------------------------------------------------------------------
1 | // Nothing here either
--------------------------------------------------------------------------------
/web-app/js/application.js:
--------------------------------------------------------------------------------
1 | var Ajax;
2 | if (Ajax && (Ajax != null)) {
3 | Ajax.Responders.register({
4 | onCreate: function() {
5 | if($('spinner') && Ajax.activeRequestCount>0)
6 | Effect.Appear('spinner',{duration:0.5,queue:'end'});
7 | },
8 | onComplete: function() {
9 | if($('spinner') && Ajax.activeRequestCount==0)
10 | Effect.Fade('spinner',{duration:0.5,queue:'end'});
11 | }
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/web-app/js/core.js:
--------------------------------------------------------------------------------
1 | // Nothing to see here
2 |
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_flat_15_cd0a0a_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_flat_15_cd0a0a_40x100.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_glass_100_e4f1fb_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_glass_100_e4f1fb_1x400.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_glass_50_3baae3_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_glass_50_3baae3_1x400.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_glass_80_d7ebf9_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_glass_80_d7ebf9_1x400.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-hard_70_000000_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-hard_70_000000_1x100.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-soft_100_deedf7_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-soft_100_deedf7_1x100.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-soft_25_ffef8f_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-bg_highlight-soft_25_ffef8f_1x100.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_2694e8_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_2694e8_256x240.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_2e83ff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_2e83ff_256x240.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_3d80b3_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_3d80b3_256x240.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_72a7cf_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_72a7cf_256x240.png
--------------------------------------------------------------------------------
/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_ffffff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/web-app/js/jquery-ui/themes/custom-theme/images/ui-icons_ffffff_256x240.png
--------------------------------------------------------------------------------
/wrapper/grails-wrapper-runtime-2.3.11.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/wrapper/grails-wrapper-runtime-2.3.11.jar
--------------------------------------------------------------------------------
/wrapper/grails-wrapper.properties:
--------------------------------------------------------------------------------
1 | wrapper.dist.url=http://dist.springframework.org.s3.amazonaws.com/release/GRAILS/
2 |
--------------------------------------------------------------------------------
/wrapper/springloaded-1.2.0.RELEASE.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grails-plugins/grails-resources/5577fdd43a45ad2c3db0c40854736eaa9f342cf7/wrapper/springloaded-1.2.0.RELEASE.jar
--------------------------------------------------------------------------------