Distribute your XULRunner app - 3 - Windows
Par joliclic le mercredi, mai 25 2011, 14:25 - mozilla - Lien permanent
Ce billet existe aussi en français
This post is part of a series about how to package a XULRunner application.
See the preamble for the context case.
3 - Windows
- 3.1 Icons for Windows
- 3.2 Create a launcher (batch or C)
- 3.3 Create a NSIS script
- 3.4 Create the installer
3.1 Icons for Windows
We need a icon in .ico format. It will be used by ours windows, and by our launcher.
It's easy to make a .ico file on Linux, we just need to install the
program icotool
, from the icoutils
package.
On Debian/Ubuntu:
apt-get install icoutils
Then we create a ico file from several png at different size, 16x16 px, 32x32 px, and 48x48 px. It would be possible to add more png, with different color depth as well.
icotool -c -o icon.ico icon16.png icon32.png icon48.png
You can find a bash script with this command line in the sample archive
related to this chapter 3 (in samples_chapter_3/data/icons/
).
To add our icon to our windows, rather than the firefox or xulrunner one,
we have just to put this icon, renamed as default.ico
, into a
icons/default
folder situated in our main chrome folder.
This icon will be used by all XUL <window>
without an
id
attribute.
But the main XUL <window>
of myapp has the attribute
id="main"
, so for this window we must have an icon named
main.ico
, situated in the same folder:
|- samples_chapter_3/
|- myapp/
|- chrome
|- icons/
|- default/
|- default.ico
|- main.ico
3.2 Create a launcher (batch or C)
The simplest launcher is a batch script. Here the content
of myapp.bat
:
set CUR_DIR=%~dp0
START firefox -app $CUR_DIR\application.ini
This file must be placed into the main folder of our app.
This works, it launches our app via the application.ini file of our app.
But there's something annoying, a black command window is opened too.
To avoid the previously mentioned default, we will create a launcher in C.
We use the code of XAL
(XUL Application Launcher). This is a light C program,
MIT
licensed, it launches
a XULRunner application with Firefox, with the -app
argument and the
application.ini
.
It must be placed in the main application folder (like the previous batch).
Bonus, it handles additional arguments (like -jsconsole
) if
used in command line. And we can add our icon, and some application
information via a .rc file.
This executable can be compiled with any C compiler, its code is
independent of the Mozilla code. I personaly compile it with
MinGW, on Linux, with
success. If you plan to use another compiler, edit the build file, and
adapt the CC
and RESCOMP
variables.
To install MinGW on Debian/Ubuntu:
apt-get install mingw32
I don't publish here the C source of this code, you'll find it in the archive related to this chapter 3, or in its dedicated page.
We can customize this launcher, using a
resource
(.rc
) file, insert our icon and specify some
information (vendor, app name, version,...).
Here the content of the myapp-res.rc file:
APPLICATION_ICON ICON "myapp.ico"
1 VERSIONINFO
FILEVERSION 0,1,0,0
PRODUCTVERSION 0,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004B0"
BEGIN
VALUE "Comments", "Published under the MPL 1.1/GPL 2.0/LGPL 2.1 licenses"
VALUE "CompanyName", "John Doe Organization"
VALUE "FileDescription", "MyApp"
VALUE "FileVersion", "1.0.0"
VALUE "InternalName", "MyApp"
VALUE "LegalCopyright", "(c) 2010 John Doe"
VALUE "ProductName", "MyApp"
VALUE "ProductVersion", "0.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x000, 1200
END
END
Some details about this content:
The line APPLICATION_ICON ICON "myapp.ico"
integrate our icon
The FILEVERSION
and PRODUCTVERSION
entries use a special format, in short integer comma integer comma integer comma integer
.
The line VALUE "Translation", 0x000, 1200
specify that the
strings for information use Unicode.
We can compile this launcher, using our resource file, with the
build.sh
script distributed with XAL:
sh build.sh myapp myapp-res.rc
After compilation, the size of the result file is ~21Kb, so very small (note that the size of the icon is included).
3.3 Create a NSIS script
There are several solution to create Windows installers. Here, we will
use NSIS,
because: it's open source, we can build our installer from Linux, and
it's easy and powerful.
Firefox itself use NSIS for its Windows installer.
To install NSIS tools on Debian/Ubuntu:
apt-get install nsis
NSIS uses its own script language to create installers.
We can use some default pages, create some custom ones, manage files (un)installation,
act on the Windows Registry,...
I will not do a complete NSIS documentation here, see their
wiki for that,
there's a lot of explanations and examples.
You should have some local examples and doc after installing nsis,
in /usr/share/doc/nsis/Examples
and /usr/share/doc/nsis/Doc
.
But here's the main proposed script here, and I will explain what actions are performed after:
!define PRODUCT_NAME "MyApp"
!define PRODUCT_INTERNAL_NAME "myapp"
!define PRODUCT_VERSION "1.0"
!define PRODUCT_WIN_VERSION "1.0.0.0"
!define MESSAGEWINDOW_NAME "${PRODUCT_NAME}MessageWindow"
!define HKEY_ROOT "HKLM"
!define UN_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define LICENSE_PATH "${PRODUCT_INTERNAL_NAME}\LICENSE.txt"
!define INSTALLER_NAME "${PRODUCT_NAME}-${PRODUCT_VERSION}-install.exe"
!define TMP_UNINSTALL_EXE "${PRODUCT_INTERNAL_NAME}_uninstall.exe"
;--------------------------------
;Variables
; The name of the product installed
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
; The file to write
OutFile "${INSTALLER_NAME}"
SetCompressor /final /solid lzma
ShowInstDetails show
ShowUninstDetails show
; The default installation directory
InstallDir $PROGRAMFILES\${PRODUCT_NAME}
; Request application privileges for Windows Vista
RequestExecutionLevel admin
Var Shortcuts_Dialog
Var Shortcuts_Label
Var Shortcuts_SM_Checkbox
Var Shortcuts_SM_Checkbox_State
Var Shortcuts_D_Checkbox
Var Shortcuts_D_Checkbox_State
Var Previous_Uninstall
Var Previous_Uninstall_dir
Var TempUninstallPath
!include "MUI2.nsh"
!include "FileFunc.nsh"
VIProductVersion "${PRODUCT_WIN_VERSION}"
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
;VIAddVersionKey "CompanyName" "${CompanyName}"
;VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of"
;VIAddVersionKey "LegalCopyright" "${CompanyName}"
VIAddVersionKey "LegalCopyright" ""
VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "FileDescription" "${PRODUCT_NAME} Installer"
VIAddVersionKey "OriginalFilename" "${INSTALLER_NAME}"
!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"
;--------------------------------
; Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${LICENSE_PATH}"
!insertmacro MUI_PAGE_DIRECTORY
Page custom onShortcutsPageCreate
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "French"
!include ./l10n/fr.nsh
!include ./l10n/en_US.nsh
;--------------------------------
Function .onInit
; an eventual previous version of the app should not be currently running.
; Abort if any.
; Explanation, when the application is running, a window with the className
; productnameMessageWindow exists
FindWindow $0 "${MESSAGEWINDOW_NAME}"
StrCmp $0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "${PRODUCT_NAME} is running. Please close it first" /SD IDOK
Abort
StrCpy $Shortcuts_SM_Checkbox_State 1
StrCpy $Shortcuts_D_Checkbox_State 1
FunctionEnd
Function un.onInit
; see Function .onInit
FindWindow $0 "${MESSAGEWINDOW_NAME}"
StrCmp $0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "${PRODUCT_NAME} is running. Please close it first" /SD IDOK
Abort
FunctionEnd
; custom page creation, for the shortcuts installation, using nsDialog
Function onShortcutsPageCreate
!insertmacro MUI_HEADER_TEXT $(l10n_SHORTCUTS_PAGE_TITLE) \
$(l10n_SHORTCUTS_PAGE_SUBTITLE)
nsDialogs::Create 1018
Pop $Shortcuts_Dialog
${If} $Shortcuts_Dialog == error
Abort
${EndIf}
${NSD_CreateLabel} 0 6 100% 12u $(l10n_CREATE_ICONS_DESC)
Pop $Shortcuts_Label
${NSD_CreateCheckbox} 15u 20u 100% 10u $(l10n_ICONS_STARTMENU)
Pop $Shortcuts_SM_Checkbox
GetFunctionAddress $0 OnSMCheckbox
nsDialogs::OnClick $Shortcuts_SM_Checkbox $0
${If} $Shortcuts_SM_Checkbox_State == ${BST_CHECKED}
${NSD_Check} $Shortcuts_SM_Checkbox
${EndIf}
${NSD_CreateCheckbox} 15u 40u 100% 10u $(l10n_ICONS_DESKTOP)
Pop $Shortcuts_D_Checkbox
GetFunctionAddress $0 OnDCheckbox
nsDialogs::OnClick $Shortcuts_D_Checkbox $0
${If} $Shortcuts_D_Checkbox_State == ${BST_CHECKED}
${NSD_Check} $Shortcuts_D_Checkbox
${EndIf}
nsDialogs::Show
FunctionEnd
; event when the Start Menu shortcut is (un)checked in the custom page
Function OnSMCheckbox
${NSD_GetState} $Shortcuts_SM_Checkbox $Shortcuts_SM_Checkbox_State
Pop $0 # HWND
FunctionEnd
; event when the Desktop shortcut is (un)checked in the custom page
Function OnDCheckbox
${NSD_GetState} $Shortcuts_D_Checkbox $Shortcuts_D_Checkbox_State
Pop $0 # HWND
FunctionEnd
Function WriteUninstallReg
WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayName" \
"${PRODUCT_NAME} (${PRODUCT_VERSION})"
WriteRegStr ${HKEY_ROOT} ${UN_KEY} "UninstallString" \
"$INSTDIR\uninstall.exe"
WriteRegStr ${HKEY_ROOT} ${UN_KEY} "QuietUninstallString" \
"$INSTDIR\uninstall.exe /S"
WriteRegStr ${HKEY_ROOT} ${UN_KEY} "InstallLocation" \
"$INSTDIR"
WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayIcon" \
"$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"
WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayVersion" \
"${PRODUCT_VERSION}"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD ${HKEY_ROOT} ${UN_KEY} "EstimatedSize" "$0"
FunctionEnd
; The stuff to install
Section ""
; uninstall an eventual previous installation
ReadRegStr $Previous_Uninstall ${HKEY_ROOT} ${UN_KEY} "UninstallString"
ClearErrors
${If} $Previous_Uninstall != ""
StrCpy $Previous_Uninstall_dir $Previous_Uninstall
${GetParent} $Previous_Uninstall $Previous_Uninstall_dir
IfFileExists "$Previous_Uninstall" myUninstallPrevious myInstall
${Else}
goto myInstall
${EndIf}
myUninstallPrevious:
; copy the previous uninstaller into TEMP
ClearErrors
StrCpy $TempUninstallPath "$TEMP\${TMP_UNINSTALL_EXE}"
CopyFiles /SILENT "$Previous_Uninstall" "$TempUninstallPath"
IfErrors myInstall
ClearErrors
ExecWait '"$TempUninstallPath" /S _?=$Previous_Uninstall_dir'
ClearErrors
Delete "$TempUninstallPath"
;MessageBox MB_OK "UNINSTALL: finished"
myInstall:
SetOutPath $INSTDIR
; copy the files
File /r ${PRODUCT_INTERNAL_NAME}\*
WriteUninstaller "uninstall.exe"
Call WriteUninstallReg
${If} $Shortcuts_SM_Checkbox_State == ${BST_CHECKED}
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" \
"$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"
${EndIf}
${If} $Shortcuts_D_Checkbox_State == ${BST_CHECKED}
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" \
"$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"
${EndIf}
SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR" /SD IDOK
; Remove installed files and uninstaller
!include ./uninstall_files.nsi
Delete "$INSTDIR\uninstall.exe"
; remove installed directories
!include ./uninstall_dirs.nsi
RMDir /r "$INSTDIR\extensions"
; Remove shortcuts, if any
Delete "$SMPROGRAMS\${PRODUCT_NAME}.lnk"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
;TODO remove eventual quicklaunch Too
; Remove the installation directory used (if empty)
RMDir "$INSTDIR"
; and delete the registry key for uninstall
DeleteRegKey ${HKEY_ROOT} ${UN_KEY}
SectionEnd
Some descriptions about this script:
It uses the default "modern" skin. But it's possible to customize it.
It uses some localized parts, including some other nsis scripts (described later).
It uses some default nsis pages, and a custom one to allow the creation of shortcuts on the desktop and in the start menu.
The installation and uninstallation are aborted if our application is currently running.
In fact, when our app is running, XULRunner/Firefox create a native Windows window with
a class MyAppMessageWindow
, this class name is the value of the field
Name
of the application.ini
concatenated with "MessageWindow".
The script just checks if such a window with this class name is opened, then abort.
It creates some minimal Registry entries to allow the uninstallation of the app with the "add/remove programs" tool.
If our app is already installed, the previous version is uninstalled before the new installation, using our previous uninstaller. And, to be more precise, this previous uninstaller is copied and launched from the Windows temp folder.
For the uninstaller, we need the complete list of installed files and directories.
These lists will be dynamically created later, for the moment in this script we
just include them as other nsis scripts (uninstall_files.nsi
and uninstall_dirs.nsi
).
Now here the content of one of the localized included script, en_US.nsh:
;!!! IMPORTANT: this must file must be edited with ANSI charset (in fact the
; Windows CP-1252 charset), but if there's no special character, UTF-8 is ok,
; because it's a subset ;)
LangString l10n_SHORTCUTS_PAGE_TITLE ${LANG_ENGLISH} \
"Set Up Shortcuts"
LangString l10n_SHORTCUTS_PAGE_SUBTITLE ${LANG_ENGLISH} \
"Create Program Icons"
LangString l10n_CREATE_ICONS_DESC ${LANG_ENGLISH} \
"Create icons for ${PRODUCT_NAME}:"
LangString l10n_ICONS_DESKTOP ${LANG_ENGLISH} \
"On my &Desktop"
LangString l10n_ICONS_STARTMENU ${LANG_ENGLISH} \
"In my &Start Menu Programs folder"
We have to be careful with the encoding of these files, this English
one is not a problem, but the French one must be edited with the Windows
CP-1252
charset for exemple.
These variables are simply used in our main script with, for example,
$(l10n_SHORTCUTS_PAGE_TITLE)
.
Note that the ampersand &
in the string definitions
define an access key for controls in the UI, here there's alt+D
and alt+S
.
3.4 Create the installer
We have now all the files to create the installer. Let see our sources tree:
|-samples_chapter_3
|- data/
|- icons/
|- icon.ico
|- myapp.bat
|- myapp-res.rc
|- myapp/
|- win
|- nsis/
|- l10n/
|- en_US.nsh
|- fr.nsh
|- myapp.nsi
|- xal-src/
|- build.sh
|- clean.sh
|- main.c
We have to build our launcher from the C source and our data (icon and resource).
And create 2 additional nsis scripts, listing our app files.
Then we can create the installer with makensis
. We will doing
this in a temporary folder. Here's the script, named build_win.sh
:
#!/bin/bash
# exit the script on errors
set -e
TMP_DIR=./tmp
# get the absolute path of our TMP_DIR folder
TMP_DIR=$(readlink -f -- "$TMP_DIR")
# re-create the TMP_DIR folder
rm -rfv "$TMP_DIR"
mkdir -v "$TMP_DIR"
# copy our app
cp -rv myapp "$TMP_DIR/"
# copy the XAL launcher sources
cp -rv win/xal-src "$TMP_DIR/xal"
# and our icon and resource file
cp -v data/icons/icon.ico "$TMP_DIR/xal/myapp.ico"
cp -v data/myapp-res.rc "$TMP_DIR/xal/"
# build the launcher
cd "$TMP_DIR/xal/"
sh build.sh myapp myapp-res.rc
cd -
# copy the launchers in the right folder
cp -v data/myapp.bat "$TMP_DIR/myapp/"
cp -v "$TMP_DIR/xal/myapp.exe" "$TMP_DIR/myapp/"
# delete the xal sources
rm -rv "$TMP_DIR/xal"
# create the nsis script listing the files to unsinstall
cd "$TMP_DIR/myapp"
find . -maxdepth 1 -type f > ../uninstall_files.nsi
sed -i -r "s|^\./(.*)$| Delete \"\$INSTDIR\\\\\1\"|" ../uninstall_files.nsi
# and the list of directory
ls -d */ > ../uninstall_dirs.nsi
sed -i -r "s|^(.*)/$| RMDir /r \"\$INSTDIR\\\\\1\"|" ../uninstall_dirs.nsi
cd -
# copy the other nsis scripts
cp -rv win/nsis/* "$TMP_DIR/"
# and create the installer
makensis "$TMP_DIR/myapp.nsi"
# finally, copy our installer in the root sources dir
cp -v "$TMP_DIR/MyApp-1.0-install.exe" ./
echo "Windows installer for myapp created."
To launch this script, in a terminal:
cd samples_chapter_3
sh ./build_win.sh
And as result we have finally the file MyApp-1.0-install.exe
in the samples_chapter_3
folder :) .
You can download all the samples of this chapter 3 (Windows) in the samples_chapter_3.tar.gz archive.
The myapp application, from developer.mozilla.org is in the Public Domain.
The icon used is from the Tango Desktop Project, and is in the Public Domain.
The C launcher XUL App Launcher (XAL) is under the MIT license.
All other added data, and sample files, of this chapter 3, are in the Public Domain too.
Commentaires
You should really post this on MDC.
Let me know if you're interested (drop me an email).
@Paul
Sure! :)
I will contact you when I'll have finished to publish here the entire howto ;)
Hello, all is going well here and ofcourse every one is sharing data,
that's in fact excellent, keep up writing.
It's hard to come by educated people about this topic, however, you sound like you know what you're talking
about! Thanks
I do not even know how I stopped up here, but I
assumed this publish was great. I don't understand who you're but definitely
you're going to a famous blogger in case you aren't already.
Cheers!
This is thee right webpage for anyone who wants to understand this topic.
You realize a whole llot its almost hard to argue with you (not that I personally will need to…HaHa).
You definitely put a fresh spin on a topic which has been discussed for ages.
Wonderful stuff, ust excellent!
Link exchange is nothing else but it is just placing the other person's weblog link on your page at proper place and
other person will also do same for you.
I'm very pleased to find this great site. I want to to thank you for ones
time for this wonderful read!! I definitely savored every little bit of it and i also have you
saved as a favorite to see new information on your web site.
If you are working within a corporation, you will need to work closely with
the marketing team to ensure a steady flow of work for
you. Therefore, if it turns out you valued this piece I do think that yoou may
want to check out the various other pieces I have penned.
CMS web design is the developing website design which meets these requirements well and
that is why it has become the most liked choice of the web developers to build a website.
Fabulous, what a web site it is! This weblog gives useful
data to us, keep it up.
Scandinavian broadcasters DR and SVT may be worried
about the premiere of The Peanuts Movie” at the 2015
BFI London Film Festival, with the all of six actors
who dubbed the voices of major characters in both Danish and Swedish versions of the film.
It's a shame you don't have a donate button! I'd without a doubt donate to this fantastic blog!
I suppose for now i'll settle for book-marking and adding your RSS feed to my Google account.
I look forward to fresh updates and will talk about this website with my Facebook group.
Chat soon!
I’m not that much of a internet reader to be honest but your sites really nice, keep it up!
I'll go ahead and bookmark your websife to come back later on. All
the best
Hello there! Quick question that's entirely
off topic. Do you know how to make your site mobile friendly?
My weblog looks weird when viewing from my iphone. I'm trying
to find a template or plugin that might be able to fix this issue.
If you have any suggestions, please share. Thank you!
bq g jones
I pay a quick visit everyday some sites and blogs to read content, but this web site gives feature based posts.
If you are going for best contents like me, only go to see this website everyday for the reason that it gives quality contents, thanks
Your style is unique in comparison to other people I have read stuff from.
I appreciate you for posting when you have the opportunity, Guess I'll just book mark
this page.
Yes! Finally something about http://finance.yahoo.com/news/perfe...
Wow, fantastic blog format! How long have you ever been running
a blog for? you make running a blog look easy.
The whole glance of your site is excellent, let alone the content
material!
Hello, I read your blogs like every week. Your humoristic style is awesome, keep up the good work!
Excellent blog here! Also your site loads up fast!
What web host are you using? Can I get your affiliate
link to your host? I wish my site loaded up as quickly as yours lol
Hello! This is kind of off topic but I need some advice from an established blog.
Is it difficult to set up your own blog? I'm not very techincal but
I can figure things out pretty fast. I'm thinking about creating my own but I'm
not sure where to begin. Do you have any tips or suggestions?
Thank you
Hi, I do believe this is a great site. I stumbledupon it ;
) I am going to revisit yet again since I book-marked it.
Money and freedom is the best way to change, may you
be rich and continue to help others.
Hi, everything is going fine here and ofcourse every one is sharing
information, that's actually excellent, keep up writing.
Have you ever considered creating an e-book or guest authoring on other blogs?
I have a blog centered on the same topics you discuss and would really like to have you share
some stories/information. I know my viewers would
appreciate your work. If you're even remotely interested, feel free to
send me an e mail.
Wow, this article is fastidious, my younger sister is analyzing these things, so I
am going to convey her.
Great beat ! I wish to apprentice while you amend your web site, how can i subscribe for a blog website?
The account aided me a acceptable deal. I had been tiny bit acquainted of this your broadcast offered bright clear
concept
It's very straightforward to find out any matter on net as compared
to textbooks, as I found this piece of writing at this web page.
Greetings from Colorado! I'm bored to death at work so
I decided to check out your blog on my iphone during lunch break.
I love the info you provide here and can't wait to take a look when I get
home. I'm surprised at how quick your blog loaded on my cell phone ..
I'm not even using WIFI, just 3G .. Anyways, wonderful site!
Wow! This blog looks just like my old one! It's on a entirely different subject but it has pretty much the same page layout and
design. Superb choice of colors!
Generally I don't learn post on blogs, but I would like
to say that this write-up very pressured me to check out and do so!
Your writing style has been surprised me. Thank you, very great post.
Pretty! This has been a really wonderful article.
Thank you for supplying this information.
First of all I want to say great blog! I had a quick question in which I'd like to
ask if you don't mind. I was interested to find out how you
center yourself and clear your thoughts before writing. I've had difficulty clearing my thoughts in getting
my ideas out there. I do enjoy writing but it just seems like the first 10
to 15 minutes are lost just trying to figure out how
to begin. Any suggestions or hints? Thank you!
great article, I want to develop my blog website
You are so interesting! I don't suppose I've read something like
that before. So nice to discover somebody with some original thoughts on this subject matter.
Really.. thank you for starting this up. This website is one thing
that is needed on the internet, someone with a little originality!
Hi my family member! I wish to say that this article is amazing,
nice written and come with almost all important infos.
I'd like to look extra posts like this .
Excellent post. I was checking continuously this blog and I am impressed!
Very useful information particularly the last part :) I care for such information a
lot. I was looking for this certain information for a long time.
Thank you and good luck.
Aw, this was an extremely good post. Taking a few minutes and actual
effort to produce a good article… but what can I
say… I hesitate a lot and never seem to get anything done.
Hello! I could have sworn I've been to this site before but after reading through some of the
post I realized it's new to me. Anyways, I'm definitely delighted I found it
and I'll be book-marking and checking back frequently!
I have been browsing online more than three hours these days,
yet I by no means found any fascinating article
like yours. It's beautiful value enough for me. Personally, if all site owners and bloggers made excellent content material as you probably did, the internet
shall be much more useful than ever before.
Spot on ԝith this write-up, I truly thіnk this site needs mᥙch more
ɑttention. I'll proƅably be returning to read more, thanks for the info!
My brother suggested I might like this website. He was entirely
right. This post actually made my day. You cann't imagine simply how much time I had spent for this information! Thanks!
Hi! I know this is somewhat off topic but I was wondering which blog platform are you using for this website?
I'm getting tired of Wordpress because I've had issues with hackers and I'm looking at alternatives for another platform.
I would be great if you could point me in the direction of a good platform.
Wow! In the end I got a web site from where I can actually
get useful information concerning my study and knowledge.
Canada best buy cannabis mix and match
First off I want to say superb blog! I had a quick question that I'd like
to ask if you don't mind. I was curious to know how you center yourself and clear your head before writing.
I've had trouble clearing my thoughts in getting my thoughts out.
I truly do take pleasure in writing however it just seems like the first 10 to
15 minutes are usually lost just trying to figure out
how to begin. Any ideas or tips? Appreciate
it! Website buy cannabis pre rolls
I just couldn't go away your web site prior to suggesting
that I extremely loved the usual information an individual provide for your
visitors? Is gonna be back steadily to check up on new posts.
Where you buy cannabis shatter
Thank you a bunch for sharing this with all people you actually recognise what you're talking approximately!
Bookmarked. Please additionally visit my website =).
We will have a hyperlink alternate arrangement between us
Ӏ am really impressed with your writing skills as well as with the layout
on your blog. Is this a paiⅾ theme or did you customize it yoursеlf?
Eitheг way keep up the nice quality writing, it is rare tօ sеe a great blog
like thіs one today.
Hi, i thіnk that i saw yоu vіsited my web site thus i got here to go back the desire?.I am
trying to tⲟ find things to enhance my web site!I assume its good enoսgh to uѕe a few of your ideas!!
As thе admin of thiѕ web page is working, no doubt very quickly it wіll be famous, due to its feature contents.
This іѕ really inteгesting, You are a very
skilled blogger. I've joined your feed and look forward to seeking mⲟre of yoսr excellent
post. Aⅼsߋ, I've shared your website in my social networks!
Thiѕ ƅlog wаs... how do you say it? Ꮢelevant!!
Finally I have found something ѡhich helped me. Cheers!
Hey there just wanteԁ to give уoᥙ a quick heads up.
The words in уour content sеem to be running off the screen in Fіrefox.
I'm not sure if this is a f᧐rmatting issue or something to ɗo with Ьrowser compatibility but I figured I'd
post to let you know. The style and dеsign look greаt though!
Hope you get the prߋblem resolved soon. Thanks
If you want to obtɑin a grеat deal from this
piece of writing then you hаve to apply these methoⅾs
to your won weblog.
This is vеry interestіng, You are a very skiⅼⅼed bⅼ᧐gger.
I've ϳoineԁ your rss feed and look forward tⲟ seeking
more of your eⲭcellent post. Also, I havе shаreԁ your web site in my social networks!
cеrtainly like your wеb-site however you need to test
the spelling on seѵeral of youг posts.
Many of them are rife with spelling problems and Ι in finding it very bothersome to infօrm the truth
nevertheⅼess I will surely come back again.
There's certainlу а great deal to find out about this
topic. I like all the points yoս made.
There's certainlу а great deal to find out about this
topic. I like all the points yoս made.
Υes! Finally someone writes aboᥙt หนังโป๊.
Ԛuality posts is the key to interest the users to pay a visit tһe web site, that's what this
web site is providing.
Hello! Tһis is my first viѕіt to your blog!
We are a team of volunteers and starting a new initiative in a community
in the sаme niche. Your blog provided us valuaƄle informatіon to work on. You have done a extraordinary job!
Whoɑ! This blog looks exactly like my оld one!
It's on a entirely Ԁifferent subject but it has pгеtty mսch the same page layout and design. Outstanding choice of ϲolors!
Unqueѕtionably believe that which you stated. Your fɑvorite
rеason seemed to ƅe on the intеrnet the simрlest thing to be awarе of.
Ι say to you, I definitely get annoyed whіle people think abօut worries that they plaіnlү
do not know about. You managed to hit the nail uрon the top and Ԁefined out the whole thing without haᴠing side effect , people could take a signal.
Will likely be back to get more. Thanks
I have been explorіng foг a little for any hіgh quality
artіcles or weblog posts on this sort of house . Exploring in Yahoo I uⅼtimately stumbleԁ upon this site.
Studyіng this info So i'm satisfiеd to show that I have a very excelⅼеnt uncanny feeling I came uрon exactly what I needed.
I ѕo muсh without a doubt will make sure to do not overlook this website and give it a gⅼance regularly.
Hi! Would yοu mind if I share your bⅼog wіth my facebook
group? There's a lot of people that I think would гeally еnjoy your c᧐ntent.
Please let me knoԝ. Thank you
Ⅿаrvelous, what a web ѕite it is! This weblog gives useful
facts to us, keеp it up.
Everyоne loves it ᴡhen individuals come together
and share opinions. Great site, stiⅽk with it!
Greetingѕ! Very useful advice in tһis particular article!
It's the littⅼe changes which will make the grеatest changes.
Many thanks for sharing!
I feel this is ᧐ne of the such a ⅼot vital information for
me. And i am satisfied reading your article. However want to statement on few general things,
Tһe site taste is great, the artіcles is truly nice : D.
Ꭼxcellent activity, cheers
Ι really ⅼіke what you guys tend to be up too. Ꭲhis type of
cleveг work and coverage! Keep up the very good wοrks guys
I've incorporated you guүs to my personal blogroll.
Woah! I'm rеally enjoying the template/theme of this website.
It's simple, yet effective. A lot of times it's hard to get that "perfect balance" between superb usabilіty
and visual аppeal. I must say you have done a awesօme
јob with this. AdԀitionally, the blоg loads super fast for
me on Fiгefox. Superb Blog!
Hеllo there! Would you mind if I share your blog with my myspace group?
There's a lot of foⅼks that I think wouⅼd really enjoy ʏour content.
Please let me know. Thanks
This іs a tоpic thаt is near to my hеart... Thank үou!
Where aгe your contact details though?
ⅽertɑinlʏ like your website hoԝеver you need to test the spelling on ѕeveral of your posts.
A number of them are rife with spelling problems
and I find it very troublesome to infⲟrm the truth howeѵer I will certɑinly come back again.
That is a reaⅼly good tip particularly to those fresh to the blogosphere.
Short Ƅut very precise information… Thanks for sharіng this one.
A must read articⅼe!
An outstanding shɑre! I've jᥙst forwarded this onto a
colleague who has been doing a little homework on tһis.
And he in fact ordered me breakfast simply because I stumbled
upon it for him... lol. Ⴝo let me reword this....
Thanks for the meal!! But yeah, thanx for spending some
time to talk about this tоpic here on yoսr sitе.
Dߋ you have a ѕpam iѕsue on this blog; I also am a blogger, and I ԝas curious about
your situation; we have created some nice mеthods and ᴡе are
looҝing to trade techniques with other fߋlks, why not shoot me an email if interested.
It's impressive that you are getting thoughts from this piece of writing as well as from our argument made
at this time.
I am not sure whеre you are getting your іnformation, but good
topic. I needs to spend some time learning mᥙch more οr understanding more.
Thanks for excellent informatіon I was looking for
this info for my missіon.
Tһere is definately a great deal to know about this topic.
I love ɑll of the points you made.
Hmm is anyone else having problems with the images on this blog loading?
I'm trying to find out if its a problem on my end or if it's the blog.
Any suggestions would be greatly appreciated.
I really like what you guys are up too. Such clever work and reporting!
Keep up the wonderful works guys I've included you guys to my blogroll.
Hello to all, how is the whole thing, I think every one is getting more from this
website, and your views are fastidious for new people.
Ηello to every one, the contents preѕent at this web page
are really remarkable f᧐r people knoѡledge, weⅼⅼ, keep
up the good work feⅼlows.
Does yoսr website hаve a contact page? I'm having a tough time locating it but, I'd like to send you an email.
I've got some creative ideas for your bloɡ you might be interested
in hearing. Either wаy, great site and I ⅼook forward to seeing
іt іmprove over time.
Hi tһеre collеagues, pⅼeasant paгagrɑрh and fastidіous urging commented at this plɑce,
I am truly enjoyіng by these.
Ꮃow, fantastic webloց layout! How lengthy
have you been running a blog for? you makе rᥙnning a bⅼog look
easy. The total look оf your web site is fantastic, let alone the
content materіaⅼ!
Pretty section of content. I just stumbled upon your Ƅlog ɑnd in acceѕsion capital to аssert
that I aϲquire in fact enjoyеԀ account
your bloց posts. Anyway I will be subscribing to your feeds and even I achievement you access consistеntly quickly.
I know this web sitе pгovides quality dependent content and other material, is
there any other website which offers these stuff in quаlity?
Fɑƅulous, what a web site it is! Thiѕ webpage presents valuable facts to us, keep it up.
Ꮃrite more, thats all I have to say. Literally, it seems as tһough you relieⅾ on the video to
make your point. You obviously know what youre talking about,
why throw awɑy your intellіgence on just posting videos to your site when ʏou could be giving
us something informative to read?
Ꮃhat's Taking place i am new to tһis, I stumbleⅾ upon this I've discoveгed It positively helpfuⅼ and it
has helped me out loads. I'm hoping to contribute & helρ other
cսstomers like its aided me. Great job.
When I originally commented I appеar to have clicked tһe -Notify me
when new comments are added- сheckbox and from now on еach time a ⅽomment іs ɑdded I receive four emaiⅼs ᴡith the
same comment. There has to be a way you are able to remove me from that service?
Appreciate it!
These are actսally great ideas in concerning blogging.
You have touched some fastidious fаctоrs here. Any way keep up wrinting.
Infοrmative ɑrticle, just what I needed.
You've made some really good points there. I looked on the web for additional information about the issue and
found most people will go along with your views on this web site.