- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
- 618
- 619
- 620
- 621
- 622
- 623
- 624
- 625
- 626
- 627
- 628
- 629
- 630
- 631
- 632
- 633
- 634
- 635
- 636
- 637
- 638
- 639
- 640
- 641
- 642
- 643
- 644
- 645
- 646
- 647
- 648
- 649
- 650
- 651
- 652
- 653
- 654
- 655
<template>
<div>
<Nav />
<!-- Masthead -->
<section class="masthead">
<h1><span>BUY {{ basicLangName.toUpperCase() }} </span> from Rosetta Stone <sup>®</sup></h1>
<div class="heading-2">
<div>6 YEARS IN A ROW — PC Magazine Editors' Choice Winner</div>
<div>BEST LANGUAGE LEARNING SOFTWARE</div>
</div>
</section>
<!-- Brief highlights -->
<section class="brief">
<div class="content">
<div class="left">
<ul>
<li><strong>Trusted by millions</strong> worldwide for 25+ years, including educational institutions and corporate clients such as NASA and the US State Department.</li>
<li>Improve your pronunciation and build confidence through our TruAccent <strong>speech-recognition software</strong>. <a class="masthead_learn_more js_meclabs_learn_more" data-modal="speak" href="#">Learn More</a></li>
<li><strong>Fully immersive curriculum</strong>, sequenced to introduce new skills in a way that stimulates your brain's natural language learning ability. <a href="#">Learn More</a></li>
</ul>
</div>
<div class="right">
<img src="https://www.rosettastone.com/lp/globals/img/online-lockup-holiday.png" alt="Devices">
<div class="ratings">* * * * * </div>
</div>
</div>
</section>
<!-- Features -->
<div class="features-wrap">
<InteractiveFeatures />
</div>
<!-- Highlights -->
<section class="highlights">
<div class="content">
<Highlights />
</div>
</section>
<!-- Products -->
<section class="products-container">
<div class="content">
<div class="products">
<!-- Loop through our products | ProductBox Component -->
<ProductBox v-for="(product, key) in products"
:msrp='product.msrp'
:price='product.price'
:level='product.lvl'
:name='product.name'
:cartlink='product.cart'
:fullprice='showFullPrice'
:threepay='threePay'
:bonusmonths='bonusMonths && product.lvl.match(/13|27/)'
:key=key
/>
</div>
</div>
</section>
<!-- How it works / demo -->
<section class="how-it-works">
<h2>HOW DOES IT WORK?</h2>
<p>Find out everything you need to know about Rosetta Stone in 2 minutes.</p>
<div class="cta-img"></div>
<a href="" class="cta-btn" @click.prevent='openDemoDialog'>TAKE THE INTERACTIVE DEMO</a>
<DemoLeadDialog v-if='isDemoDialogOpen' />
</section>
<!-- -->
</div>
</template>
<script>
import Nav from '~/components/Nav.vue'
import InteractiveFeatures from '~/components/InteractiveFeatures.vue'
import Highlights from '~/components/Highlights.vue'
import ProductBox from '~/components/ProductBox.vue'
import DemoLeadDialog from '~/components/DemoLeadDialog.vue'
// this.showOverlay = false
export default {
/* Components this page uses. They are imported above */
components: {
Nav,
InteractiveFeatures,
Highlights,
ProductBox,
DemoLeadDialog
},
/**
* Sets <head> information for this page.
* Metadata, scripts, css, title, etc, can
* all be set and fetched here
*/
head ()
{
return {
title: 'Rosetta Stone - Learn a New Language',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'Rosetta Stone' }
],
script: [
{ src: `https://www.rosettastone.com/lp/static/data/us/models/js/sitewide.js` }
]
}
},
/**
* We use this to populate the pages's `data` variables
* on the server - `nuxt generate` will use this and pass
* the needed variables via `payload` param.
* But because this is also ran on the client before
* components are mounted, we must provide dummy values
* since we won't have a payload param when in dev mode.
* We set these dummy values in the process.server else condition
*/
async asyncData ({ params, route, payload })
{
// Variables we will preset on server for client.
// These are also set in dev mode, based on the URL.
let code
let langName
let filter = [ '1', '8', '13', '18', '27' ]
/**
* Two cases can cause process.server to be true
* 1: User hard reloads the browser while in dev mode
* 2. Nuxt Generate is generating this page.
* If this is case 1 and this function is being called
* by nuxt generate on the server,
* we can preeset some data variables in the server
* so they're set on the client when page is compiled.
* To do this, we must fetch the RSI model file from the web.
* since it's browserless mode and the `script` tag doesn't exist.
*/
if (process.server && typeof payload != 'undefined')
{
code = payload.code
langName = payload.name
}
/**
* This will run if:
* 1. Page is being hot reloaded while dev'ing,
* 2. User hard reloads browser while in dev mode.
* 3. regular page load - aka, when a user visits the web page
* If it's case 2, then we must get the RSI model from ftp server
* since there is no window object to insert `<script>`.
* Also, `payload` isnt' set since it isn't being called by nuxt generate.
*/
if (typeof payload == 'undefined')
{
if (process.server)
{
console.log('----- SERVER')
}
else
{
console.log('----- browser!')
}
let modelURI = `https://www.rosettastone.com/lp/static/data/us/models/js/sitewide.js`
let model = await fetch(modelURI)
model = await model.text()
// eval(model) will error. we need to split the RSI function from the props
var [original, fn, props] = model.match(/(.+)(RSI\.id=.+)/)
let RSI = eval(`(${fn})`)
eval(props)
global.RSI = RSI
let urlMap = [
{
route: `buy-spanish`,
name: "Spanish (Latin America)",
code: "esp"
},
{
route: `buy-french`,
name: "French",
code: "fra"
},
{
route: `buy-italian`,
name: "Italian",
code: "ita"
},
{
route: `buy-german`,
name: "German",
code: "deu"
},
{
route: `buy-english`,
name: "English (American)",
code: "eng"
},
{
route: `buy-arabic`,
name: "Arabic",
code: "ara"
},
{
route: `buy-chinese`,
name: "Chinese (Mandarin)",
code: "chi"
},
{
route: `learn-dari`,
name: "Dari",
code: "dar"
},
{
route: `buy-dutch`,
name: "Dutch",
code: "ned"
},
{
route: `learn-english-british`,
name: "English (British)",
code: "ebr"
},
{
route: `learn-tagalog`,
name: "Filipino (Tagalog)",
code: "tgl"
},
{
route: `learn-greek`,
name: "Greek",
code: "grk"
},
{
route: `learn-hebrew`,
name: "Hebrew",
code: "heb"
},
{
route: `learn-hindi`,
name: "Hindi",
code: "hin"
},
{
route: `learn-indonesian`,
name: "Indonesian",
code: "ind"
},
{
route: `learn-irish`,
name: "Irish",
code: "gle"
},
{
route: `buy-japanese`,
name: "Japanese",
code: "jpn"
},
{
route: `learn-korean`,
name: "Korean",
code: "kor"
},
{
route: `learn-latin`,
name: "Latin",
code: "lat"
},
{
route: `learn-pashto`,
name: "Pashto",
code: "pas"
},
{
route: `learn-persian`,
name: "Persian (Farsi)",
code: "far"
},
{
route: `learn-polish`,
name: "Polish",
code: "pol"
},
{
route: `learn-spanish-spain`,
name: "Spanish (Spain)",
code: "esc"
},
{
route: `learn-swahili`,
name: "Swahili",
code: "kis"
},
{
route: `learn-swedish`,
name: "Swedish",
code: "sve"
},
{
route: `learn-turkish`,
name: "Turkish",
code: "tur"
},
{
route: `learn-urdu`,
name: "Urdu",
code: "urd"
},
{
route: `learn-vietnamese`,
name: "Vietnamese",
code: "vie"
},
];
urlMap.forEach(o => {
if (route.params.page.match(o.route))
{
code = o.route
langName = o.name
return
}
})
}
return {
// 3-letter lang code - esp, ita, fra...
code: code,
langName: langName,
bonusMonths: RSI.bonusmonths,
productList: RSI({cat: 'esp', media: 'subscription'}),
filter: filter,
}
},
/* Page state data */
data()
{
return {
// 3-letter lang code. Populated via asyncData
code: null,
// Full language name. Populated via asyncData
langName: null,
// List of products to show. Each time a language is changed, this
// gets populated and the product view gets updated (new cart urls, etc)
productList: [],
// Products we don't want to show
filter: null,
// If showfull=1 is in url, this becomes true. Show full prices
showFullPrice: null,
// If ?3pay=1 is in url, this becomes true. 24m becomes 3pay
threePay: null,
bonusMonths: null,
isDemoDialogOpen: true,
}
},
/**
* Called on client side.
* We use this to fill State data above
* before mount, since the /lp/globals/models/<model>.js
* has been loaded into the browser's window object.
* This essentially is our state data.
*/
beforeMount()
{
// Adding on to the state data, but on the client side.
//this.productList = RSI({cat: this.selectedLang, media: 'subscription'})
//this.languageName = window.RSI({cat: this.code})[0].language
this.isMobile = window.innerWidth <= 540 ? true : false
this.expirationdate = window.RSI.expirationDate
this.showFullPrice = window.location.search.match(/showfull=1/)
this.threePay = window.location.search.match(/3pay=1/)
this.bonusMonths = window.RSI.bonusmonths
if (this.$route.query.onemonth )
{
this.filter = this.filter.map(e => e+'' == '1' ? `3` : e)
}
},
mounted ()
{
window.pagedata = this
},
methods: {
openDemoDialog ()
{
console.log('AYYYY')
}
},
computed: {
/**
* Language name without parenthesis
*/
basicLangName ()
{
return this.langName.replace(/\(.+\)/, "").trim()
},
/**
* Everytime state's productList gets updated with a new value,
* this computed property will automatically be updated too.
* We have this computed property so we don't have to
* filter/map/sort theproductsList each time we update it.
* Use this in the template view instead of productList
*/
products ()
{
return this.productList
.filter(p => this.filter.indexOf(p.lvl) == -1 )
.map(p => { p.name = p.lvl; return p })
.sort( (a, b) => parseInt(a.lvl) < parseInt(b.lvl) ? -1 : 1)
}
},
}
</script>
<style lang='stylus'>
@require '~assets/css/normalize'
@require '~assets/css/variables'
body
//overflow-x hidden
font-family gothambook, helvetica
a
text-decoration none
// This div is for fixed content (not fluid)
.content
max-width 1100px
display flex
padding 0 2em
box-sizing border-box
@media $phone
width 100%
/********************************
* MASTHEAD
**********************************/
.masthead
//border-top 3px solid #fff
background #01a13a
box-shadow inset 0 1px 73px 0 rgba(0,0,0,.24)
text-align center
padding 3.2em 0
font-family gothambook
color #fff
h1
font-size 130%
margin 0 0 1em 0
font-size 30px
color #fff
letter-spacing 1px
font-weight 100
span
color #ffbd00
font-weight 400
sup
font-size 13px
position relative
top -13pxpadding-bottom 2em
left -10ppadding-bottom 2em
.heading-2 padding-bottom 2em
color #fff padding-bottom 2em
& > div:firpadding-bottom 2em
font-sizepadding-bottom 2em
& > div:last-of-type
font-size 19px
font-weight 700
/********************************
* PRODUCT BRIEF HIGHLIGHTS & IMAGE
**********************************/
.brief
display flex
width 100%
padding 2em 0
justify-content center
background #f1f1f1
.content
align-items center
.left
width 45%
ul
margin 0
padding 0
li
margin 0 0 1em 0
line-height 23px
font-size 110%
a
color #4788be
font-weight 700
text-transform uppercase
.right
flex-grow 1
text-align center
img
width 60%
@media $tablet
.content
flex-direction column
.left
width 100%
order 2
.right
order 1
padding-bottom 2em
/********************************
* PRODUCT FEATURES
**********************************/
.features-wrap
background #392e07
color #fff
display flex
justify-content center
padding 2em 0
/********************************
* PRODUCT HIGHLIGHTS
*********************************/
// .highlights-wrap
// background blue
// padding 2em 0
// background #4788be
// color #fff
// display flex
// justify-content center
.highlights
padding 2em 0
background #4788be
color #fff
display flex
justify-content center
.content
display flex
@media $tablet
.content
flex-direction column
width 100%
/********************************
* PRODUCT HIGHLIGHTS
**********************************/
.products-container
padding 3em 0
display flex
justify-content center
background #4788be
p:first-of-type
margin 4em 0
.content
// flex-direction column
// align-items center
// padding 0 1em
width 100%
@media $phone
padding 0 1em
.products
display flex
flex-wrap wrap
justify-content space-between
width 100%
@media $tablet
justify-content space-around
@media $phone
// Reverse product order in mobile
list = 1..15
for n in list
.product-wrap:nth-child({n})
order list[-(n)]
.how-it-works
text-align center
background #f1f1f1
padding 3em 0
display flex
flex-direction column
align-items center
h2
font-size 30px
font-weight 100
p
padding-bottom 2em
.cta-img
width 574px
height 323px
display flex
background url(https://www.rosettastone.com/lp/catalog/catalog-pages-assets/video_still_demo.jpg)
background-size contain
.cta-btn
padding 16px 38px
color #fff
font-size 16px
border-radius 4px
margin-top 50px
background $blue
font-weight 700
</style>